From 905e56f8823e7513b35b9de4e5c6f8b0b6cca539 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Tue, 8 May 2012 23:30:34 +0300 Subject: CHUI-103 FIXED Implemented switching text view modes from pop-up menu --- indra/newview/llnearbychat.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index a7303ad035..3a43750408 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -120,6 +120,9 @@ void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) tmp_chat.mFromName = chat.mFromName; LLSD chat_args = args; chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; + chat_args["show_time"] = true; + chat_args["show_names_for_p2p_conv"] = true; + mChatHistory->appendMessage(chat, chat_args); } -- cgit v1.3 From 4fc8000d6692290516eae1f865b6b41f1d56cc0b Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Thu, 17 May 2012 19:35:05 +0300 Subject: CHUI-105 ADD. FIX Implement changes to all open conversations and nearby chat --- indra/newview/llfloaterpreference.cpp | 21 +++---- indra/newview/llimfloater.cpp | 85 ++++++++++++++------------- indra/newview/llimfloater.h | 14 +++-- indra/newview/llimview.cpp | 18 +++--- indra/newview/llimview.h | 14 ++--- indra/newview/llnearbychat.cpp | 104 +++++++++++++++++----------------- indra/newview/llnearbychat.h | 8 ++- 7 files changed, 140 insertions(+), 124 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 1c29323594..3ed575086c 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -423,13 +423,13 @@ void LLFloaterPreference::saveAvatarProperties( void ) BOOL LLFloaterPreference::postBuild() { - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate)); - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate)); - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate)); - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged)); @@ -457,14 +457,11 @@ BOOL LLFloaterPreference::postBuild() void LLFloaterPreference::onBusyResponseChanged() { // set "BusyResponseChanged" TRUE if user edited message differs from default, FALSE otherwise - if (LLTrans::getString("BusyModeResponseDefault") != getChild("busy_response")->getValue().asString()) - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", TRUE ); - } - else - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", FALSE ); - } + bool busy_flag = + LLTrans::getString("BusyModeResponseDefault") + != getChild("busy_response")->getValue().asString(); + + gSavedPerAccountSettings.setBOOL("BusyResponseChanged", busy_flag ); } LLFloaterPreference::~LLFloaterPreference() diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 051bb39540..c6608337c8 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -44,6 +44,7 @@ #include "lllayoutstack.h" #include "lllineeditor.h" #include "lllogchat.h" +#include "llnearbychat.h" #include "llpanelimcontrolpanel.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" @@ -63,13 +64,14 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mSessionID(session_id), mLastMessageIndex(-1), mDialog(IM_NOTHING_SPECIAL), - mChatHistory(NULL), mInputEditor(NULL), mExpandCollapseBtn(NULL), mTearOffBtn(NULL), mSavedTitle(), mTypingStart(), + mIsP2PChat(false), mShouldSendTypingState(false), + mChatHistory(NULL), mMeTyping(false), mOtherTyping(false), mTypingTimer(), @@ -77,12 +79,14 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mPositioned(false), mSessionInitialized(false) { - LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID); - if (im_session) + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + + if (mSession) { - mSessionInitialized = im_session->mSessionInitialized; + mIsP2PChat = mSession->isP2PSessionType(); + mSessionInitialized = mSession->mSessionInitialized; - mDialog = im_session->mType; + mDialog = mSession->mType; switch (mDialog) { case IM_SESSION_CONFERENCE_START: @@ -101,8 +105,6 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); } break; - case IM_NOTHING_SPECIAL: - case IM_SESSION_P2P_INVITE: default: break; } @@ -135,14 +137,13 @@ 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) { std::string item = userdata.asString(); bool plain_text = gSavedSettings.getBOOL("PlainTextChatHistory"); bool is_not_names = (item != "IMShowNamesForP2PConv"); - LLIMModel::LLIMSession* im_session = LLIMModel::instance().findIMSession(mSessionID); - bool is_p2p_chat = im_session && im_session->isP2PSessionType(); - return (plain_text && (is_not_names || is_p2p_chat)); + return (plain_text && (is_not_names || mIsP2PChat)); } void LLIMFloater::onIMSessionMenuItemClicked(const LLSD& userdata) @@ -159,7 +160,8 @@ void LLIMFloater::onIMSessionMenuItemClicked(const LLSD& userdata) gSavedSettings.setBOOL(item, !prev_value); } - reloadMessages(); + LLIMFloater::processChatHistoryStyleUpdate(); + LLNearbyChat::processChatHistoryStyleUpdate(); } void LLIMFloater::onFocusLost() @@ -353,18 +355,16 @@ BOOL LLIMFloater::postBuild() mTypingStart = LLTrans::getString("IM_typing_start_string"); // Disable input editor if session cannot accept text - LLIMModel::LLIMSession* im_session = - LLIMModel::instance().findIMSession(mSessionID); - if ( im_session && !im_session->mTextIMPossible ) + if ( mSession && !mSession->mTextIMPossible ) { mInputEditor->setEnabled(FALSE); mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); } - if (im_session && im_session->isP2PSessionType()) + if (mIsP2PChat) { // look up display name for window title - LLAvatarNameCache::get(im_session->mOtherParticipantID, + LLAvatarNameCache::get(mSession->mOtherParticipantID, boost::bind(&LLIMFloater::onAvatarNameCache, this, _1, _2)); } @@ -424,16 +424,14 @@ void LLIMFloater::enableDisableCallBtn() bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionID); - - if (!session) + if (!mSession) { getChildView("voice_call_btn")->setEnabled(false); return; } - bool session_initialized = session->mSessionInitialized; - bool callback_enabled = session->mCallBackEnabled; + bool session_initialized = mSession->mSessionInitialized; + bool callback_enabled = mSession->mCallBackEnabled; BOOL enable_connect = session_initialized && voice_enabled @@ -763,12 +761,16 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) if (mSessionID != im_session_id) { mSessionID = im_session_id; + setKey(im_session_id); if (mControlPanel) { mControlPanel->setSessionId(im_session_id); } boundVoiceChannel(); + + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + mIsP2PChat = mSession && mSession->isP2PSessionType(); } //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) @@ -785,30 +787,35 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) } } -void LLIMFloater::updateMessages() +void LLIMFloater::appendMessage(const LLChat& chat, const LLSD &args) { - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); + LLChat& tmp_chat = const_cast(chat); + + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = 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"] = !mIsP2PChat + || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + mChatHistory->appendMessage(chat, chat_args); + } +} + +void LLIMFloater::updateMessages() +{ std::list messages; // we shouldn't reset unread message counters if IM floater doesn't have focus - if (hasFocus()) - { - LLIMModel::instance().getMessages(mSessionID, messages, mLastMessageIndex + 1); - } - else - { - LLIMModel::instance().getMessagesSilently(mSessionID, messages, mLastMessageIndex + 1); - } + LLIMModel::instance().getMessages( + mSessionID, messages, mLastMessageIndex + 1, hasFocus()); if (messages.size()) { - bool is_p2p_chat = (mDialog == IM_SESSION_P2P_INVITE || mDialog == IM_NOTHING_SPECIAL); - LLSD chat_args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); - chat_args["show_names_for_p2p_conv"] = (!is_p2p_chat) || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); - std::ostringstream message; std::list::const_reverse_iterator iter = messages.rbegin(); std::list::const_reverse_iterator iter_end = messages.rend(); @@ -858,7 +865,7 @@ void LLIMFloater::updateMessages() chat.mText = message; } - mChatHistory->appendMessage(chat, chat_args); + appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); // if it is a notification - next message is a notification history log, so skip it @@ -1014,7 +1021,7 @@ void LLIMFloater::processAgentListUpdates(const LLSD& body) } } -void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue) +void LLIMFloater::processChatHistoryStyleUpdate() { LLFontGL* font = LLViewerChat::getChatFont(); LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 03f52fb316..4cbdaccea5 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -27,12 +27,13 @@ #ifndef LL_IMFLOATER_H #define LL_IMFLOATER_H +#include "llimview.h" #include "llinstantmessage.h" #include "lllogchat.h" #include "lltooldraganddrop.h" -#include "lltransientdockablefloater.h" #include "llvoicechannel.h" #include "llvoiceclient.h" +#include "lltransientdockablefloater.h" class LLAvatarName; class LLButton; @@ -47,7 +48,7 @@ class LLInventoryCategory; * optionally "docked" to the bottom tray. */ class LLIMFloater - : public LLTransientDockableFloater + : public LLTransientDockableFloater , public LLVoiceClientStatusObserver { LOG_CLASS(LLIMFloater); @@ -109,7 +110,7 @@ public: void processAgentListUpdates(const LLSD& body); void processSessionUpdate(const LLSD& session_update); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); + static void processChatHistoryStyleUpdate(); BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -153,7 +154,7 @@ private: BOOL isInviteAllowed() const; BOOL inviteToSession(const uuid_vec_t& agent_ids); - + void appendMessage(const LLChat& chat, const LLSD &args = 0); static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); @@ -186,11 +187,13 @@ private: LLPanelChatControlPanel* mControlPanel; LLUUID mSessionID; + LLIMModel::LLIMSession* mSession; S32 mLastMessageIndex; + LLChatHistory* mChatHistory; + EInstantMessage mDialog; LLUUID mOtherParticipantUUID; - LLChatHistory* mChatHistory; LLLineEditor* mInputEditor; bool mPositioned; @@ -199,6 +202,7 @@ private: bool mMeTyping; bool mOtherTyping; bool mShouldSendTypingState; + bool mIsP2PChat; LLFrameTimer mTypingTimer; LLFrameTimer mTypingTimeoutTimer; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index a7c4618fa4..18d39b7aa4 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -716,6 +716,16 @@ bool LLIMModel::clearSession(const LLUUID& session_id) return true; } +void LLIMModel::getMessages(const LLUUID& session_id, std::list& messages, int start_index, const bool sendNoUnreadMsgs) +{ + getMessagesSilently(session_id, messages, start_index); + + if (sendNoUnreadMsgs) + { + sendNoUnreadMessages(session_id); + } +} + void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list& messages, int start_index) { LLIMSession* session = findIMSession(session_id); @@ -757,13 +767,6 @@ void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id) mNoUnreadMsgsSignal(arg); } -void LLIMModel::getMessages(const LLUUID& session_id, std::list& messages, int start_index) -{ - getMessagesSilently(session_id, messages, start_index); - - sendNoUnreadMessages(session_id); -} - bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { LLIMSession* session = findIMSession(session_id); @@ -2497,6 +2500,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString()); } // log message to file + else { std::string session_name; diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 7c2cd03d97..9d19af4b62 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -191,12 +191,6 @@ public: */ bool clearSession(const LLUUID& session_id); - /** - * Populate supplied std::list with messages starting from index specified by start_index without - * emitting no unread messages signal. - */ - void getMessagesSilently(const LLUUID& session_id, std::list& messages, int start_index = 0); - /** * Sends no unread messages signal. */ @@ -205,7 +199,7 @@ public: /** * Populate supplied std::list with messages starting from index specified by start_index */ - void getMessages(const LLUUID& session_id, std::list& messages, int start_index = 0); + void getMessages(const LLUUID& session_id, std::list& messages, int start_index = 0, const bool sendNoUnreadMsgs = true); /** * Add a message to an IM Model - the message is saved in a message store associated with a session specified by session_id @@ -287,6 +281,12 @@ public: private: + /** + * Populate supplied std::list with messages starting from index specified by start_index without + * emitting no unread messages signal. + */ + void getMessagesSilently(const LLUUID& session_id, std::list& messages, int start_index = 0); + /** * Add message to a list of message associated with session specified by session_id */ diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 3a43750408..3c4b0b9aae 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" @@ -55,8 +54,42 @@ #include "llfloaterreg.h" #include "lltrans.h" -static const S32 RESIZE_BAR_THICKNESS = 3; +// --- 2 functions in the global namespace :( --- +bool isWordsName(const std::string& name) +{ + // checking to see if it's display name plus username in parentheses + S32 open_paren = name.find(" (", 0); + S32 close_paren = name.find(')', 0); + + if (open_paren != std::string::npos && + close_paren == name.length()-1) + { + return true; + } + else + { + //checking for a single space + S32 pos = name.find(' ', 0); + return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + } +} + +std::string appendTime() +{ + time_t utc_time; + utc_time = time_corrected(); + std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" + +LLTrans::getString("TimeMin")+"]"; + + LLSD substitution; + + substitution["datetime"] = (S32) utc_time; + LLStringUtil::format (timeStr, substitution); + return timeStr; +} + +static const S32 RESIZE_BAR_THICKNESS = 3; static LLRegisterPanelClassWrapper t_panel_nearby_chat("panel_nearby_chat"); @@ -90,41 +123,32 @@ BOOL LLNearbyChat::postBuild() return true; } -std::string appendTime() -{ - time_t utc_time; - utc_time = time_corrected(); - std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"]"; - - LLSD substitution; - - substitution["datetime"] = (S32) utc_time; - LLStringUtil::format (timeStr, substitution); - - return timeStr; -} - -void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +void LLNearbyChat::appendMessage(const LLChat& chat, const LLSD &args) { LLChat& tmp_chat = const_cast(chat); if(tmp_chat.mTimeStr.empty()) tmp_chat.mTimeStr = appendTime(); - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); - if (!chat.mMuted) { tmp_chat.mFromName = chat.mFromName; - LLSD chat_args = args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - chat_args["show_time"] = true; - chat_args["show_names_for_p2p_conv"] = true; + LLSD chat_args; + if (args) chat_args = 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"); mChatHistory->appendMessage(chat, chat_args); } +} + +void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +{ + appendMessage(chat, args); if(archive) { @@ -133,12 +157,9 @@ void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) mMessageArchive.erase(mMessageArchive.begin()); } - if (args["do_not_log"].asBoolean()) - { - return; - } - - if (gSavedPerAccountSettings.getBOOL("LogNearbyChat")) + // logging + if (!args["do_not_log"].asBoolean() + && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) { std::string from_name = chat.mFromName; @@ -165,10 +186,10 @@ void LLNearbyChat::onNearbySpeakers() LLFloaterSidePanelContainer::showPanel("people", "panel_people", param); } - void LLNearbyChat::onNearbyChatContextMenuItemClicked(const LLSD& userdata) { } + bool LLNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) { std::string str = userdata.asString(); @@ -216,7 +237,7 @@ void LLNearbyChat::updateChatHistoryStyle() } //static -void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue) +void LLNearbyChat::processChatHistoryStyleUpdate() { LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); LLNearbyChat* nearby_chat = chat_bar->findChild("nearby_chat"); @@ -224,25 +245,6 @@ void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue) nearby_chat->updateChatHistoryStyle(); } -bool isWordsName(const std::string& name) -{ - // checking to see if it's display name plus username in parentheses - S32 open_paren = name.find(" (", 0); - S32 close_paren = name.find(')', 0); - - if (open_paren != std::string::npos && - close_paren == name.length()-1) - { - return true; - } - else - { - //checking for a single space - S32 pos = name.find(' ', 0); - return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; - } -} - void LLNearbyChat::loadHistory() { LLSD do_not_log; diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 7c5975cbc5..47f4de1c6d 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -34,7 +34,8 @@ class LLResizeBar; class LLChatHistory; -class LLNearbyChat: public LLPanel +class LLNearbyChat +: public LLPanel { public: LLNearbyChat(const Params& p = LLPanel::getDefaultParams()); @@ -57,7 +58,7 @@ public: virtual void updateChatHistoryStyle(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); + static void processChatHistoryStyleUpdate(); void loadHistory(); @@ -67,7 +68,8 @@ public: private: void getAllowedRect (LLRect& rect); - + // prepare chat's params and out one message to chatHistory + void appendMessage(const LLChat& chat, const LLSD &args = 0); void onNearbySpeakers (); -- cgit v1.3 From 47ec4faeb4dc67f9614e218a75d4957ccf6f794c Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Wed, 30 May 2012 19:58:20 +0300 Subject: CHUI-119 WIP Prepare the nearby chat for hosting it by the IM-container --- indra/llui/llfloater.cpp | 2 + indra/llui/llfloater.h | 2 + indra/llui/llview.cpp | 2 +- indra/newview/CMakeLists.txt | 2 + indra/newview/llfloaterpreference.cpp | 8 +- indra/newview/llimconversation.cpp | 280 ++++++++++ indra/newview/llimconversation.h | 97 ++++ indra/newview/llimfloater.cpp | 600 +++++++-------------- indra/newview/llimfloater.h | 58 +- indra/newview/llimfloatercontainer.cpp | 1 + indra/newview/llimview.cpp | 2 +- indra/newview/llnearbychat.cpp | 26 +- indra/newview/llnearbychat.h | 10 +- indra/newview/llnearbychatbar.cpp | 110 ++-- indra/newview/llnearbychatbar.h | 16 +- .../skins/default/xui/en/floater_chat_bar.xml | 162 +++++- .../skins/default/xui/en/panel_nearby_chat.xml | 21 +- 17 files changed, 847 insertions(+), 552 deletions(-) create mode 100644 indra/newview/llimconversation.cpp create mode 100644 indra/newview/llimconversation.h (limited to 'indra/newview/llnearbychat.cpp') 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("close_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + + mExpandCollapseBtn = getChild("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("im_control_panel_holder")->setVisible(false); + } + + mTearOffBtn = getChild("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(*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("voice_call_btn")->setImageOverlay( + callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + enableDisableCallBtn(); + +} + +void LLIMConversation::onSlide(LLIMConversation* self) +{ + LLIMFloaterContainer* host_floater = dynamic_cast(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(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(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("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("close_btn"); - mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); - - mExpandCollapseBtn = getChild("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("im_control_panel_holder")->setVisible(false); - } - - mTearOffBtn = getChild("tear_off_btn"); - mTearOffBtn->setCommitCallback(boost::bind(&LLIMFloater::onTearOffClicked, this)); mInputEditor = getChild("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("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(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("impanel", session_id); + + return conversation; +} + +LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::getTypedInstance("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("impanel", session_id); -} - -LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) -{ - return LLFloaterReg::getTypedInstance("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(*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() { @@ -1351,14 +1143,6 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& return; } -// 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() { @@ -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 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("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("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("chat_bar"); } +//static +//LLNearbyChatBar* LLNearbyChatBar::findInstance() +//{ +// return LLFloaterReg::findTypedInstance("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("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"> + VoicePTT_Off + VoicePTT_On + + + + + + + + + + + + + - + + width="384" /> + 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 @@ + width="394"> + width="394"> + width="387"> + width="374" /> + width="394"> + width="384" /> -- cgit v1.3 From bba0f4f74e56d911df8fc534d83cd4a84993bc8b Mon Sep 17 00:00:00 2001 From: Seth ProductEngine Date: Thu, 31 May 2012 16:37:22 +0300 Subject: CHUI-119 WIP --- indra/newview/CMakeLists.txt | 4 - indra/newview/llagent.cpp | 6 +- indra/newview/llchatitemscontainerctrl.cpp | 6 +- indra/newview/llfloatertranslationsettings.cpp | 4 +- indra/newview/llgesturemgr.cpp | 4 +- indra/newview/llimconversation.cpp | 97 ++- indra/newview/llimconversation.h | 12 +- indra/newview/llimfloater.cpp | 67 +- indra/newview/llimfloater.h | 9 +- indra/newview/llimfloatercontainer.cpp | 2 +- indra/newview/llimview.cpp | 5 +- indra/newview/llnearbychat.cpp | 840 ++++++++++++++++++--- indra/newview/llnearbychat.h | 107 ++- indra/newview/llnearbychatbar.cpp | 709 ----------------- indra/newview/llnearbychatbar.h | 105 --- indra/newview/llnearbychatbarlistener.cpp | 4 +- indra/newview/llnearbychatbarlistener.h | 6 +- indra/newview/llnearbychathandler.cpp | 9 +- indra/newview/llnotificationtiphandler.cpp | 5 +- indra/newview/llpanelimcontrolpanel.cpp | 81 -- indra/newview/llviewerfloaterreg.cpp | 4 +- indra/newview/llviewergesture.cpp | 4 +- indra/newview/llviewerkeyboard.cpp | 10 +- indra/newview/llviewerwindow.cpp | 8 +- .../skins/default/xui/en/floater_chat_bar.xml | 202 ----- .../skins/default/xui/en/floater_im_session.xml | 119 ++- .../default/xui/en/panel_adhoc_control_panel.xml | 95 --- .../default/xui/en/panel_group_control_panel.xml | 60 -- .../skins/default/xui/en/panel_nearby_chat.xml | 19 +- 29 files changed, 1037 insertions(+), 1566 deletions(-) delete mode 100644 indra/newview/llnearbychatbar.cpp delete mode 100644 indra/newview/llnearbychatbar.h delete mode 100644 indra/newview/skins/default/xui/en/floater_chat_bar.xml delete mode 100644 indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml delete mode 100644 indra/newview/skins/default/xui/en/panel_group_control_panel.xml (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 86d30c239f..509f9581d6 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -332,7 +332,6 @@ set(viewer_SOURCE_FILES llnamelistctrl.cpp llnavigationbar.cpp llnearbychat.cpp - llnearbychatbar.cpp llnearbychathandler.cpp llnearbychatbarlistener.cpp llnetmap.cpp @@ -364,7 +363,6 @@ set(viewer_SOURCE_FILES llpanelgroupnotices.cpp llpanelgrouproles.cpp llpanelhome.cpp - llpanelimcontrolpanel.cpp llpanelland.cpp llpanellandaudio.cpp llpanellandmarkinfo.cpp @@ -890,7 +888,6 @@ set(viewer_HEADER_FILES llnamelistctrl.h llnavigationbar.h llnearbychat.h - llnearbychatbar.h llnearbychathandler.h llnearbychatbarlistener.h llnetmap.h @@ -916,7 +913,6 @@ set(viewer_HEADER_FILES llpanelgroupnotices.h llpanelgrouproles.h llpanelhome.h - llpanelimcontrolpanel.h llpanelland.h llpanellandaudio.h llpanellandmarkinfo.h diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 3870a3be2e..0db03289d8 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -54,7 +54,7 @@ #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationsutil.h" #include "llpaneltopinfobar.h" #include "llparcel.h" @@ -1778,7 +1778,7 @@ void LLAgent::startTyping() { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- @@ -1790,7 +1790,7 @@ void LLAgent::stopTyping() { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 7477fbd656..477bdb3967 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -35,7 +35,7 @@ #include "llfloaterreg.h" #include "lllocalcliprect.h" #include "lltrans.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llagentdata.h" @@ -316,12 +316,12 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - LLNearbyChatBar::getInstance()->showHistory(); + LLNearbyChat::getInstance()->showHistory(); return FALSE; } } - LLNearbyChatBar::getInstance()->showHistory(); + LLNearbyChat::getInstance()->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 1a17183efd..bb01ce5a7e 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -29,7 +29,7 @@ #include "llfloatertranslationsettings.h" // Viewer includes -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "lltranslate.h" #include "llviewercontrol.h" // for gSavedSettings @@ -293,6 +293,6 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - LLNearbyChatBar::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); + LLNearbyChat::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); closeFloater(false); } diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 66ca76bfb0..26b63bdacb 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -51,7 +51,7 @@ #include "llviewermessage.h" #include "llvoavatarself.h" #include "llviewerstats.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llappearancemgr.h" #include "llgesturelistener.h" @@ -997,7 +997,7 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - LLNearbyChatBar::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); + LLNearbyChat::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); gesture->mCurrentStep++; break; diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index f5d84e80c1..893d8dc83f 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -27,25 +27,27 @@ #include "llviewerprecompiledheaders.h" -#include "llpanelimcontrolpanel.h" +#include "llimconversation.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" +#include "llnearbychat.h" + +const F32 REFRESH_INTERVAL = 0.2; LLIMConversation::LLIMConversation(const LLUUID& session_id) : LLTransientDockableFloater(NULL, true, session_id) - , mControlPanel(NULL) + , LLEventTimer(REFRESH_INTERVAL) , mIsP2PChat(false) , mExpandCollapseBtn(NULL) , mTearOffBtn(NULL) , mCloseBtn(NULL) , mSessionID(session_id) + , mParticipantList(NULL) { mCommitCallbackRegistrar.add("IMSession.Menu.Action", boost::bind(&LLIMConversation::onIMSessionMenuItemClicked, this, _2)); @@ -63,6 +65,15 @@ LLIMConversation::LLIMConversation(const LLUUID& session_id) boost::bind(&LLIMConversation::onIMShowModesMenuItemEnable, this, _2)); } +LLIMConversation::~LLIMConversation() +{ + if (mParticipantList) + { + delete mParticipantList; + mParticipantList = NULL; + } +} + BOOL LLIMConversation::postBuild() { mCloseBtn = getChild("close_btn"); @@ -71,19 +82,12 @@ BOOL LLIMConversation::postBuild() mExpandCollapseBtn = getChild("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("im_control_panel_holder")->setVisible(false); - } + mParticipantListPanel = getChild("speakers_list_panel"); + mParticipantListPanel->setVisible( + mIsNearbyChat? false : gSavedSettings.getBOOL("IMShowControlPanel")); + mExpandCollapseBtn->setImageOverlay( + getString(mParticipantListPanel->getVisible() ? "collapse_icon" : "expand_icon")); + mExpandCollapseBtn->setEnabled(!mIsP2PChat); mTearOffBtn = getChild("tear_off_btn"); mTearOffBtn->setCommitCallback(boost::bind(&LLIMConversation::onTearOffClicked, this)); @@ -93,6 +97,8 @@ BOOL LLIMConversation::postBuild() setOpenPositioning(LLFloaterEnums::OPEN_POSITIONING_NONE); } + buildParticipantList(); + if (isChatMultiTab()) { return LLFloater::postBuild(); @@ -104,6 +110,47 @@ BOOL LLIMConversation::postBuild() } +BOOL LLIMConversation::tick() +{ + // Need to resort the participant list if it's in sort by recent speaker order. + if (mParticipantList) + { + mParticipantList->update(); + } + + return false; +} + +void LLIMConversation::buildParticipantList() +{ if (mIsNearbyChat) + { + } + else + { + // for group and Ad-hoc chat we need to include agent into list + if(!mIsP2PChat && !mParticipantList && mSessionID.notNull()) + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + mParticipantList = new LLParticipantList(speaker_manager, getChild("speakers_list"), true, false); + } + } +} + +void LLIMConversation::onSortMenuItemClicked(const LLSD& userdata) +{ + // TODO: Check this code when when sort order menu will be added. (EM) + if (true || !mParticipantList) + return; + + std::string chosen_item = userdata.asString(); + + if (chosen_item == "sort_name") + { + mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); + } + +} + void LLIMConversation::onIMSessionMenuItemClicked(const LLSD& userdata) { std::string item = userdata.asString(); @@ -162,11 +209,11 @@ void LLIMConversation::updateHeaderAndToolbar() } bool is_control_panel_visible = false; - if (mControlPanel) + if (!mIsP2PChat) { // 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); + mParticipantListPanel->setVisible(is_control_panel_visible); } // Display collapse image (<<) if the floater is hosted @@ -215,10 +262,10 @@ void LLIMConversation::processChatHistoryStyleUpdate() } } - LLNearbyChatBar* nearby_chat_bar = LLNearbyChatBar::getInstance(); - if (nearby_chat_bar) + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); + if (nearby_chat) { - nearby_chat_bar->reloadMessages(); + nearby_chat->reloadMessages(); } } @@ -240,12 +287,12 @@ void LLIMConversation::onSlide(LLIMConversation* self) } else ///< floater is torn off { - if (self->mControlPanel) + if (!self->mIsP2PChat) { - bool expand = !self->mControlPanel->getParent()->getVisible(); + bool expand = !self->mParticipantListPanel->getVisible(); // Expand/collapse the IM control panel - self->mControlPanel->getParent()->setVisible(expand); + self->mParticipantListPanel->setVisible(expand); gSavedSettings.setBOOL("IMShowControlPanel", expand); diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index 501977e061..d31ae0808a 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -28,19 +28,24 @@ #ifndef LL_IMCONVERSATION_H #define LL_IMCONVERSATION_H +#include "lllayoutstack.h" +#include "llparticipantlist.h" #include "lltransientdockablefloater.h" #include "llviewercontrol.h" +#include "lleventtimer.h" class LLPanelChatControlPanel; class LLIMConversation : public LLTransientDockableFloater + , public LLEventTimer { public: LOG_CLASS(LLIMConversation); LLIMConversation(const LLUUID& session_id); + ~LLIMConversation(); // reload all message with new settings of visual modes static void processChatHistoryStyleUpdate(); @@ -75,13 +80,16 @@ protected: // set the enable/disable state for the Call button virtual void enableDisableCallBtn() = 0; -// /* virtual */ void updateTitleButtons(); + void buildParticipantList(); + void onSortMenuItemClicked(const LLSD& userdata); + /*virtual*/ BOOL tick(); - LLPanelChatControlPanel* mControlPanel; bool mIsNearbyChat; bool mIsP2PChat; + LLLayoutPanel* mParticipantListPanel; + LLParticipantList* mParticipantList; LLUUID mSessionID; LLButton* mExpandCollapseBtn; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 5339bcb936..c99da9e9c1 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -44,7 +44,6 @@ //#include "lllayoutstack.h" #include "lllineeditor.h" #include "lllogchat.h" -#include "llpanelimcontrolpanel.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" #include "lltrans.h" @@ -82,29 +81,7 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) { mIsP2PChat = mSession->isP2PSessionType(); mSessionInitialized = mSession->mSessionInitialized; - mDialog = mSession->mType; - switch (mDialog) - { - case IM_SESSION_CONFERENCE_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - break; - case IM_SESSION_GROUP_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - break; - case IM_SESSION_INVITE: - if (gAgent.isInGroup(mSessionID)) - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - } - else - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - } - break; - default: - break; - } } setOverlapsScreenChannel(true); @@ -113,24 +90,6 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) setDocked(true); } -// 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::onFocusLost() { LLIMModel::getInstance()->resetActiveSessionID(); @@ -409,8 +368,10 @@ void LLIMFloater::onAvatarNameCache(const LLUUID& agent_id, } // virtual -void LLIMFloater::draw() +BOOL LLIMFloater::tick() { + BOOL parents_retcode = LLIMConversation::tick(); + if ( mMeTyping ) { // Time out if user hasn't typed for a while. @@ -420,7 +381,7 @@ void LLIMFloater::draw() } } - LLTransientDockableFloater::draw(); + return parents_retcode; } //static @@ -643,16 +604,14 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) if (mSessionID != im_session_id) { mSessionID = im_session_id; - setKey(im_session_id); - if (mControlPanel) - { - mControlPanel->setSessionId(im_session_id); - } + boundVoiceChannel(); mSession = LLIMModel::getInstance()->findIMSession(mSessionID); mIsP2PChat = mSession && mSession->isP2PSessionType(); + + buildParticipantList(); } //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) @@ -841,10 +800,14 @@ void LLIMFloater::setTyping(bool typing) } } - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); - + if (!mIsNearbyChat) + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); + } + } } void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index c7793f73eb..24f28c8aee 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -61,15 +61,15 @@ public: /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); // Check typing timeout timer. - /*virtual*/ void draw(); - - static void* createPanelGroupControl(void* userdata); - static void* createPanelAdHocControl(void* userdata); + /*virtual*/ BOOL tick(); static LLIMFloater* findInstance(const LLUUID& session_id); static LLIMFloater* getInstance(const LLUUID& session_id); static void addToHost(const LLUUID& session_id); + static void* createPanelGroupControl(void* userdata); + static void* createPanelAdHocControl(void* userdata); + // LLFloater overrides /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); @@ -147,7 +147,6 @@ private: static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); void setTyping(bool typing); - void onCallButtonClicked(); // set the enable/disable state for the Call button diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index f72ddef412..3b6240de44 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -31,7 +31,7 @@ #include "llfloaterreg.h" #include "lllayoutstack.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagent.h" #include "llavatariconctrl.h" diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index c3ac1d32cb..46b1cb5f18 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -907,7 +907,7 @@ const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const LLIMSession* session = findIMSession(session_id); if (!session) { - llwarns << "session " << session_id << "does not exist " << llendl; + llwarns << "session " << session_id << " does not exist " << llendl; return LLUUID::null; } @@ -2483,8 +2483,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild("nearby_chat"); + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); if(nearby_chat) { diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 497690d656..2d7095957e 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -1,8 +1,8 @@ /** * @file LLNearbyChat.cpp - * @brief Nearby chat history scrolling panel implementation + * @brief LLNearbyChat class implementation * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -25,34 +25,50 @@ */ #include "llviewerprecompiledheaders.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llrootview.h" -//#include "llchatitemscontainerctrl.h" + +#include "message.h" + #include "lliconctrl.h" +#include "llappviewer.h" +#include "llfloaterreg.h" +#include "lltrans.h" +#include "llimfloatercontainer.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "lllogchat.h" #include "llresizebar.h" #include "llresizehandle.h" +#include "lldraghandle.h" #include "llmenugl.h" -#include "llviewermenu.h"//for gMenuHolder - +#include "llviewermenu.h" // for gMenuHolder #include "llnearbychathandler.h" #include "llchannelmanager.h" - -#include "llagent.h" // gAgent #include "llchathistory.h" #include "llstylemap.h" - #include "llavatarnamecache.h" - -#include "lldraghandle.h" - -#include "llnearbychatbar.h" #include "llfloaterreg.h" #include "lltrans.h" +#include "llfirstuse.h" +#include "llnearbychat.h" +#include "llagent.h" // gAgent +#include "llgesturemgr.h" +#include "llmultigesture.h" +#include "llkeyboard.h" +#include "llanimationstates.h" +#include "llviewerstats.h" +#include "llcommandhandler.h" +#include "llviewercontrol.h" +#include "llnavigationbar.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llrootview.h" +#include "llviewerchat.h" +#include "lltranslate.h" + +S32 LLNearbyChat::sLastSpecialChatChannel = 0; + + // --- 2 functions in the global namespace :( --- bool isWordsName(const std::string& name) { @@ -88,90 +104,83 @@ std::string appendTime() return timeStr; } -static const S32 RESIZE_BAR_THICKNESS = 3; -static LLRegisterPanelClassWrapper t_panel_nearby_chat("panel_nearby_chat"); +const S32 EXPANDED_HEIGHT = 266; +const S32 COLLAPSED_HEIGHT = 60; +const S32 EXPANDED_MIN_HEIGHT = 150; -LLNearbyChat::LLNearbyChat(const LLNearbyChat::Params& p) - : LLPanel(p), - mChatHistory(NULL) -{ -} +// legacy callback glue +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); -BOOL LLNearbyChat::postBuild() -{ - //menu - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; +struct LLChatTypeTrigger { + std::string name; + EChatType type; +}; - enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); - registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); +static LLChatTypeTrigger sChatTypeTriggers[] = { + { "/whisper" , CHAT_TYPE_WHISPER}, + { "/shout" , CHAT_TYPE_SHOUT} +}; - - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(menu) - mPopupMenuHandle = menu->getHandle(); - gSavedSettings.declareS32("nearbychat_showicons_and_names",2,"NearByChat header settings",true); +LLNearbyChat::LLNearbyChat(const LLSD& key) +: LLIMConversation(key), + mChatBox(NULL), + mChatHistory(NULL), + mOutputMonitor(NULL), + mSpeakerMgr(NULL), + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) +{ + mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); +} - mChatHistory = getChild("chat_history"); +//virtual +BOOL LLNearbyChat::postBuild() +{ + mChatBox = getChild("chat_editor"); - return LLPanel::postBuild(); -} + mChatBox->setCommitCallback(boost::bind(&LLNearbyChat::onChatBoxCommit, this)); + mChatBox->setKeystrokeCallback(&onChatBoxKeystroke, this); + mChatBox->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); + mChatBox->setFocusReceivedCallback(boost::bind(&LLNearbyChat::onChatBoxFocusReceived, this)); + mChatBox->setIgnoreArrowKeys( FALSE ); + mChatBox->setCommitOnFocusLost( FALSE ); + mChatBox->setRevertOnEsc( FALSE ); + mChatBox->setIgnoreTab(TRUE); + mChatBox->setPassDelete(TRUE); + mChatBox->setReplaceNewlinesWithSpaces(FALSE); + mChatBox->setEnableLineHistory(TRUE); + mChatBox->setFont(LLViewerChat::getChatFont()); + mOutputMonitor = getChild("chat_zone_indicator"); + mOutputMonitor->setVisible(FALSE); -void LLNearbyChat::appendMessage(const LLChat& chat, const LLSD &args) -{ - LLChat& tmp_chat = const_cast(chat); + // Register for font change notifications + LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChat::onChatFontChange, this, _1)); - if(tmp_chat.mTimeStr.empty()) - tmp_chat.mTimeStr = appendTime(); + enableResizeCtrls(true, true, false); - if (!chat.mMuted) - { - tmp_chat.mFromName = chat.mFromName; - LLSD chat_args; - if (args) chat_args = 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"] = true; + addToHost(); - mChatHistory->appendMessage(chat, chat_args); - } -} + //for menu + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; -void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) -{ - appendMessage(chat, args); + enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); + registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); - if(archive) + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(menu) { - mMessageArchive.push_back(chat); - if(mMessageArchive.size()>200) - mMessageArchive.erase(mMessageArchive.begin()); + mPopupMenuHandle = menu->getHandle(); } - // logging - if (!args["do_not_log"].asBoolean() - && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) - { - std::string from_name = chat.mFromName; - - if (chat.mSourceType == CHAT_SOURCE_AGENT) - { - // if the chat is coming from an agent, log the complete name - LLAvatarName av_name; - LLAvatarNameCache::get(chat.mFromID, &av_name); + // obsolete, but may be needed for backward compatibility? + gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", true); - if (!av_name.mIsDisplayNameDefault) - { - from_name = av_name.getCompleteName(); - } - } + mChatHistory = getChild("chat_history"); - LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); - } + return LLIMConversation::postBuild();; } void LLNearbyChat::onNearbySpeakers() @@ -189,33 +198,40 @@ bool LLNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) { std::string str = userdata.asString(); if(str == "nearby_people") - onNearbySpeakers(); + onNearbySpeakers(); return false; } -void LLNearbyChat::removeScreenChat() +void LLNearbyChat::getAllowedRect(LLRect& rect) { - LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); - if(chat_channel) - { - chat_channel->removeToastsFromChannel(); - } + rect = gViewerWindow->getWorldViewRectScaled(); } - -void LLNearbyChat::setVisible(BOOL visible) +//////////////////////////////////////////////////////////////////////////////// +// +void LLNearbyChat::onFocusReceived() { - if(visible) - { - removeScreenChat(); - } - - LLPanel::setVisible(visible); + setBackgroundOpaque(true); + LLIMConversation::onFocusReceived(); } +//////////////////////////////////////////////////////////////////////////////// +// +void LLNearbyChat::onFocusLost() +{ + setBackgroundOpaque(false); + LLIMConversation::onFocusLost(); +} -void LLNearbyChat::getAllowedRect(LLRect& rect) +BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) { - rect = gViewerWindow->getWorldViewRectScaled(); + //fix for EXT-6625 + //highlight NearbyChat history whenever mouseclick happen in NearbyChat + //setting focus to eidtor will force onFocusLost() call that in its turn will change + //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + + if(mChatHistory) + mChatHistory->setFocus(TRUE); + return LLPanel::handleMouseDown(x, y, mask); } void LLNearbyChat::reloadMessages() @@ -265,9 +281,9 @@ void LLNearbyChat::loadHistory() chat.mSourceType = CHAT_SOURCE_AGENT; if (from_id.isNull() && SYSTEM_FROM == from) - { + { chat.mSourceType = CHAT_SOURCE_SYSTEM; - + } else if (from_id.isNull()) { @@ -280,43 +296,117 @@ void LLNearbyChat::loadHistory() } } -//static -LLNearbyChat* LLNearbyChat::getInstance() +void LLNearbyChat::removeScreenChat() { - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - return chat_bar->findChild("nearby_chat"); + LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + if(chat_channel) + { + chat_channel->removeToastsFromChannel(); + } } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusReceived() +void LLNearbyChat::setVisible(BOOL visible) { - setBackgroundOpaque(true); - LLPanel::onFocusReceived(); + if(visible) + { + removeScreenChat(); + } + + LLIMConversation::setVisible(visible); } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusLost() +void LLNearbyChat::onCallButtonClicked() { - setBackgroundOpaque(false); - LLPanel::onFocusLost(); + LLAgent::toggleMicrophone(NULL); } -BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) +void LLNearbyChat::enableDisableCallBtn() { - //fix for EXT-6625 - //highlight NearbyChat history whenever mouseclick happen in NearbyChat - //setting focus to eidtor will force onFocusLost() call that in its turn will change - //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + // bool btn_enabled = LLAgent::isActionAllowed("speak"); + + getChildView("voice_call_btn")->setEnabled(false /*btn_enabled*/); +} + +void LLNearbyChat::addToHost() +{ + if (LLIMConversation::isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + + if (im_box) + { + im_box->addFloater(this, FALSE, LLTabContainer::END); + } + } +} + +// virtual +void LLNearbyChat::onOpen(const LLSD& key) +{ + LLIMConversation::onOpen(key); + showTranslationCheckbox(LLTranslate::isTranslationConfigured()); +} + +bool LLNearbyChat::applyRectControl() +{ + bool rect_controlled = LLFloater::applyRectControl(); + +/* if (!mNearbyChat->getVisible()) + { + reshape(getRect().getWidth(), getMinHeight()); + enableResizeCtrls(true, true, false); + } + else + {*/ + enableResizeCtrls(true); + setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); +// } - if(mChatHistory) - mChatHistory->setFocus(TRUE); - return LLPanel::handleMouseDown(x, y, mask); + return rect_controlled; +} + +void LLNearbyChat::onChatFontChange(LLFontGL* fontp) +{ + // Update things with the new font whohoo + if (mChatBox) + { + mChatBox->setFont(fontp); + } +} + +//static +LLNearbyChat* LLNearbyChat::getInstance() +{ + return LLFloaterReg::getTypedInstance("chat_bar"); +} + +//static +//LLNearbyChat* LLNearbyChat::findInstance() +//{ +// return LLFloaterReg::findTypedInstance("chat_bar"); +//} + +void LLNearbyChat::showHistory() +{ + openFloater(); + setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); + reshape(getRect().getWidth(), mExpandedHeight); + enableResizeCtrls(true); + storeRectControl(); } -void LLNearbyChat::draw() +void LLNearbyChat::showTranslationCheckbox(BOOL show) { + getChild("translate_chat_checkbox_lp")->setVisible(show); +} + +BOOL LLNearbyChat::tick() +{ + BOOL parents_retcode = LLIMConversation::tick(); + + displaySpeakingIndicator(); + updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); + // *HACK: Update transparency type depending on whether our children have focus. // This is needed because this floater is chrome and thus cannot accept focus, so // the transparency type setting code from LLFloater::setFocus() isn't reached. @@ -325,5 +415,511 @@ void LLNearbyChat::draw() setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); } - LLPanel::draw(); + return parents_retcode; } + +std::string LLNearbyChat::getCurrentChat() +{ + return mChatBox ? mChatBox->getText() : LLStringUtil::null; +} + +// virtual +BOOL LLNearbyChat::handleKeyHere( KEY key, MASK mask ) +{ + BOOL handled = FALSE; + + if( KEY_RETURN == key && mask == MASK_CONTROL) + { + // shout + sendChat(CHAT_TYPE_SHOUT); + handled = TRUE; + } + + return handled; +} + +BOOL LLNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) +{ + U32 in_len = in_str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + bool string_was_found = false; + + for (S32 n = 0; n < cnt && !string_was_found; n++) + { + if (in_len <= sChatTypeTriggers[n].name.length()) + { + 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 string_was_found; +} + +void LLNearbyChat::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) +{ + LLFirstUse::otherAvatarChatFirst(false); + + LLNearbyChat* self = (LLNearbyChat *)userdata; + + LLWString raw_text = self->mChatBox->getWText(); + + // Can't trim the end, because that will cause autocompletion + // to eat trailing spaces that might be part of a gesture. + LLWStringUtil::trimHead(raw_text); + + S32 length = raw_text.length(); + + if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences + { + gAgent.startTyping(); + } + else + { + gAgent.stopTyping(); + } + + /* Doesn't work -- can't tell the difference between a backspace + that killed the selection vs. backspace at the end of line. + if (length > 1 + && text[0] == '/' + && key == KEY_BACKSPACE) + { + // the selection will already be deleted, but we need to trim + // off the character before + std::string new_text = raw_text.substr(0, length-1); + self->mInputEditor->setText( new_text ); + self->mInputEditor->setCursorToEnd(); + length = length - 1; + } + */ + + KEY key = gKeyboard->currentKey(); + + // Ignore "special" keys, like backspace, arrows, etc. + if (length > 1 + && raw_text[0] == '/' + && key < KEY_SPECIAL) + { + // we're starting a gesture, attempt to autocomplete + + std::string utf8_trigger = wstring_to_utf8str(raw_text); + std::string utf8_out_str(utf8_trigger); + + if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part + S32 outlength = self->mChatBox->getLength(); // in characters + + // Select to end of line, starting from the character + // after the last one the user typed. + self->mChatBox->setSelection(length, outlength); + } + else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part + self->mChatBox->setCursorToEnd(); + } + + //llinfos << "GESTUREDEBUG " << trigger + // << " len " << length + // << " outlen " << out_str.getLength() + // << llendl; + } +} + +// static +void LLNearbyChat::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) +{ + // stop typing animation + gAgent.stopTyping(); +} + +void LLNearbyChat::onChatBoxFocusReceived() +{ + mChatBox->setEnabled(!gDisconnected); +} + +EChatType LLNearbyChat::processChatTypeTriggers(EChatType type, std::string &str) +{ + U32 length = str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + for (S32 n = 0; n < cnt; n++) + { + if (length >= sChatTypeTriggers[n].name.length()) + { + std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); + + if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) + { + U32 trigger_length = sChatTypeTriggers[n].name.length(); + + // It's to remove space after trigger name + if (length > trigger_length && str[trigger_length] == ' ') + trigger_length++; + + str = str.substr(trigger_length, length); + + if (CHAT_TYPE_NORMAL == type) + return sChatTypeTriggers[n].type; + else + break; + } + } + } + + return type; +} + +void LLNearbyChat::sendChat( EChatType type ) +{ + if (mChatBox) + { + LLWString text = mChatBox->getConvertedText(); + if (!text.empty()) + { + // store sent line in history, duplicates will get filtered + mChatBox->updateHistory(); + // Check if this is destined for another channel + S32 channel = 0; + stripChannelNumber(text, &channel); + + std::string utf8text = wstring_to_utf8str(text); + // Try to trigger a gesture, if not chat to a script. + std::string utf8_revised_text; + if (0 == channel) + { + // discard returned "found" boolean + LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); + } + else + { + utf8_revised_text = utf8text; + } + + utf8_revised_text = utf8str_trim(utf8_revised_text); + + type = processChatTypeTriggers(type, utf8_revised_text); + + if (!utf8_revised_text.empty()) + { + // Chat with animation + sendChatFromViewer(utf8_revised_text, type, TRUE); + } + } + + mChatBox->setText(LLStringExplicit("")); + } + + gAgent.stopTyping(); + + // If the user wants to stop chatting on hitting return, lose focus + // and go out of chat mode. + if (gSavedSettings.getBOOL("CloseChatOnReturn")) + { + stopChat(); + } +} + + +void LLNearbyChat::appendMessage(const LLChat& chat, const LLSD &args) +{ + LLChat& tmp_chat = const_cast(chat); + + if(tmp_chat.mTimeStr.empty()) + tmp_chat.mTimeStr = appendTime(); + + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = 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"] = true; + + mChatHistory->appendMessage(chat, chat_args); + } +} + +void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +{ + appendMessage(chat, args); + + if(archive) + { + mMessageArchive.push_back(chat); + if(mMessageArchive.size()>200) + mMessageArchive.erase(mMessageArchive.begin()); + } + + // logging + if (!args["do_not_log"].asBoolean() + && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) + { + std::string from_name = chat.mFromName; + + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // if the chat is coming from an agent, log the complete name + LLAvatarName av_name; + LLAvatarNameCache::get(chat.mFromID, &av_name); + + if (!av_name.mIsDisplayNameDefault) + { + from_name = av_name.getCompleteName(); + } + } + + LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + } +} + + +void LLNearbyChat::onChatBoxCommit() +{ + if (mChatBox->getText().length() > 0) + { + sendChat(CHAT_TYPE_NORMAL); + } + + gAgent.stopTyping(); +} + +void LLNearbyChat::displaySpeakingIndicator() +{ + LLSpeakerMgr::speaker_list_t speaker_list; + LLUUID id; + + id.setNull(); + mSpeakerMgr->update(TRUE); + mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); + + for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) + { + LLPointer s = *i; + if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) + { + id = s->mID; + break; + } + } + + if (!id.isNull()) + { + mOutputMonitor->setVisible(TRUE); + mOutputMonitor->setSpeakerId(id); + } + else + { + mOutputMonitor->setVisible(FALSE); + } +} + +void LLNearbyChat::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) +{ + sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); +} + +void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) +{ + // Look for "/20 foo" channel chats. + S32 channel = 0; + LLWString out_text = stripChannelNumber(wtext, &channel); + std::string utf8_out_text = wstring_to_utf8str(out_text); + std::string utf8_text = wstring_to_utf8str(wtext); + + utf8_text = utf8str_trim(utf8_text); + if (!utf8_text.empty()) + { + utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); + } + + // Don't animate for chats people can't hear (chat to scripts) + if (animate && (channel == 0)) + { + if (type == CHAT_TYPE_WHISPER) + { + lldebugs << "You whisper " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_NORMAL) + { + lldebugs << "You say " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_SHOUT) + { + lldebugs << "You shout " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); + } + else + { + llinfos << "send_chat_from_viewer() - invalid volume" << llendl; + return; + } + } + else + { + if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) + { + lldebugs << "Channel chat: " << utf8_text << llendl; + } + } + + send_chat_from_viewer(utf8_out_text, type, channel); +} + +// static +void LLNearbyChat::startChat(const char* line) +{ + LLNearbyChat* cb = LLNearbyChat::getInstance(); + + if (cb ) + { + cb->setVisible(TRUE); + cb->setFocus(TRUE); + cb->mChatBox->setFocus(TRUE); + + if (line) + { + std::string line_string(line); + cb->mChatBox->setText(line_string); + } + + cb->mChatBox->setCursorToEnd(); + } +} + +// Exit "chat mode" and do the appropriate focus changes +// static +void LLNearbyChat::stopChat() +{ + LLNearbyChat* cb = LLNearbyChat::getInstance(); + + if (cb) + { + cb->mChatBox->setFocus(FALSE); + + // stop typing animation + gAgent.stopTyping(); + } +} + +// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. +// Otherwise returns input and channel 0. +LLWString LLNearbyChat::stripChannelNumber(const LLWString &mesg, S32* channel) +{ + if (mesg[0] == '/' + && mesg[1] == '/') + { + // This is a "repeat channel send" + *channel = sLastSpecialChatChannel; + return mesg.substr(2, mesg.length() - 2); + } + else if (mesg[0] == '/' + && mesg[1] + && LLStringOps::isDigit(mesg[1])) + { + // This a special "/20" speak on a channel + S32 pos = 0; + + // Copy the channel number into a string + LLWString channel_string; + llwchar c; + do + { + c = mesg[pos+1]; + channel_string.push_back(c); + pos++; + } + while(c && pos < 64 && LLStringOps::isDigit(c)); + + // Move the pointer forward to the first non-whitespace char + // Check isspace before looping, so we can handle "/33foo" + // as well as "/33 foo" + while(c && iswspace(c)) + { + c = mesg[pos+1]; + pos++; + } + + sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); + *channel = sLastSpecialChatChannel; + return mesg.substr(pos, mesg.length() - pos); + } + else + { + // This is normal chat. + *channel = 0; + return mesg; + } +} + +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ChatFromViewer); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ChatData); + msg->addStringFast(_PREHASH_Message, utf8_out_text); + msg->addU8Fast(_PREHASH_Type, type); + msg->addS32("Channel", channel); + + gAgent.sendReliableMessage(); + + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); +} + +class LLChatCommandHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } + + // Your code here + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + bool retval = false; + // Need at least 2 tokens to have a valid message. + if (tokens.size() < 2) + { + retval = false; + } + else + { + S32 channel = tokens[0].asInteger(); + // VWR-19499 Restrict function to chat channels greater than 0. + if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) + { + retval = true; + // Send unescaped message, see EXT-6353. + std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); + send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); + } + else + { + retval = false; + // Tell us this is an unsupported SLurl. + } + } + return retval; + } +}; + +// Creating the object registers with the dispatcher. +LLChatCommandHandler gChatHandler; diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 62a41c17cb..b38111defa 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -1,8 +1,8 @@ - /** +/** * @file llnearbychat.h - * @brief nearby chat history scrolling panel implementation + * @brief LLNearbyChat class definition * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -24,9 +24,16 @@ * $/LicenseInfo$ */ -#ifndef LL_LLNEARBYCHAT_H_ -#define LL_LLNEARBYCHAT_H_ +#ifndef LL_LLNEARBYCHAT_H +#define LL_LLNEARBYCHAT_H +#include "llimconversation.h" +#include "llcombobox.h" +#include "llgesturemgr.h" +#include "llchat.h" +#include "llvoiceclient.h" +#include "lloutputmonitorctrl.h" +#include "llspeakers.h" #include "llscrollbar.h" #include "llviewerchat.h" #include "llpanel.h" @@ -35,32 +42,88 @@ class LLResizeBar; class LLChatHistory; class LLNearbyChat - : public LLPanel + : public LLIMConversation { public: - LLNearbyChat(const Params& p = LLPanel::getDefaultParams()); + // constructor for inline chat-bars (e.g. hosted in chat history window) + LLNearbyChat(const LLSD& key); + ~LLNearbyChat() {} - BOOL postBuild (); - - /** @param archive true - to save a message to the chat history log */ - void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); - void onNearbyChatContextMenuItemClicked(const LLSD& userdata); - bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); - - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual void draw(); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); // focus overrides /*virtual*/ void onFocusLost(); /*virtual*/ void onFocusReceived(); - + /*virtual*/ void setVisible(BOOL visible); - + void loadHistory(); void reloadMessages(); - static LLNearbyChat* getInstance(); void removeScreenChat(); + static LLNearbyChat* getInstance(); + + void addToHost(); + + /** @param archive true - to save a message to the chat history log */ + void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); + void onNearbyChatContextMenuItemClicked(const LLSD& userdata); + bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); + + LLLineEditor* getChatBox() { return mChatBox; } + + //virtual void draw(); + + std::string getCurrentChat(); + + virtual BOOL handleKeyHere( KEY key, MASK mask ); + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + + static void startChat(const char* line); + static void stopChat(); + + static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); + static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); + + void showHistory(); + void showTranslationCheckbox(BOOL show); + +protected: + static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); + static void onChatBoxKeystroke(LLLineEditor* caller, void* userdata); + static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); + void onChatBoxFocusReceived(); + + void sendChat( EChatType type ); + void onChatBoxCommit(); + void onChatFontChange(LLFontGL* fontp); + + /* virtual */ bool applyRectControl(); + + void onToggleNearbyChatPanel(); + + static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); + EChatType processChatTypeTriggers(EChatType type, std::string &str); + + 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; + + LLLineEditor* mChatBox; + LLOutputMonitorCtrl* mOutputMonitor; + LLLocalSpeakerMgr* mSpeakerMgr; + + S32 mExpandedHeight; + + /*virtual*/ BOOL tick(); + private: void getAllowedRect (LLRect& rect); @@ -68,14 +131,10 @@ private: void appendMessage(const LLChat& chat, const LLSD &args = 0); void onNearbySpeakers (); - -private: LLHandle mPopupMenuHandle; + std::vector mMessageArchive; LLChatHistory* mChatHistory; - std::vector mMessageArchive; }; #endif - - diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp deleted file mode 100644 index 68934be11a..0000000000 --- a/indra/newview/llnearbychatbar.cpp +++ /dev/null @@ -1,709 +0,0 @@ -/** - * @file llnearbychatbar.cpp - * @brief LLNearbyChatBar class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" - -#include "message.h" - -#include "llappviewer.h" -#include "llfloaterreg.h" -#include "lltrans.h" -#include "llimfloatercontainer.h" -#include "llfirstuse.h" -#include "llnearbychatbar.h" -#include "llagent.h" -#include "llgesturemgr.h" -#include "llmultigesture.h" -#include "llkeyboard.h" -#include "llanimationstates.h" -#include "llviewerstats.h" -#include "llcommandhandler.h" -#include "llviewercontrol.h" -#include "llnavigationbar.h" -#include "llwindow.h" -#include "llviewerwindow.h" -#include "llrootview.h" -#include "llviewerchat.h" -#include "llnearbychat.h" -#include "lltranslate.h" - -#include "llresizehandle.h" - -S32 LLNearbyChatBar::sLastSpecialChatChannel = 0; - -const S32 EXPANDED_HEIGHT = 266; -const S32 COLLAPSED_HEIGHT = 60; -const S32 EXPANDED_MIN_HEIGHT = 150; - -// legacy callback glue -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); - -struct LLChatTypeTrigger { - std::string name; - EChatType type; -}; - -static LLChatTypeTrigger sChatTypeTriggers[] = { - { "/whisper" , CHAT_TYPE_WHISPER}, - { "/shout" , CHAT_TYPE_SHOUT} -}; - -LLNearbyChatBar::LLNearbyChatBar(const LLSD& key) -: LLIMConversation(key), - mChatBox(NULL), - mNearbyChat(NULL), - mOutputMonitor(NULL), - mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) -{ - mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); -} - -//virtual -BOOL LLNearbyChatBar::postBuild() -{ - mChatBox = getChild("chat_box"); - - mChatBox->setCommitCallback(boost::bind(&LLNearbyChatBar::onChatBoxCommit, this)); - mChatBox->setKeystrokeCallback(&onChatBoxKeystroke, this); - mChatBox->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); - mChatBox->setFocusReceivedCallback(boost::bind(&LLNearbyChatBar::onChatBoxFocusReceived, this)); - - mChatBox->setIgnoreArrowKeys( FALSE ); - mChatBox->setCommitOnFocusLost( FALSE ); - mChatBox->setRevertOnEsc( FALSE ); - mChatBox->setIgnoreTab(TRUE); - mChatBox->setPassDelete(TRUE); - mChatBox->setReplaceNewlinesWithSpaces(FALSE); - mChatBox->setEnableLineHistory(TRUE); - mChatBox->setFont(LLViewerChat::getChatFont()); - - mNearbyChat = getChildView("nearby_chat"); - - LLUICtrl* show_btn = getChild("show_nearby_chat"); - show_btn->setCommitCallback(boost::bind(&LLNearbyChatBar::onToggleNearbyChatPanel, this)); - - mOutputMonitor = getChild("chat_zone_indicator"); - mOutputMonitor->setVisible(FALSE); - - gSavedSettings.declareBOOL("nearbychat_history_visibility", mNearbyChat->getVisible(), "Visibility state of nearby chat history", TRUE); - - mNearbyChat->setVisible(gSavedSettings.getBOOL("nearbychat_history_visibility")); - - // Register for font change notifications - LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChatBar::onChatFontChange, this, _1)); - - enableResizeCtrls(true, true, false); - - addToHost(); - - return LLIMConversation::postBuild();; -} - -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()); -} - -bool LLNearbyChatBar::applyRectControl() -{ - bool rect_controlled = LLFloater::applyRectControl(); - - if (!mNearbyChat->getVisible()) - { - reshape(getRect().getWidth(), getMinHeight()); - enableResizeCtrls(true, true, false); - } - else - { - enableResizeCtrls(true); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - } - - return rect_controlled; -} - -void LLNearbyChatBar::onChatFontChange(LLFontGL* fontp) -{ - // Update things with the new font whohoo - if (mChatBox) - { - mChatBox->setFont(fontp); - } -} - -//static -LLNearbyChatBar* LLNearbyChatBar::getInstance() -{ - return LLFloaterReg::getTypedInstance("chat_bar"); -} - -//static -//LLNearbyChatBar* LLNearbyChatBar::findInstance() -//{ -// return LLFloaterReg::findTypedInstance("chat_bar"); -//} - -void LLNearbyChatBar::showHistory() -{ - openFloater(); - - if (!getChildView("nearby_chat")->getVisible()) - { - onToggleNearbyChatPanel(); - } -} - -void LLNearbyChatBar::showTranslationCheckbox(BOOL show) -{ - getChild("translate_chat_checkbox_lp")->setVisible(show); -} - -void LLNearbyChatBar::draw() -{ - displaySpeakingIndicator(); - updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); - LLIMConversation::draw(); -} - -std::string LLNearbyChatBar::getCurrentChat() -{ - return mChatBox ? mChatBox->getText() : LLStringUtil::null; -} - -// virtual -BOOL LLNearbyChatBar::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - if( KEY_RETURN == key && mask == MASK_CONTROL) - { - // shout - sendChat(CHAT_TYPE_SHOUT); - handled = TRUE; - } - - return handled; -} - -BOOL LLNearbyChatBar::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) -{ - U32 in_len = in_str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - bool string_was_found = false; - - for (S32 n = 0; n < cnt && !string_was_found; n++) - { - if (in_len <= sChatTypeTriggers[n].name.length()) - { - 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 string_was_found; -} - -void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) -{ - LLFirstUse::otherAvatarChatFirst(false); - - LLNearbyChatBar* self = (LLNearbyChatBar *)userdata; - - LLWString raw_text = self->mChatBox->getWText(); - - // Can't trim the end, because that will cause autocompletion - // to eat trailing spaces that might be part of a gesture. - LLWStringUtil::trimHead(raw_text); - - S32 length = raw_text.length(); - - if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences - { - gAgent.startTyping(); - } - else - { - gAgent.stopTyping(); - } - - /* Doesn't work -- can't tell the difference between a backspace - that killed the selection vs. backspace at the end of line. - if (length > 1 - && text[0] == '/' - && key == KEY_BACKSPACE) - { - // the selection will already be deleted, but we need to trim - // off the character before - std::string new_text = raw_text.substr(0, length-1); - self->mInputEditor->setText( new_text ); - self->mInputEditor->setCursorToEnd(); - length = length - 1; - } - */ - - KEY key = gKeyboard->currentKey(); - - // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL) - { - // we're starting a gesture, attempt to autocomplete - - std::string utf8_trigger = wstring_to_utf8str(raw_text); - std::string utf8_out_str(utf8_trigger); - - if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part - S32 outlength = self->mChatBox->getLength(); // in characters - - // Select to end of line, starting from the character - // after the last one the user typed. - self->mChatBox->setSelection(length, outlength); - } - else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part - self->mChatBox->setCursorToEnd(); - } - - //llinfos << "GESTUREDEBUG " << trigger - // << " len " << length - // << " outlen " << out_str.getLength() - // << llendl; - } -} - -// static -void LLNearbyChatBar::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) -{ - // stop typing animation - gAgent.stopTyping(); -} - -void LLNearbyChatBar::onChatBoxFocusReceived() -{ - mChatBox->setEnabled(!gDisconnected); -} - -EChatType LLNearbyChatBar::processChatTypeTriggers(EChatType type, std::string &str) -{ - U32 length = str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (length >= sChatTypeTriggers[n].name.length()) - { - std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); - - if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) - { - U32 trigger_length = sChatTypeTriggers[n].name.length(); - - // It's to remove space after trigger name - if (length > trigger_length && str[trigger_length] == ' ') - trigger_length++; - - str = str.substr(trigger_length, length); - - if (CHAT_TYPE_NORMAL == type) - return sChatTypeTriggers[n].type; - else - break; - } - } - } - - return type; -} - -void LLNearbyChatBar::sendChat( EChatType type ) -{ - if (mChatBox) - { - LLWString text = mChatBox->getConvertedText(); - if (!text.empty()) - { - // store sent line in history, duplicates will get filtered - mChatBox->updateHistory(); - // Check if this is destined for another channel - S32 channel = 0; - stripChannelNumber(text, &channel); - - std::string utf8text = wstring_to_utf8str(text); - // Try to trigger a gesture, if not chat to a script. - std::string utf8_revised_text; - if (0 == channel) - { - // discard returned "found" boolean - LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); - } - else - { - utf8_revised_text = utf8text; - } - - utf8_revised_text = utf8str_trim(utf8_revised_text); - - type = processChatTypeTriggers(type, utf8_revised_text); - - if (!utf8_revised_text.empty()) - { - // Chat with animation - sendChatFromViewer(utf8_revised_text, type, TRUE); - } - } - - mChatBox->setText(LLStringExplicit("")); - } - - gAgent.stopTyping(); - - // If the user wants to stop chatting on hitting return, lose focus - // and go out of chat mode. - if (gSavedSettings.getBOOL("CloseChatOnReturn")) - { - stopChat(); - } -} - - -void LLNearbyChatBar::onToggleNearbyChatPanel() -{ - LLView* nearby_chat = getChildView("nearby_chat"); - - if (nearby_chat->getVisible()) - { - if (!isMinimized()) - { - mExpandedHeight = getRect().getHeight(); - } - setResizeLimits(getMinWidth(), COLLAPSED_HEIGHT); - nearby_chat->setVisible(FALSE); - reshape(getRect().getWidth(), COLLAPSED_HEIGHT); - enableResizeCtrls(true, true, false); - storeRectControl(); - } - else - { - nearby_chat->setVisible(TRUE); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - reshape(getRect().getWidth(), mExpandedHeight); - enableResizeCtrls(true); - storeRectControl(); - } - - gSavedSettings.setBOOL("nearbychat_history_visibility", mNearbyChat->getVisible()); -} - -void LLNearbyChatBar::reloadMessages() -{ - LLNearbyChat::getInstance()->reloadMessages(); -} - -void LLNearbyChatBar::setMinimized(BOOL b) -{ - LLNearbyChat* nearby_chat = getChild("nearby_chat"); - // when unminimizing with nearby chat visible, go ahead and kill off screen chats - if (!b && nearby_chat->getVisible()) - { - nearby_chat->removeScreenChat(); - } - LLFloater::setMinimized(b); -} - -void LLNearbyChatBar::onChatBoxCommit() -{ - if (mChatBox->getText().length() > 0) - { - sendChat(CHAT_TYPE_NORMAL); - } - - gAgent.stopTyping(); -} - -void LLNearbyChatBar::displaySpeakingIndicator() -{ - LLSpeakerMgr::speaker_list_t speaker_list; - LLUUID id; - - id.setNull(); - mSpeakerMgr->update(TRUE); - mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); - - for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) - { - LLPointer s = *i; - if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) - { - id = s->mID; - break; - } - } - - if (!id.isNull()) - { - mOutputMonitor->setVisible(TRUE); - mOutputMonitor->setSpeakerId(id); - } - else - { - mOutputMonitor->setVisible(FALSE); - } -} - -void LLNearbyChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) -{ - sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); -} - -void LLNearbyChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) -{ - // Look for "/20 foo" channel chats. - S32 channel = 0; - LLWString out_text = stripChannelNumber(wtext, &channel); - std::string utf8_out_text = wstring_to_utf8str(out_text); - std::string utf8_text = wstring_to_utf8str(wtext); - - utf8_text = utf8str_trim(utf8_text); - if (!utf8_text.empty()) - { - utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); - } - - // Don't animate for chats people can't hear (chat to scripts) - if (animate && (channel == 0)) - { - if (type == CHAT_TYPE_WHISPER) - { - lldebugs << "You whisper " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_NORMAL) - { - lldebugs << "You say " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_SHOUT) - { - lldebugs << "You shout " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); - } - else - { - llinfos << "send_chat_from_viewer() - invalid volume" << llendl; - return; - } - } - else - { - if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) - { - lldebugs << "Channel chat: " << utf8_text << llendl; - } - } - - send_chat_from_viewer(utf8_out_text, type, channel); -} - -// static -void LLNearbyChatBar::startChat(const char* line) -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (cb ) - { - cb->setVisible(TRUE); - cb->setFocus(TRUE); - cb->mChatBox->setFocus(TRUE); - - if (line) - { - std::string line_string(line); - cb->mChatBox->setText(line_string); - } - - cb->mChatBox->setCursorToEnd(); - } -} - -// Exit "chat mode" and do the appropriate focus changes -// static -void LLNearbyChatBar::stopChat() -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (cb) - { - cb->mChatBox->setFocus(FALSE); - - // stop typing animation - gAgent.stopTyping(); - } -} - -// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. -// Otherwise returns input and channel 0. -LLWString LLNearbyChatBar::stripChannelNumber(const LLWString &mesg, S32* channel) -{ - if (mesg[0] == '/' - && mesg[1] == '/') - { - // This is a "repeat channel send" - *channel = sLastSpecialChatChannel; - return mesg.substr(2, mesg.length() - 2); - } - else if (mesg[0] == '/' - && mesg[1] - && LLStringOps::isDigit(mesg[1])) - { - // This a special "/20" speak on a channel - S32 pos = 0; - - // Copy the channel number into a string - LLWString channel_string; - llwchar c; - do - { - c = mesg[pos+1]; - channel_string.push_back(c); - pos++; - } - while(c && pos < 64 && LLStringOps::isDigit(c)); - - // Move the pointer forward to the first non-whitespace char - // Check isspace before looping, so we can handle "/33foo" - // as well as "/33 foo" - while(c && iswspace(c)) - { - c = mesg[pos+1]; - pos++; - } - - sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); - *channel = sLastSpecialChatChannel; - return mesg.substr(pos, mesg.length() - pos); - } - else - { - // This is normal chat. - *channel = 0; - return mesg; - } -} - -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ChatFromViewer); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ChatData); - msg->addStringFast(_PREHASH_Message, utf8_out_text); - msg->addU8Fast(_PREHASH_Type, type); - msg->addS32("Channel", channel); - - gAgent.sendReliableMessage(); - - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); -} - -class LLChatCommandHandler : public LLCommandHandler -{ -public: - // not allowed from outside the app - LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } - - // Your code here - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) - { - bool retval = false; - // Need at least 2 tokens to have a valid message. - if (tokens.size() < 2) - { - retval = false; - } - else - { - S32 channel = tokens[0].asInteger(); - // VWR-19499 Restrict function to chat channels greater than 0. - if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) - { - retval = true; - // Send unescaped message, see EXT-6353. - std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); - send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); - } - else - { - retval = false; - // Tell us this is an unsupported SLurl. - } - } - return retval; - } -}; - -// Creating the object registers with the dispatcher. -LLChatCommandHandler gChatHandler; - - diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h deleted file mode 100644 index b7c4c993c6..0000000000 --- a/indra/newview/llnearbychatbar.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @file llnearbychatbar.h - * @brief LLNearbyChatBar class definition - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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_LLNEARBYCHATBAR_H -#define LL_LLNEARBYCHATBAR_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 LLIMConversation -{ -public: - // constructor for inline chat-bars (e.g. hosted in chat history window) - LLNearbyChatBar(const LLSD& key); - ~LLNearbyChatBar() {} - - /*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(); - - std::string getCurrentChat(); - virtual BOOL handleKeyHere( KEY key, MASK mask ); - - static void startChat(const char* line); - static void stopChat(); - - static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); - static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - - void showHistory(); - void showTranslationCheckbox(BOOL show); - /*virtual*/void setMinimized(BOOL b); - -protected: - static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); - static void onChatBoxKeystroke(LLLineEditor* caller, void* userdata); - static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); - void onChatBoxFocusReceived(); - - void sendChat( EChatType type ); - void onChatBoxCommit(); - void onChatFontChange(LLFontGL* fontp); - - /* virtual */ bool applyRectControl(); - - void onToggleNearbyChatPanel(); - - static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); - EChatType processChatTypeTriggers(EChatType type, std::string &str); - - void displaySpeakingIndicator(); - - // set the enable/disable state for the Call button - virtual void enableDisableCallBtn(); - - // Which non-zero channel did we last chat on? - static S32 sLastSpecialChatChannel; - - LLLineEditor* mChatBox; - LLView* mNearbyChat; - LLOutputMonitorCtrl* mOutputMonitor; - LLLocalSpeakerMgr* mSpeakerMgr; - - S32 mExpandedHeight; -}; - -#endif diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llnearbychatbarlistener.cpp index a63e1fb76e..61815d1864 100644 --- a/indra/newview/llnearbychatbarlistener.cpp +++ b/indra/newview/llnearbychatbarlistener.cpp @@ -29,14 +29,14 @@ #include "llviewerprecompiledheaders.h" #include "llnearbychatbarlistener.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagent.h" #include "llchat.h" -LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar) +LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChat & chatbar) : LLEventAPI("LLChatBar", "LLChatBar listener to (e.g.) sendChat, etc."), mChatbar(chatbar) diff --git a/indra/newview/llnearbychatbarlistener.h b/indra/newview/llnearbychatbarlistener.h index 9af9bc1f7b..0537275424 100644 --- a/indra/newview/llnearbychatbarlistener.h +++ b/indra/newview/llnearbychatbarlistener.h @@ -33,17 +33,17 @@ #include "lleventapi.h" class LLSD; -class LLNearbyChatBar; +class LLNearbyChat; class LLNearbyChatBarListener : public LLEventAPI { public: - LLNearbyChatBarListener(LLNearbyChatBar & chatbar); + LLNearbyChatBarListener(LLNearbyChat & chatbar); private: void sendChat(LLSD const & chat_data) const; - LLNearbyChatBar & mChatbar; + LLNearbyChat & mChatbar; }; #endif // LL_LLNEARBYCHATBARLISTENER_H diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index f26cc85019..e91a3fc334 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -40,7 +40,7 @@ #include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance #include "llviewerwindow.h"//for screen channel position -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llrootview.h" #include "lllayoutstack.h" @@ -487,9 +487,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, if(chat_msg.mText.empty()) return;//don't process empty messages - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - - LLNearbyChat* nearby_chat = chat_bar->findChild("nearby_chat"); + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); // Build notification data LLSD chat; @@ -558,8 +556,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, sChatWatcher->post(chat); - if( !chat_bar->isMinimized() - && nearby_chat->isInVisibleChain() + if( nearby_chat->isInVisibleChain() || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT && gSavedSettings.getBOOL("UseChatBubbles") ) || mChannel.isDead() diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index a1d5db2e27..507c6686fd 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -29,7 +29,7 @@ #include "llfloaterreg.h" #include "llnearbychat.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationhandler.h" #include "llnotifications.h" #include "lltoastnotifypanel.h" @@ -86,8 +86,7 @@ bool LLTipHandler::processNotification(const LLNotificationPtr& notification) // don't show toast if Nearby Chat is opened LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - LLNearbyChatBar* nearby_chat_bar = LLNearbyChatBar::getInstance(); - if (!nearby_chat_bar->isMinimized() && nearby_chat_bar->getVisible() && nearby_chat->getVisible()) + if (!nearby_chat->isMinimized() && nearby_chat->getVisible()) { return false; } diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index bc4097cd93..389baa86cd 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -41,85 +41,4 @@ #include "llspeakers.h" #include "lltrans.h" -LLPanelChatControlPanel::~LLPanelChatControlPanel() -{ -} -BOOL LLPanelChatControlPanel::postBuild() -{ - return TRUE; -} - -void LLPanelChatControlPanel::setSessionId(const LLUUID& session_id) -{ - //Method is called twice for AdHoc and Group chat. Second time when server init reply received - mSessionId = session_id; -} - - -LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id): -mParticipantList(NULL) -{ -} - -BOOL LLPanelGroupControlPanel::postBuild() -{ - - return LLPanelChatControlPanel::postBuild(); -} - -LLPanelGroupControlPanel::~LLPanelGroupControlPanel() -{ - delete mParticipantList; - mParticipantList = NULL; -} - -// virtual -void LLPanelGroupControlPanel::draw() -{ - // Need to resort the participant list if it's in sort by recent speaker order. - if (mParticipantList) - mParticipantList->update(); - LLPanelChatControlPanel::draw(); -} - - -void LLPanelGroupControlPanel::onSortMenuItemClicked(const LLSD& userdata) -{ - // TODO: Check this code when when sort order menu will be added. (EM) - if (false && !mParticipantList) - return; - - std::string chosen_item = userdata.asString(); - - if (chosen_item == "sort_name") - { - mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); - } - -} - -void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id) -{ - LLPanelChatControlPanel::setSessionId(session_id); - - mGroupID = session_id; - - // for group and Ad-hoc chat we need to include agent into list - if(!mParticipantList) - { - LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(session_id); - mParticipantList = new LLParticipantList(speaker_manager, getChild("speakers_list"), true, false); - } -} - - -LLPanelAdHocControlPanel::LLPanelAdHocControlPanel(const LLUUID& session_id):LLPanelGroupControlPanel(session_id) -{ -} - -BOOL LLPanelAdHocControlPanel::postBuild() -{ - //We don't need LLPanelGroupControlPanel::postBuild() to be executed as there is no group_info_btn at AdHoc chat - return LLPanelChatControlPanel::postBuild(); -} diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 5ced32a9ab..df1962f5fe 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -134,7 +134,6 @@ #include "llscriptfloater.h" #include "llfloatermodelpreview.h" #include "llcommandhandler.h" -#include "llnearbychatbar.h" // *NOTE: Please add files in alphabetical order to keep merges easy. @@ -187,8 +186,6 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("chat_bar", "floater_chat_bar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -211,6 +208,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("hud", "floater_hud.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("chat_bar", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("im_container", "floater_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("im_well_window", "floater_sys_well.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index a32a78cbf9..c7d37e102e 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -40,7 +40,7 @@ #include "llviewermessage.h" // send_guid_sound_trigger #include "llviewernetwork.h" #include "llagent.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" // Globals LLViewerGestureList gGestureList; @@ -130,7 +130,7 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) { // Don't play nodding animation, since that might not blend // with the gesture animation. - LLNearbyChatBar::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); } } diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 1aa9fd8a45..385d3cd29a 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -31,7 +31,7 @@ #include "llmath.h" #include "llagent.h" #include "llagentcamera.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llfocusmgr.h" #include "llmorphview.h" @@ -534,7 +534,7 @@ void stop_moving( EKeystate s ) void start_chat( EKeystate s ) { // start chat - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } void start_gesture( EKeystate s ) @@ -543,15 +543,15 @@ void start_gesture( EKeystate s ) if (KEYSTATE_UP == s && ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) { - if (LLNearbyChatBar::getInstance()->getCurrentChat().empty()) + if (LLNearbyChat::getInstance()->getCurrentChat().empty()) { // No existing chat in chat editor, insert '/' - LLNearbyChatBar::startChat("/"); + LLNearbyChat::startChat("/"); } else { // Don't overwrite existing text in chat editor - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } } } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 32f693b009..5b8cf52298 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -185,7 +185,7 @@ #include "llviewerjoystick.h" #include "llviewernetwork.h" #include "llpostprocess.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagentui.h" #include "llwearablelist.h" @@ -2482,7 +2482,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // Traverses up the hierarchy if( keyboard_focus ) { - LLNearbyChatBar* nearby_chat = LLFloaterReg::findTypedInstance("chat_bar"); + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("chat_bar"); if (nearby_chat) { @@ -2549,11 +2549,11 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) { - LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance("chat_bar")->getChatBox(); + LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance("chat_bar")->getChatBox(); if (chat_editor) { // passing NULL here, character will be added later when it is handled by character handler. - LLNearbyChatBar::getInstance()->startChat(NULL); + LLNearbyChat::getInstance()->startChat(NULL); return TRUE; } } diff --git a/indra/newview/skins/default/xui/en/floater_chat_bar.xml b/indra/newview/skins/default/xui/en/floater_chat_bar.xml deleted file mode 100644 index 7688525e13..0000000000 --- a/indra/newview/skins/default/xui/en/floater_chat_bar.xml +++ /dev/null @@ -1,202 +0,0 @@ - - - VoicePTT_Off - VoicePTT_On - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index a332bb5b12..c5cacab9f4 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -138,18 +138,24 @@ top_pad="0" left="0"> - - + + - - - - + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml b/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml deleted file mode 100644 index d68fa6ca6c..0000000000 --- a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - + width="31"/> + width="31"/> -- cgit v1.3 From ae2e611dfb7b712c159ebafabb83ebbc1f7465b6 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Fri, 31 Aug 2012 16:52:50 +0300 Subject: CHUI-315 (Nearby chat messages do not appear in conversation floater): cancelled inheritance LLNearbyChat from LLSingleton; set mSingleInstance flag for it. --- indra/llui/llfloater.cpp | 7 ++++++ indra/llui/llfloater.h | 1 + indra/newview/llagent.cpp | 6 +++-- indra/newview/llchatitemscontainerctrl.cpp | 4 ++-- indra/newview/llfloatertranslationsettings.cpp | 3 ++- indra/newview/llgesturemgr.cpp | 4 +++- indra/newview/llimconversation.cpp | 31 +++++++++++++------------- indra/newview/llimview.cpp | 6 +++-- indra/newview/llnearbychat.cpp | 21 +++++++++-------- indra/newview/llnearbychat.h | 4 +--- indra/newview/llnearbychathandler.cpp | 5 +++-- indra/newview/llnotificationhandlerutil.cpp | 5 +++-- indra/newview/llnotificationtiphandler.cpp | 3 ++- indra/newview/llviewergesture.cpp | 4 +++- indra/newview/llviewerkeyboard.cpp | 3 ++- indra/newview/llviewermessage.cpp | 8 ++++--- indra/newview/llviewerwindow.cpp | 10 +++++---- 17 files changed, 76 insertions(+), 49 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 52812dc050..029c47c726 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -627,6 +627,13 @@ void LLFloater::setVisible( BOOL visible ) storeVisibilityControl(); } + +void LLFloater::setIsSingleInstance(BOOL is_single_instance) +{ + mSingleInstance = is_single_instance; +} + + // virtual void LLFloater::handleVisibilityChange ( BOOL new_visibility ) { diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index a1cac64a4a..4b738f88ea 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -217,6 +217,7 @@ public: /*virtual*/ void setFocus( BOOL b ); /*virtual*/ void setIsChrome(BOOL is_chrome); /*virtual*/ void setRect(const LLRect &rect); + void setIsSingleInstance(BOOL is_single_instance); void initFloater(const Params& p); diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index be6901c36a..bb0dbc7ff0 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -1911,7 +1911,8 @@ void LLAgent::startTyping() { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - (LLNearbyChat::instance()).sendChatFromViewer("", CHAT_TYPE_START, FALSE); + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- @@ -1923,7 +1924,8 @@ void LLAgent::stopTyping() { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - (LLNearbyChat::instance()).sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index e6340e0fa3..f1b5c42ef3 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -323,12 +323,12 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - LLNearbyChat::instance().showHistory(); + (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); return FALSE; } } - LLNearbyChat::instance().showHistory(); + (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index b5b86dadc2..29d7732a68 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -293,6 +293,7 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - LLNearbyChat::instance().showTranslationCheckbox(LLTranslate::isTranslationConfigured()); + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + showTranslationCheckbox(LLTranslate::isTranslationConfigured()); closeFloater(false); } diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 0377337af6..0996af6125 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -35,6 +35,7 @@ // library #include "llaudioengine.h" #include "lldatapacker.h" +#include "llfloaterreg.h" #include "llinventory.h" #include "llkeyframemotion.h" #include "llmultigesture.h" @@ -997,7 +998,8 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - LLNearbyChat::instance().sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); gesture->mCurrentStep++; break; diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 7bb29be27b..5a5196fb7e 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -144,7 +144,7 @@ BOOL LLIMConversation::postBuild() updateHeaderAndToolbar(); - mSaveRect = isTornOff(); + mSaveRect = !getHost(); initRectControl(); if (isChatMultiTab()) @@ -267,11 +267,11 @@ void LLIMConversation::hideOrShowTitle() LLView* floater_contents = getChild("contents_view"); LLRect floater_rect = getLocalRect(); - S32 top_border_of_contents = floater_rect.mTop - (isTornOff()? floater_header_size : 0); + S32 top_border_of_contents = floater_rect.mTop - (getHost()? 0 : floater_header_size); LLRect handle_rect (0, floater_rect.mTop, floater_rect.mRight, top_border_of_contents); LLRect contents_rect (0, top_border_of_contents, floater_rect.mRight, floater_rect.mBottom); mDragHandle->setShape(handle_rect); - mDragHandle->setVisible(isTornOff()); + mDragHandle->setVisible(!getHost()); floater_contents->setShape(contents_rect); } @@ -289,8 +289,8 @@ void LLIMConversation::hideAllStandardButtons() void LLIMConversation::updateHeaderAndToolbar() { - bool is_torn_off = !getHost(); - if (!is_torn_off) + bool is_hosted = !!getHost(); + if (is_hosted) { hideAllStandardButtons(); } @@ -299,7 +299,7 @@ void LLIMConversation::updateHeaderAndToolbar() // Participant list should be visible only in torn off floaters. bool is_participant_list_visible = - is_torn_off + !is_hosted && gSavedSettings.getBOOL("IMShowControlPanel") && !mIsP2PChat; @@ -307,21 +307,21 @@ void LLIMConversation::updateHeaderAndToolbar() // Display collapse image (<<) if the floater is hosted // or if it is torn off but has an open control panel. - bool is_expanded = !is_torn_off || is_participant_list_visible; + bool is_expanded = is_hosted || is_participant_list_visible; mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); // toggle floater's drag handle and title visibility if (mDragHandle) { - mDragHandle->setTitleVisible(is_torn_off); + mDragHandle->setTitleVisible(!is_hosted); } // The button (>>) should be disabled for torn off P2P conversations. - mExpandCollapseBtn->setEnabled(!is_torn_off || !mIsP2PChat); + mExpandCollapseBtn->setEnabled(is_hosted || !mIsP2PChat); - mTearOffBtn->setImageOverlay(getString(is_torn_off? "return_icon" : "tear_off_icon")); + mTearOffBtn->setImageOverlay(getString(is_hosted? "tear_off_icon" : "return_icon")); - mCloseBtn->setVisible(!is_torn_off && !mIsNearbyChat); + mCloseBtn->setVisible(is_hosted && !mIsNearbyChat); enableDisableCallBtn(); @@ -358,9 +358,10 @@ void LLIMConversation::processChatHistoryStyleUpdate() } } - if (LLNearbyChat::instanceExists()) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) { - LLNearbyChat::instance().reloadMessages(); + nearby_chat->reloadMessages(); } } @@ -427,8 +428,8 @@ void LLIMConversation::onClose(bool app_quitting) void LLIMConversation::onTearOffClicked() { - setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE); - mSaveRect = isTornOff(); + setFollows(getHost()? FOLLOWS_NONE : FOLLOWS_ALL); + mSaveRect = !getHost(); initRectControl(); LLFloater::onClickTearOff(this); updateHeaderAndToolbar(); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index effcc9a826..f5392b442a 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -2486,9 +2486,11 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - if (LLNearbyChat::instanceExists()) + + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (nearby_chat) { - LLNearbyChat::instance().addMessage(chat); + nearby_chat->addMessage(chat); } } else // going to IM session diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index f1518fe825..25bbc82fee 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -134,6 +134,7 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) mKey = LLSD(); mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); setName("nearby_chat"); + setIsSingleInstance(TRUE); } //virtual @@ -780,20 +781,21 @@ void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BO // static void LLNearbyChat::startChat(const char* line) { - if (LLNearbyChat::instanceExists()) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) { - (LLNearbyChat::instance()).show(); - (LLNearbyChat::instance()).setVisible(TRUE); - (LLNearbyChat::instance()).setFocus(TRUE); - (LLNearbyChat::instance().mInputEditor)->setFocus(TRUE); + nearby_chat->show(); + nearby_chat->setVisible(TRUE); + nearby_chat->setFocus(TRUE); + nearby_chat->mInputEditor->setFocus(TRUE); if (line) { std::string line_string(line); - (LLNearbyChat::instance().mInputEditor)->setText(line_string); + nearby_chat->mInputEditor->setText(line_string); } - (LLNearbyChat::instance().mInputEditor)->endOfDoc(); + nearby_chat->mInputEditor->endOfDoc(); } } @@ -801,9 +803,10 @@ void LLNearbyChat::startChat(const char* line) // static void LLNearbyChat::stopChat() { - if (LLNearbyChat::instanceExists()) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) { - (LLNearbyChat::instance().mInputEditor)->setFocus(FALSE); + nearby_chat->mInputEditor->setFocus(FALSE); gAgent.stopTyping(); } } diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 379bfbee4b..4fc5cb7f76 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -35,15 +35,13 @@ #include "lloutputmonitorctrl.h" #include "llspeakers.h" #include "llscrollbar.h" -#include "llsingleton.h" #include "llviewerchat.h" #include "llpanel.h" class LLResizeBar; class LLNearbyChat - : public LLIMConversation, - public LLSingleton + : public LLIMConversation { public: // constructor for inline chat-bars (e.g. hosted in chat history window) diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index 37f4cc4c19..ca3fffeffd 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -537,7 +537,8 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } } - LLNearbyChat::instance().addMessage(chat_msg, true, args); + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + nearby_chat->addMessage(chat_msg, true, args); if(chat_msg.mSourceType == CHAT_SOURCE_AGENT && chat_msg.mFromID.notNull() @@ -553,7 +554,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, // Send event on to LLEventStream sChatWatcher->post(chat); - if( LLNearbyChat::instance().isInVisibleChain() + if( nearby_chat->isInVisibleChain() || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT && gSavedSettings.getBOOL("UseChatBubbles") ) || mChannel.isDead() diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index db8e917435..2484040ac4 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -181,13 +181,14 @@ void LLHandlerUtil::logGroupNoticeToIMGroup( // static void LLHandlerUtil::logToNearbyChat(const LLNotificationPtr& notification, EChatSourceType type) { - if (LLNearbyChat::instanceExists()) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) { LLChat chat_msg(notification->getMessage()); chat_msg.mSourceType = type; chat_msg.mFromName = SYSTEM_FROM; chat_msg.mFromID = LLUUID::null; - LLNearbyChat::instance().addMessage(chat_msg); + nearby_chat->addMessage(chat_msg); } } diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 67fc9b27dc..ef6668247c 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -85,7 +85,8 @@ bool LLTipHandler::processNotification(const LLNotificationPtr& notification) LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); // don't show toast if Nearby Chat is opened - if (LLNearbyChat::instance().isChatVisible()) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat && nearby_chat->isChatVisible()) { return false; } diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index a2dea31d9b..71608b5280 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -33,6 +33,7 @@ #include "llviewerinventory.h" #include "sound_ids.h" // for testing +#include "llfloaterreg.h" #include "llkeyboard.h" // for key shortcuts for testing #include "llinventorymodel.h" #include "llvoavatar.h" @@ -130,7 +131,8 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) { // Don't play nodding animation, since that might not blend // with the gesture animation. - LLNearbyChat::instance().sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); } } diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 7105720eb4..f8e988bc0c 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llappviewer.h" +#include "llfloaterreg.h" #include "llviewerkeyboard.h" #include "llmath.h" #include "llagent.h" @@ -543,7 +544,7 @@ void start_gesture( EKeystate s ) if (KEYSTATE_UP == s && ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) { - if (LLNearbyChat::instance().getCurrentChat().empty()) + if ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) { // No existing chat in chat editor, insert '/' LLNearbyChat::startChat("/"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 9abd269f0f..81cbc3b6c3 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2297,9 +2297,10 @@ void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string m // Treat like a system message and put in chat history. chat.mText = av_name.getCompleteName() + ": " + message; - if (LLNearbyChat::instanceExists()) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) { - LLNearbyChat::instance().addMessage(chat); + nearby_chat->addMessage(chat); } } @@ -2895,7 +2896,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // Note: lie to Nearby Chat, pretending that this is NOT an IM, because // IMs from obejcts don't open IM sessions. - if(!chat_from_system && LLNearbyChat::instanceExists()) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if(!chat_from_system && nearby_chat) { chat.mOwnerID = from_id; LLSD args; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 23d2b1633d..791cadaee4 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2493,12 +2493,14 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) return TRUE; } + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + // Traverses up the hierarchy if( keyboard_focus ) { - if (LLNearbyChat::instanceExists()) + if (nearby_chat) { - LLChatEntry* chat_editor = LLNearbyChat::instance().getChatBox(); + LLChatEntry* chat_editor = nearby_chat->getChatBox(); // arrow keys move avatar while chatting hack if (chat_editor && chat_editor->hasFocus()) @@ -2562,11 +2564,11 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) { - LLChatEntry* chat_editor = LLNearbyChat::instance().getChatBox(); + LLChatEntry* chat_editor = nearby_chat->getChatBox(); if (chat_editor) { // passing NULL here, character will be added later when it is handled by character handler. - LLNearbyChat::instance().startChat(NULL); + nearby_chat->startChat(NULL); return TRUE; } } -- cgit v1.3 From 3cf624b371eace5ec382796d7bd811d181d5e877 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Wed, 5 Sep 2012 18:48:07 +0300 Subject: CHUI-268 (Transfer the common functionality from LLNearbyChat and LLIMFloater to LLIMConversation): moved focusLost(), focusReceived and enable/disable of the call button to base class --- indra/newview/llimconversation.cpp | 42 ++++++++++++++++++++++++++++++++++++++ indra/newview/llimconversation.h | 14 +++++++++---- indra/newview/llimfloater.cpp | 39 ----------------------------------- indra/newview/llimfloater.h | 9 -------- indra/newview/llnearbychat.cpp | 23 +-------------------- indra/newview/llnearbychat.h | 7 ------- 6 files changed, 53 insertions(+), 81 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 5a5196fb7e..d8c81a7849 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -31,6 +31,8 @@ #include "llchatentry.h" #include "llchathistory.h" +#include "llchiclet.h" +#include "llchicletbar.h" #include "lldraghandle.h" #include "llfloaterreg.h" #include "llimfloater.h" @@ -53,6 +55,8 @@ LLIMConversation::LLIMConversation(const LLUUID& session_id) , mInputEditorTopPad(0) , mRefreshTimer(new LLTimer()) { + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + mCommitCallbackRegistrar.add("IMSession.Menu.Action", boost::bind(&LLIMConversation::onIMSessionMenuItemClicked, this, _2)); mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem", @@ -182,6 +186,44 @@ void LLIMConversation::draw() } } +void LLIMConversation::enableDisableCallBtn() +{ + getChildView("voice_call_btn")->setEnabled( + mSessionID.notNull() + && mSession + && mSession->mSessionInitialized + && LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking() + && mSession->mCallBackEnabled); +} + + +void LLIMConversation::onFocusReceived() +{ + setBackgroundOpaque(true); + + if (mSessionID.notNull()) + { + LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, true); + + if (getVisible()) + { + // suppress corresponding toast only if this floater is visible and have focus + LLIMModel::getInstance()->setActiveSessionID(mSessionID); + LLIMModel::instance().sendNoUnreadMessages(mSessionID); + } + } + + LLTransientDockableFloater::onFocusReceived(); +} + +void LLIMConversation::onFocusLost() +{ + setBackgroundOpaque(false); + LLTransientDockableFloater::onFocusLost(); +} + + void LLIMConversation::buildParticipantList() { if (mIsNearbyChat) diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index 26151ad1be..50feb12aed 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -33,6 +33,7 @@ #include "lltransientdockablefloater.h" #include "llviewercontrol.h" #include "lleventtimer.h" +#include "llimview.h" #include "llconversationmodel.h" class LLPanelChatControlPanel; @@ -87,9 +88,6 @@ protected: // 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; - void buildParticipantList(); void onSortMenuItemClicked(const LLSD& userdata); @@ -99,9 +97,18 @@ protected: /// Update floater header and toolbar buttons when hosted/torn off state is toggled. void updateHeaderAndToolbar(); + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + + // process focus events to set a currently active session + /* virtual */ void onFocusLost(); + /* virtual */ void onFocusReceived(); + bool mIsNearbyChat; bool mIsP2PChat; + LLIMModel::LLIMSession* mSession; + LLLayoutPanel* mParticipantListPanel; LLParticipantList* mParticipantList; LLUUID mSessionID; @@ -119,7 +126,6 @@ private: /// Refreshes the floater at a constant rate. virtual void refresh() = 0; - /** * Adjusts chat history height to fit vertically with input chat field * and avoid overlapping, since input chat field can be vertically expanded. diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index a601561c62..7b475c1e0b 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -92,24 +92,6 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) setDocked(true); } -void LLIMFloater::onFocusLost() -{ - LLIMModel::getInstance()->resetActiveSessionID(); - - LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, false); -} - -void LLIMFloater::onFocusReceived() -{ - LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, true); - - if (getVisible()) - { - // suppress corresponding toast only if this floater is visible and have focus - LLIMModel::getInstance()->setActiveSessionID(mSessionID); - LLIMModel::instance().sendNoUnreadMessages(mSessionID); - } -} // virtual void LLIMFloater::refresh() @@ -513,27 +495,6 @@ void LLIMFloater::boundVoiceChannel() } } -void LLIMFloater::enableDisableCallBtn() -{ - bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() - && LLVoiceClient::getInstance()->isVoiceWorking(); - - 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); - } -} - - void LLIMFloater::onCallButtonClicked() { LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 24a8f17feb..7b2c9e7aef 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -134,10 +134,6 @@ public: private: - // process focus events to set a currently active session - /* virtual */ void onFocusLost(); - /* virtual */ void onFocusReceived(); - /*virtual*/ void refresh(); /*virtual*/ void onClickCloseBtn(); @@ -169,9 +165,6 @@ private: void onCallButtonClicked(); - // set the enable/disable state for the Call button - virtual void enableDisableCallBtn(); - void boundVoiceChannel(); // Add the "User is typing..." indicator. @@ -184,8 +177,6 @@ private: static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); - - LLIMModel::LLIMSession* mSession; S32 mLastMessageIndex; EInstantMessage mDialog; diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 25bbc82fee..c2ad8cfda3 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -133,6 +133,7 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) setIsChrome(TRUE); mKey = LLSD(); mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); + mSessionID = LLUUID(); setName("nearby_chat"); setIsSingleInstance(TRUE); } @@ -216,21 +217,6 @@ bool LLNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) return false; } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusReceived() -{ - setBackgroundOpaque(true); - LLIMConversation::onFocusReceived(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusLost() -{ - setBackgroundOpaque(false); - LLIMConversation::onFocusLost(); -} BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) { @@ -326,13 +312,6 @@ void LLNearbyChat::setVisible(BOOL visible) } -void LLNearbyChat::enableDisableCallBtn() -{ - // bool btn_enabled = LLAgent::isActionAllowed("speak"); - - getChildView("voice_call_btn")->setEnabled(false /*btn_enabled*/); -} - void LLNearbyChat::onTearOffClicked() { LLIMConversation::onTearOffClicked(); diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 4fc5cb7f76..1db7afc01f 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -51,10 +51,6 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - // focus overrides - /*virtual*/ void onFocusLost(); - /*virtual*/ void onFocusReceived(); - /*virtual*/ void setVisible(BOOL visible); void loadHistory(); @@ -102,9 +98,6 @@ protected: void displaySpeakingIndicator(); - // set the enable/disable state for the Call button - virtual void enableDisableCallBtn(); - // Which non-zero channel did we last chat on? static S32 sLastSpecialChatChannel; -- cgit v1.3 From 47fe3b48fe32f9eb810a23d82eb08c11c41ac335 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Mon, 10 Sep 2012 13:48:37 +0300 Subject: CHUI-268 (Transfer the common functionality from LLNearbyChat and LLIMFloater to LLIMConversation): moved appendMessage() to base class --- indra/newview/llimconversation.cpp | 40 ++++++++++++++++++++++++++++++++++ indra/newview/llimconversation.h | 5 +++++ indra/newview/llimfloater.cpp | 19 ---------------- indra/newview/llimfloater.h | 1 - indra/newview/llnearbychat.cpp | 44 ++------------------------------------ indra/newview/llnearbychat.h | 2 -- 6 files changed, 47 insertions(+), 64 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index d8c81a7849..ef3b4f7404 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -223,6 +223,46 @@ void LLIMConversation::onFocusLost() LLTransientDockableFloater::onFocusLost(); } +std::string LLIMConversation::appendTime() +{ + time_t utc_time; + utc_time = time_corrected(); + std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" + +LLTrans::getString("TimeMin")+"]"; + + LLSD substitution; + + substitution["datetime"] = (S32) utc_time; + LLStringUtil::format (timeStr, substitution); + + return timeStr; +} + +void LLIMConversation::appendMessage(const LLChat& chat, const LLSD &args) +{ + LLChat& tmp_chat = const_cast(chat); + + if(tmp_chat.mTimeStr.empty()) + tmp_chat.mTimeStr = appendTime(); + + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = 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"] = + !mIsP2PChat || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + + if (mChatHistory) + { + mChatHistory->appendMessage(chat, chat_args); + } + } +} + void LLIMConversation::buildParticipantList() { diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index 50feb12aed..41a76c206e 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -104,6 +104,11 @@ protected: /* virtual */ void onFocusLost(); /* virtual */ void onFocusReceived(); + // prepare chat's params and out one message to chatHistory + void appendMessage(const LLChat& chat, const LLSD &args = 0); + + std::string appendTime(); + bool mIsNearbyChat; bool mIsP2PChat; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 7b475c1e0b..2474fe0891 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -848,25 +848,6 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) } } -void LLIMFloater::appendMessage(const LLChat& chat, const LLSD &args) -{ - LLChat& tmp_chat = const_cast(chat); - - if (!chat.mMuted) - { - tmp_chat.mFromName = chat.mFromName; - LLSD chat_args; - if (args) chat_args = 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"] = !mIsP2PChat - || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); - - mChatHistory->appendMessage(chat, chat_args); - } -} - void LLIMFloater::updateMessages() { std::list messages; diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 7b2c9e7aef..e4a67a3d56 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -152,7 +152,6 @@ private: BOOL isInviteAllowed() const; BOOL inviteToSession(const uuid_vec_t& agent_ids); - void appendMessage(const LLChat& chat, const LLSD &args = 0); static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); static void onInputEditorKeystroke(LLTextEditor* caller, void* userdata); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index c2ad8cfda3..ddd271e23f 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -69,7 +69,7 @@ S32 LLNearbyChat::sLastSpecialChatChannel = 0; -// --- 2 functions in the global namespace :( --- +// --- function in the global namespace :( --- bool isWordsName(const std::string& name) { // checking to see if it's display name plus username in parentheses @@ -89,22 +89,6 @@ bool isWordsName(const std::string& name) } } -std::string appendTime() -{ - time_t utc_time; - utc_time = time_corrected(); - std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"]"; - - LLSD substitution; - - substitution["datetime"] = (S32) utc_time; - LLStringUtil::format (timeStr, substitution); - - return timeStr; -} - - const S32 EXPANDED_HEIGHT = 266; const S32 COLLAPSED_HEIGHT = 60; const S32 EXPANDED_MIN_HEIGHT = 150; @@ -129,6 +113,7 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) mSpeakerMgr(NULL), mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) { + mIsP2PChat = false; mIsNearbyChat = true; setIsChrome(TRUE); mKey = LLSD(); @@ -604,31 +589,6 @@ void LLNearbyChat::sendChat( EChatType type ) } } - -void LLNearbyChat::appendMessage(const LLChat& chat, const LLSD &args) -{ - LLChat& tmp_chat = const_cast(chat); - - if(tmp_chat.mTimeStr.empty()) - tmp_chat.mTimeStr = appendTime(); - - if (!chat.mMuted) - { - tmp_chat.mFromName = chat.mFromName; - LLSD chat_args; - if (args) chat_args = 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"] = true; - - if (mChatHistory) - { - mChatHistory->appendMessage(chat, chat_args); - } - } -} - void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) { appendMessage(chat, args); diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 1db7afc01f..2cbafbfa62 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -108,8 +108,6 @@ protected: private: - // prepare chat's params and out one message to chatHistory - void appendMessage(const LLChat& chat, const LLSD &args = 0); void onNearbySpeakers (); /*virtual*/ void refresh(); -- cgit v1.3 From e4de40ad8e1abed99c8c8d681c1dda46e72df94f Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Fri, 14 Sep 2012 14:37:48 +0300 Subject: CHUI-119 (Add Nearby chat to Conversations floater): addit. fix: use LLSD(LLUUID::null) instead LLSD::null as "default" floater's key --- indra/llui/llfloaterreg.cpp | 2 +- indra/llui/llfloaterreg.h | 24 ++++++++++++------------ indra/newview/llnearbychat.cpp | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 9115eb7174..920525448c 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -318,7 +318,7 @@ void LLFloaterReg::showInitialVisibleInstances() BOOL isvis = LLFloater::getControlGroup()->getBOOL(controlname); if (isvis) { - showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true + showInstance(name, LLSD(LLUUID())); // keyed floaters shouldn't set save_vis to true } } } diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index a1e1f8a988..7924b2a7b8 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -90,23 +90,23 @@ public: static LLFloater* getLastFloaterCascading(); // Find / get (create) / remove / destroy - static LLFloater* findInstance(const std::string& name, const LLSD& key = LLSD()); - static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD()); - static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD()); - static bool destroyInstance(const std::string& name, const LLSD& key = LLSD()); + static LLFloater* findInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); + static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); + static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); + static bool destroyInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); // Iterators static const_instance_list_t& getFloaterList(const std::string& name); // Visibility Management // return NULL if instance not found or can't create instance (no builder) - static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), BOOL focus = FALSE); + static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(LLUUID()), BOOL focus = FALSE); // Close a floater (may destroy or set invisible) // return false if can't find instance - static bool hideInstance(const std::string& name, const LLSD& key = LLSD()); + static bool hideInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); // return true if instance is visible: - static bool toggleInstance(const std::string& name, const LLSD& key = LLSD()); - static bool instanceVisible(const std::string& name, const LLSD& key = LLSD()); + static bool toggleInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); + static bool instanceVisible(const std::string& name, const LLSD& key = LLSD(LLUUID())); static void showInitialVisibleInstances(); static void hideVisibleInstances(const std::set& exceptions = std::set()); @@ -126,23 +126,23 @@ public: static void registerControlVariables(); // Callback wrappers - static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); + static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD(LLUUID())); // Typed find / get / show template - static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD()) + static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())) { return dynamic_cast(findInstance(name, key)); } template - static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD()) + static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())) { return dynamic_cast(getInstance(name, key)); } template - static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), BOOL focus = FALSE) + static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(LLUUID()), BOOL focus = FALSE) { return dynamic_cast(showInstance(name, key, focus)); } diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index ddd271e23f..a803b35aa8 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -108,7 +108,7 @@ static LLChatTypeTrigger sChatTypeTriggers[] = { LLNearbyChat::LLNearbyChat(const LLSD& llsd) -: LLIMConversation(LLSD()), +: LLIMConversation(llsd.asUUID()), //mOutputMonitor(NULL), mSpeakerMgr(NULL), mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) @@ -116,7 +116,7 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) mIsP2PChat = false; mIsNearbyChat = true; setIsChrome(TRUE); - mKey = LLSD(); + mKey = LLSD(LLUUID()); mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); mSessionID = LLUUID(); setName("nearby_chat"); -- cgit v1.3 From fcb010e835d9b894ba6d1012ac8e3a85c5ab3400 Mon Sep 17 00:00:00 2001 From: Paul ProductEngine Date: Wed, 19 Sep 2012 19:20:21 +0300 Subject: CHUI-338 FIXED (LLAvatarNameResponder warning shown in debug console when using spinner to page through chat history viewer) - Trying to restore avatarID by its name before appending message to chat history. - Also prevented requesting avatar name by null LLUUID in LLAvatarIconCtrl::setValue --- indra/newview/llavatariconctrl.cpp | 7 ++-- indra/newview/llfloaterconversationpreview.cpp | 27 ++++++++++++--- indra/newview/llnearbychat.cpp | 40 +++++++++++----------- indra/newview/llnearbychat.h | 2 ++ .../xui/en/floater_conversation_preview.xml | 2 +- 5 files changed, 50 insertions(+), 28 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index b539ac38ed..62c6c6763b 100755 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -245,9 +245,10 @@ void LLAvatarIconCtrl::setValue(const LLSD& value) LLIconCtrl::setValue(value); } - LLAvatarNameCache::get(mAvatarId, - boost::bind(&LLAvatarIconCtrl::onAvatarNameCache, - this, _1, _2)); + if (mAvatarId != LLUUID::null) + { + LLAvatarNameCache::get(mAvatarId, boost::bind(&LLAvatarIconCtrl::onAvatarNameCache, this, _1, _2)); + } } bool LLAvatarIconCtrl::updateFromCache() diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp index 88efc39764..a3825eafc8 100644 --- a/indra/newview/llfloaterconversationpreview.cpp +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -29,6 +29,7 @@ #include "llfloaterconversationpreview.h" #include "llimview.h" #include "lllineeditor.h" +#include "llnearbychat.h" #include "llspinctrl.h" #include "lltrans.h" @@ -43,7 +44,6 @@ LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_i BOOL LLFloaterConversationPreview::postBuild() { mChatHistory = getChild("chat_history"); - getChild("more_history")->setCommitCallback(boost::bind(&LLFloaterConversationPreview::onMoreHistoryBtnClick, this)); const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID); std::string name; @@ -119,20 +119,39 @@ void LLFloaterConversationPreview::showHistory() { LLSD msg = *iter; + LLUUID from_id = LLUUID::null; std::string time = msg["time"].asString(); - LLUUID from_id = msg["from_id"].asUUID(); std::string from = msg["from"].asString(); std::string message = msg["message"].asString(); - bool is_history = msg["is_history"].asBoolean(); + + if (msg["from_id"].isDefined()) + { + from_id = msg["from_id"].asUUID(); + } + else + { + std::string legacy_name = gCacheName->buildLegacyName(from); + gCacheName->getUUID(legacy_name, from_id); + } LLChat chat; chat.mFromID = from_id; chat.mSessionID = mSessionID; chat.mFromName = from; chat.mTimeStr = time; - chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle; + chat.mChatStyle = CHAT_STYLE_HISTORY; chat.mText = message; + if (from_id.isNull() && SYSTEM_FROM == from) + { + chat.mSourceType = CHAT_SOURCE_SYSTEM; + + } + else if (from_id.isNull()) + { + chat.mSourceType = LLNearbyChat::isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; + } + mChatHistory->appendMessage(chat); } diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index a803b35aa8..76626bd5a6 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -69,26 +69,6 @@ S32 LLNearbyChat::sLastSpecialChatChannel = 0; -// --- function in the global namespace :( --- -bool isWordsName(const std::string& name) -{ - // checking to see if it's display name plus username in parentheses - S32 open_paren = name.find(" (", 0); - S32 close_paren = name.find(')', 0); - - if (open_paren != std::string::npos && - close_paren == name.length()-1) - { - return true; - } - else - { - //checking for a single space - S32 pos = name.find(' ', 0); - return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; - } -} - const S32 EXPANDED_HEIGHT = 266; const S32 COLLAPSED_HEIGHT = 60; const S32 EXPANDED_MIN_HEIGHT = 150; @@ -717,6 +697,26 @@ void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BO send_chat_from_viewer(utf8_out_text, type, channel); } +// static +bool LLNearbyChat::isWordsName(const std::string& name) +{ + // checking to see if it's display name plus username in parentheses + S32 open_paren = name.find(" (", 0); + S32 close_paren = name.find(')', 0); + + if (open_paren != std::string::npos && + close_paren == name.length()-1) + { + return true; + } + else + { + //checking for a single space + S32 pos = name.find(' ', 0); + return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + } +} + // static void LLNearbyChat::startChat(const char* line) { diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 2cbafbfa62..648098113a 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -79,6 +79,8 @@ public: static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); + static bool isWordsName(const std::string& name); + void showHistory(); protected: diff --git a/indra/newview/skins/default/xui/en/floater_conversation_preview.xml b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml index 0e5af67f68..6f1ddaaf4f 100644 --- a/indra/newview/skins/default/xui/en/floater_conversation_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml @@ -52,7 +52,7 @@ width="50"/> Date: Wed, 19 Sep 2012 17:59:37 -0700 Subject: CHUI-340 : WIP : Update time stamp for IM and voice utterance --- indra/newview/llimfloater.cpp | 3 +++ indra/newview/llnearbychat.cpp | 3 +++ indra/newview/llparticipantlist.cpp | 21 +++++++++++++++++++++ indra/newview/llparticipantlist.h | 9 +++++++++ indra/newview/llspeakers.cpp | 17 +++++++++++++++++ indra/newview/llspeakers.h | 9 +++++++++ 6 files changed, 62 insertions(+) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 2474fe0891..fbc1b8e7fe 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -907,6 +907,9 @@ void LLIMFloater::updateMessages() chat.mText = message; } + // Merov debug + llinfos << "Merov debug : LLIMFloater::updateMessages, session = " << mSessionID << ", from = " << msg["from"].asString() << ", uuid = " << msg["from_id"].asString() << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; + appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 76626bd5a6..bf47aa06a8 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -233,6 +233,9 @@ void LLNearbyChat::loadHistory() gCacheName->getUUID(legacy_name, from_id); } + // Merov debug + llinfos << "Merov debug : LLNearbyChat::loadHistory, session = " << mSessionID << ", from = " << msg[IM_FROM].asString() << ", uuid = " << msg[IM_FROM_ID].asString() << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; + LLChat chat; chat.mFromName = from; chat.mFromID = from_id; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 339cee3f95..8bc6bb60cf 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -224,12 +224,14 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, mSpeakerRemoveListener = new SpeakerRemoveListener(*this); mSpeakerClearListener = new SpeakerClearListener(*this); mSpeakerModeratorListener = new SpeakerModeratorUpdateListener(*this); + mSpeakerUpdateListener = new SpeakerUpdateListener(*this); mSpeakerMuteListener = new SpeakerMuteListener(*this); mSpeakerMgr->addListener(mSpeakerAddListener, "add"); mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); + mSpeakerMgr->addListener(mSpeakerUpdateListener, "update_speaker"); setSessionID(mSpeakerMgr->getSessionID()); @@ -584,6 +586,17 @@ bool LLParticipantList::onClearListEvent(LLPointer event, return true; } +bool LLParticipantList::onSpeakerUpdateEvent(LLPointer event, const LLSD& userdata) +{ + const LLSD& evt_data = event->getValue(); + if ( evt_data.has("id") ) + { + LLUUID id = evt_data["id"]; + llinfos << "Merov debug : onSpeakerUpdateEvent, session = " << mUUID << ", uuid = " << id << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; + } + return true; +} + bool LLParticipantList::onModeratorUpdateEvent(LLPointer event, const LLSD& userdata) { const LLSD& evt_data = event->getValue(); @@ -745,6 +758,14 @@ bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + return mParent.onSpeakerUpdateEvent(event, userdata); +} + // // LLParticipantList::SpeakerModeratorListener // diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index f8165aa292..acee68873c 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -95,6 +95,7 @@ protected: bool onRemoveItemEvent(LLPointer event, const LLSD& userdata); bool onClearListEvent(LLPointer event, const LLSD& userdata); bool onModeratorUpdateEvent(LLPointer event, const LLSD& userdata); + bool onSpeakerUpdateEvent(LLPointer event, const LLSD& userdata); bool onSpeakerMuteEvent(LLPointer event, const LLSD& userdata); /** @@ -136,6 +137,13 @@ protected: /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); }; + class SpeakerUpdateListener : public BaseSpeakerListener + { + public: + SpeakerUpdateListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + class SpeakerModeratorUpdateListener : public BaseSpeakerListener { public: @@ -264,6 +272,7 @@ private: LLPointer mSpeakerAddListener; LLPointer mSpeakerRemoveListener; LLPointer mSpeakerClearListener; + LLPointer mSpeakerUpdateListener; LLPointer mSpeakerModeratorListener; LLPointer mSpeakerMuteListener; diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 07d2f1ad6f..f50ae28421 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -84,6 +84,19 @@ bool LLSpeaker::isInVoiceChannel() return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED; } +LLSpeakerUpdateSpeakerEvent::LLSpeakerUpdateSpeakerEvent(LLSpeaker* source) +: LLEvent(source, "Speaker update speaker event"), +mSpeakerID (source->mID) +{ +} + +LLSD LLSpeakerUpdateSpeakerEvent::getValue() +{ + LLSD ret; + ret["id"] = mSpeakerID; + return ret; +} + LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source) : LLEvent(source, "Speaker add moderator event"), mSpeakerID (source->mID), @@ -374,6 +387,8 @@ void LLSpeakerMgr::update(BOOL resort_ok) { speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); speakerp->mHasSpoken = TRUE; + llinfos << "Merov debug : LLSpeakerMgr::update, session = " << getSessionID() << ", uuid = " << speaker_id << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; + speakerp->fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; // interpolate between active color and full speaking color based on power of speech output @@ -548,6 +563,8 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) { speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); speakerp->mHasSpoken = TRUE; + llinfos << "Merov debug : LLSpeakerMgr::speakerChatted, session = " << getSessionID() << ", uuid = " << speaker_id << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; + speakerp->fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } } diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 1c6f51e131..8ab08661d3 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -79,6 +79,15 @@ public: BOOL mModeratorMutedText; }; +class LLSpeakerUpdateSpeakerEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerUpdateSpeakerEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +private: + const LLUUID& mSpeakerID; +}; + class LLSpeakerUpdateModeratorEvent : public LLOldEvents::LLEvent { public: -- cgit v1.3 From fc6bbee3f4ba1abba2956ee92f7ac7ba01d0f59b Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Thu, 20 Sep 2012 20:48:20 -0700 Subject: CHUI-340 : WIP : Implement time update on all IM typing cases --- indra/newview/llconversationmodel.cpp | 9 +++++++++ indra/newview/llconversationmodel.h | 1 + indra/newview/llimfloater.cpp | 4 +++- indra/newview/llnearbychat.cpp | 5 +++-- indra/newview/llparticipantlist.cpp | 7 +------ 5 files changed, 17 insertions(+), 9 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index b39b997a55..31f9ca6a32 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -167,6 +167,15 @@ void LLConversationItemSession::setParticipantIsModerator(const LLUUID& particip } } +void LLConversationItemSession::setParticipantTimeNow(const LLUUID& participant_id) +{ + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + participant->setTimeNow(); + } +} + // The time of activity of a session is the time of the most recent participation const bool LLConversationItemSession::getTime(F64& time) const { diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index f3f99b5575..e2c88785a2 100755 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -149,6 +149,7 @@ public: void setParticipantIsMuted(const LLUUID& participant_id, bool is_muted); void setParticipantIsModerator(const LLUUID& participant_id, bool is_moderator); + void setParticipantTimeNow(const LLUUID& participant_id); bool isLoaded() { return mIsLoaded; } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index fbc1b8e7fe..8268764816 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -907,9 +907,11 @@ void LLIMFloater::updateMessages() chat.mText = message; } - // Merov debug + // Update the participant activity time + mParticipantList->setParticipantTimeNow(from_id); llinfos << "Merov debug : LLIMFloater::updateMessages, session = " << mSessionID << ", from = " << msg["from"].asString() << ", uuid = " << msg["from_id"].asString() << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; + // Add the message to the chat log appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index bf47aa06a8..0d52a9e14c 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -233,9 +233,10 @@ void LLNearbyChat::loadHistory() gCacheName->getUUID(legacy_name, from_id); } - // Merov debug + // Update the participant activity time + mParticipantList->setParticipantTimeNow(from_id); llinfos << "Merov debug : LLNearbyChat::loadHistory, session = " << mSessionID << ", from = " << msg[IM_FROM].asString() << ", uuid = " << msg[IM_FROM_ID].asString() << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; - + LLChat chat; chat.mFromName = from; chat.mFromID = from_id; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 9f470a735e..6283c8f296 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -592,12 +592,7 @@ bool LLParticipantList::onSpeakerUpdateEvent(LLPointer eve if ( evt_data.has("id") ) { LLUUID participant_id = evt_data["id"]; - LLConversationItemParticipant* participant = findParticipant(participant_id); - if (participant) - { - participant->setTimeNow(); - } - llinfos << "Merov debug : onSpeakerUpdateEvent, session = " << mUUID << ", uuid = " << participant_id << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; + setParticipantTimeNow(participant_id); } return true; } -- cgit v1.3 From b5583906d0cce652f456851732db5b1c19659662 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 21 Sep 2012 18:12:06 -0700 Subject: CHUI-340 : WIP : Fix sorting bugs on time for sessions, simplified the update time mechanism and clean up --- indra/llui/llfolderviewmodel.h | 2 +- indra/newview/llconversationmodel.cpp | 53 +++++++++++++++++++++++++--------- indra/newview/llconversationmodel.h | 10 +++---- indra/newview/llimconversation.cpp | 8 +++++ indra/newview/llimfloater.cpp | 4 --- indra/newview/llimfloatercontainer.cpp | 15 ++++++++++ indra/newview/llimfloatercontainer.h | 1 + indra/newview/llnearbychat.cpp | 4 --- indra/newview/llparticipantlist.cpp | 7 ++++- 9 files changed, 75 insertions(+), 29 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index 22bfc4dfb4..c99fa07c8b 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -226,7 +226,7 @@ public: mParent(NULL), mRootViewModel(root_view_model) { - std::for_each(mChildren.begin(), mChildren.end(), DeletePointer()); + mChildren.clear(); } void requestSort() { mSortVersion = -1; } diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 31f9ca6a32..e090d1647f 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -38,7 +38,8 @@ LLConversationItem::LLConversationItem(std::string display_name, const LLUUID& u mName(display_name), mUUID(uuid), mNeedsRefresh(true), - mConvType(CONV_UNKNOWN) + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) { } @@ -47,7 +48,8 @@ LLConversationItem::LLConversationItem(const LLUUID& uuid, LLFolderViewModelInte mName(""), mUUID(uuid), mNeedsRefresh(true), - mConvType(CONV_UNKNOWN) + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) { } @@ -56,7 +58,8 @@ LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_mod mName(""), mUUID(), mNeedsRefresh(true), - mConvType(CONV_UNKNOWN) + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) { } @@ -167,8 +170,10 @@ void LLConversationItemSession::setParticipantIsModerator(const LLUUID& particip } } -void LLConversationItemSession::setParticipantTimeNow(const LLUUID& participant_id) +void LLConversationItemSession::setTimeNow(const LLUUID& participant_id) { + mLastActiveTime = LLFrameTimer::getElapsedSeconds(); + mNeedsRefresh = true; LLConversationItemParticipant* participant = findParticipant(participant_id); if (participant) { @@ -176,11 +181,11 @@ void LLConversationItemSession::setParticipantTimeNow(const LLUUID& participant_ } } -// The time of activity of a session is the time of the most recent participation +// The time of activity of a session is the time of the most recent activity, session and participants included const bool LLConversationItemSession::getTime(F64& time) const { - bool has_time = false; - F64 most_recent_time = 0.0; + F64 most_recent_time = mLastActiveTime; + bool has_time = (most_recent_time > 0.1); LLConversationItemParticipant* participant = NULL; child_list_t::const_iterator iter; for (iter = mChildren.begin(); iter != mChildren.end(); iter++) @@ -197,7 +202,6 @@ const bool LLConversationItemSession::getTime(F64& time) const { time = most_recent_time; } - llinfos << "Merov debug : get time session, uuid = " << mUUID << ", has_time = " << has_time << ", time = " << time << llendl; return has_time; } @@ -220,8 +224,7 @@ void LLConversationItemSession::dumpDebugData() LLConversationItemParticipant::LLConversationItemParticipant(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : LLConversationItem(display_name,uuid,root_view_model), mIsMuted(false), - mIsModerator(false), - mLastActiveTime(0.0) + mIsModerator(false) { mConvType = CONV_PARTICIPANT; } @@ -265,19 +268,34 @@ bool LLConversationSort::operator()(const LLConversationItem* const& a, const LL { F64 time_a = 0.0; F64 time_b = 0.0; - if (a->getTime(time_a) && b->getTime(time_b)) + bool has_time_a = a->getTime(time_a); + bool has_time_b = b->getTime(time_b); + if (has_time_a && has_time_b) { return (time_a > time_b); } + else if (has_time_a || has_time_b) + { + // If we have only one time updated, we consider the element with time as the "highest". + // That boils down to "has_time_a" if you think about it. + return has_time_a; + } + // If not time available, we'll default to sort by name at the end of this method } else if (sort_order == LLConversationFilter::SO_DISTANCE) { F32 dist_a = 0.0; F32 dist_b = 0.0; - if (a->getDistanceToAgent(dist_a) && b->getDistanceToAgent(dist_b)) + bool has_dist_a = a->getDistanceToAgent(dist_a); + bool has_dist_b = b->getDistanceToAgent(dist_b); + if (has_dist_a && has_dist_b) { return (dist_a > dist_b); } + else if (has_dist_a || has_dist_b) + { + return has_dist_a; + } } } else if ((type_a > LLConversationItem::CONV_PARTICIPANT) && (type_b > LLConversationItem::CONV_PARTICIPANT)) @@ -288,10 +306,19 @@ bool LLConversationSort::operator()(const LLConversationItem* const& a, const LL { F64 time_a = 0.0; F64 time_b = 0.0; - if (a->getTime(time_a) && b->getTime(time_b)) + bool has_time_a = a->getTime(time_a); + bool has_time_b = b->getTime(time_b); + if (has_time_a && has_time_b) { return (time_a > time_b); } + else if (has_time_a || has_time_b) + { + // If we have only one time updated, we consider the element with time as the "highest". + // That boils down to "has_time_a" if you think about it. + return has_time_a; + } + // If not time available, we'll default to sort by name at the end of this method } else if (sort_order == LLConversationFilter::SO_SESSION_TYPE) { diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index e2c88785a2..e67aeb9aca 100755 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -106,7 +106,7 @@ public: // Methods used in sorting (see LLConversationSort::operator()) EConversationType const getType() const { return mConvType; } - virtual const bool getTime(F64& time) const { return false; } + virtual const bool getTime(F64& time) const { time = mLastActiveTime; return (time > 0.1); } virtual const bool getDistanceToAgent(F32& distance) const { return false; } // This method will be called to determine if a drop can be @@ -129,6 +129,7 @@ protected: LLUUID mUUID; // UUID of the session or the participant EConversationType mConvType; // Type of conversation item bool mNeedsRefresh; // Flag signaling to the view that something changed for this item + F64 mLastActiveTime; }; class LLConversationItemSession : public LLConversationItem @@ -149,7 +150,7 @@ public: void setParticipantIsMuted(const LLUUID& participant_id, bool is_muted); void setParticipantIsModerator(const LLUUID& participant_id, bool is_moderator); - void setParticipantTimeNow(const LLUUID& participant_id); + void setTimeNow(const LLUUID& participant_id); bool isLoaded() { return mIsLoaded; } @@ -172,18 +173,15 @@ public: bool isModerator() {return mIsModerator; } void setIsMuted(bool is_muted) { mIsMuted = is_muted; mNeedsRefresh = true; } void setIsModerator(bool is_moderator) { mIsModerator = is_moderator; mNeedsRefresh = true; } - void setTimeNow() { mLastActiveTime = LLFrameTimer::getElapsedSeconds(); } + void setTimeNow() { mLastActiveTime = LLFrameTimer::getElapsedSeconds(); mNeedsRefresh = true; } void onAvatarNameCache(const LLAvatarName& av_name); - virtual const bool getTime(F64& time) const { time = mLastActiveTime; return (time > 0.1 ? true : false); } - void dumpDebugData(); private: bool mIsMuted; // default is false bool mIsModerator; // default is false - F64 mLastActiveTime; }; // We don't want to ever filter conversations but we need to declare that class to create a conversation view model. diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index a2efe63546..5425baec6d 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -250,6 +250,14 @@ std::string LLIMConversation::appendTime() void LLIMConversation::appendMessage(const LLChat& chat, const LLSD &args) { + // Update the participant activity time + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->setTimeNow(mSessionID,chat.mFromID); + } + + LLChat& tmp_chat = const_cast(chat); if(tmp_chat.mTimeStr.empty()) diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 8268764816..43adfdfd08 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -907,10 +907,6 @@ void LLIMFloater::updateMessages() chat.mText = message; } - // Update the participant activity time - mParticipantList->setParticipantTimeNow(from_id); - llinfos << "Merov debug : LLIMFloater::updateMessages, session = " << mSessionID << ", from = " << msg["from"].asString() << ", uuid = " << msg["from_id"].asString() << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; - // Add the message to the chat log appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 4e0fba9502..f84da25baa 100755 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -710,6 +710,21 @@ void LLIMFloaterContainer::setConvItemSelect(LLUUID& session_id) } } +void LLIMFloaterContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) +{ + conversations_items_map::iterator item_it = mConversationsItems.find(session_id); + if (item_it != mConversationsItems.end()) + { + LLConversationItemSession* item = dynamic_cast(item_it->second); + if (item) + { + item->setTimeNow(participant_id); + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + } + } +} + void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid) { bool is_nearby_chat = uuid.isNull(); diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 1f526091bb..a7a5b8a391 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -124,6 +124,7 @@ private: public: void removeConversationListItem(const LLUUID& uuid, bool change_focus = true); void addConversationListItem(const LLUUID& uuid); + void setTimeNow(const LLUUID& session_id, const LLUUID& participant_id); private: LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 0d52a9e14c..76626bd5a6 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -233,10 +233,6 @@ void LLNearbyChat::loadHistory() gCacheName->getUUID(legacy_name, from_id); } - // Update the participant activity time - mParticipantList->setParticipantTimeNow(from_id); - llinfos << "Merov debug : LLNearbyChat::loadHistory, session = " << mSessionID << ", from = " << msg[IM_FROM].asString() << ", uuid = " << msg[IM_FROM_ID].asString() << ", date = " << LLFrameTimer::getElapsedSeconds() << llendl; - LLChat chat; chat.mFromName = from; chat.mFromID = from_id; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 6283c8f296..09f2716773 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -34,6 +34,7 @@ #include "llagent.h" #include "llimview.h" +#include "llimfloatercontainer.h" #include "llpanelpeoplemenus.h" #include "llnotificationsutil.h" #include "llparticipantlist.h" @@ -592,7 +593,11 @@ bool LLParticipantList::onSpeakerUpdateEvent(LLPointer eve if ( evt_data.has("id") ) { LLUUID participant_id = evt_data["id"]; - setParticipantTimeNow(participant_id); + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->setTimeNow(mUUID,participant_id); + } } return true; } -- cgit v1.3 From f9e0831ba04f99335bfb494a22435446dc0852de Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Mon, 24 Sep 2012 18:57:04 +0300 Subject: CHUI-355 FIXED Nearby chat entries do not appear in torn off nearby chat window when opening from a toast: moved setIsSingleInstance() from constructor to postBuild() for prevent of a resetting it in buildFromXML(); implemented correct set of mReuseInstance; changed type of the key of LLIMConversation from LLUUID() to LLSD() --- indra/llui/llfloater.cpp | 5 +++++ indra/llui/llfloater.h | 7 ++++--- indra/newview/llimconversation.cpp | 4 ++-- indra/newview/llimconversation.h | 2 +- indra/newview/llnearbychat.cpp | 7 +++---- indra/newview/llnearbychat.h | 4 ++-- 6 files changed, 17 insertions(+), 12 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 029c47c726..58b17f74a8 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -240,6 +240,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mTitle(p.title), mShortTitle(p.short_title), mSingleInstance(p.single_instance), + mIsReuseInitialized(p.reuse_instance.isProvided()), mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default mKey(key), mCanTearOff(p.can_tear_off), @@ -631,6 +632,10 @@ void LLFloater::setVisible( BOOL visible ) void LLFloater::setIsSingleInstance(BOOL is_single_instance) { mSingleInstance = is_single_instance; + if (!mIsReuseInitialized) + { + mReuseInstance = is_single_instance; // reuse single-instance floaters by default + } } diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 4b738f88ea..07b79d5523 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -447,9 +447,10 @@ private: LLUIString mTitle; LLUIString mShortTitle; - BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater - bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it - std::string mInstanceName; // Store the instance name so we can remove ourselves from the list + BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater + bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it + bool mIsReuseInitialized; // true if mReuseInstance already set from parameters + std::string mInstanceName; // Store the instance name so we can remove ourselves from the list BOOL mCanTearOff; BOOL mCanMinimize; diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 3ca93b1b07..2ad7f9b193 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -42,13 +42,13 @@ const F32 REFRESH_INTERVAL = 0.2f; -LLIMConversation::LLIMConversation(const LLUUID& session_id) +LLIMConversation::LLIMConversation(const LLSD& session_id) : LLTransientDockableFloater(NULL, true, session_id) , mIsP2PChat(false) , mExpandCollapseBtn(NULL) , mTearOffBtn(NULL) , mCloseBtn(NULL) - , mSessionID(session_id) + , mSessionID(session_id.asUUID()) , mParticipantList(NULL) , mChatHistory(NULL) , mInputEditor(NULL) diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index bad5eaa99f..c54081d316 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -47,7 +47,7 @@ class LLIMConversation public: LOG_CLASS(LLIMConversation); - LLIMConversation(const LLUUID& session_id); + LLIMConversation(const LLSD& session_id); ~LLIMConversation(); // reload all message with new settings of visual modes diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 76626bd5a6..71c4938ae9 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -88,7 +88,7 @@ static LLChatTypeTrigger sChatTypeTriggers[] = { LLNearbyChat::LLNearbyChat(const LLSD& llsd) -: LLIMConversation(llsd.asUUID()), +: LLIMConversation(llsd), //mOutputMonitor(NULL), mSpeakerMgr(NULL), mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) @@ -96,16 +96,15 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) mIsP2PChat = false; mIsNearbyChat = true; setIsChrome(TRUE); - mKey = LLSD(LLUUID()); mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); mSessionID = LLUUID(); - setName("nearby_chat"); - setIsSingleInstance(TRUE); } + //virtual BOOL LLNearbyChat::postBuild() { + setIsSingleInstance(TRUE); BOOL result = LLIMConversation::postBuild(); mInputEditor->setCommitCallback(boost::bind(&LLNearbyChat::onChatBoxCommit, this)); mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 648098113a..da1b58e326 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -45,8 +45,8 @@ class LLNearbyChat { public: // constructor for inline chat-bars (e.g. hosted in chat history window) - LLNearbyChat(const LLSD& key = LLSD()); - ~LLNearbyChat() {} + LLNearbyChat(const LLSD& key = LLSD(LLUUID())); + ~LLNearbyChat() {}; /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); -- cgit v1.3 From 976a2f5a442150ef89cff6b39450eabbca956d0f Mon Sep 17 00:00:00 2001 From: Paul ProductEngine Date: Wed, 26 Sep 2012 20:45:18 +0300 Subject: CHUI-344 FIXED (LLConversationViewSession: enable icon update) - This commit also fixes CHUI-345 CHUI-346 --- indra/newview/llconversationview.cpp | 84 ++++++++++++++++++++- indra/newview/llconversationview.h | 13 +++- indra/newview/llnearbychat.cpp | 1 + indra/newview/lloutputmonitorctrl.cpp | 20 ++++- indra/newview/lloutputmonitorctrl.h | 3 + .../default/textures/icons/nearby_chat_icon.png | Bin 0 -> 793 bytes indra/newview/skins/default/textures/textures.xml | 2 + .../xui/en/panel_conversation_list_item.xml | 26 ++++++- 8 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 indra/newview/skins/default/textures/icons/nearby_chat_icon.png (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index a4733bb693..ab15e20d89 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -30,10 +30,12 @@ #include "llconversationview.h" #include +#include "llagentdata.h" #include "llconversationmodel.h" #include "llimconversation.h" #include "llimfloatercontainer.h" #include "llfloaterreg.h" +#include "llgroupiconctrl.h" #include "lluictrlfactory.h" // @@ -51,20 +53,79 @@ LLConversationViewSession::LLConversationViewSession(const LLConversationViewSes LLFolderViewFolder(p), mContainer(p.container), mItemPanel(NULL), - mSessionTitle(NULL) + mCallIconLayoutPanel(NULL), + mSessionTitle(NULL), + mSpeakingIndicator(NULL) { } +LLConversationViewSession::~LLConversationViewSession() +{ + mActiveVoiceChannelConnection.disconnect(); +} + BOOL LLConversationViewSession::postBuild() { LLFolderViewItem::postBuild(); mItemPanel = LLUICtrlFactory::getInstance()->createFromFile("panel_conversation_list_item.xml", NULL, LLPanel::child_registry_t::instance()); - addChild(mItemPanel); + mCallIconLayoutPanel = mItemPanel->getChild("call_icon_panel"); mSessionTitle = mItemPanel->getChild("conversation_title"); + mActiveVoiceChannelConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLConversationViewSession::onCurrentVoiceSessionChanged, this, _1)); + mSpeakingIndicator = getChild("speaking_indicatorn"); + + LLConversationItem* vmi = dynamic_cast(getViewModelItem()); + if (vmi) + { + switch(vmi->getType()) + { + case LLConversationItem::CONV_PARTICIPANT: + case LLConversationItem::CONV_SESSION_1_ON_1: + { + LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID()); + if (session) + { + LLAvatarIconCtrl* icon = mItemPanel->getChild("avatar_icon"); + icon->setVisible(true); + icon->setValue(session->mOtherParticipantID); + mSpeakingIndicator->setSpeakerId(gAgentID, session->mSessionID); + mSpeakingIndicator->setShowParticipantsTalking(true); + } + break; + } + case LLConversationItem::CONV_SESSION_AD_HOC: + { + LLGroupIconCtrl* icon = mItemPanel->getChild("group_icon"); + icon->setVisible(true); + mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID()); + mSpeakingIndicator->setShowParticipantsTalking(true); + } + case LLConversationItem::CONV_SESSION_GROUP: + { + LLGroupIconCtrl* icon = mItemPanel->getChild("group_icon"); + icon->setVisible(true); + icon->setValue(vmi->getUUID()); + mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID()); + mSpeakingIndicator->setShowParticipantsTalking(true); + break; + } + case LLConversationItem::CONV_SESSION_NEARBY: + { + LLIconCtrl* icon = mItemPanel->getChild("nearby_chat_icon"); + icon->setVisible(true); + mSpeakingIndicator->setSpeakerId(gAgentID); + mSpeakingIndicator->setShowParticipantsTalking(true); + break; + } + + default: + break; + } + } + refresh(); return TRUE; @@ -188,6 +249,25 @@ void LLConversationViewSession::refresh() LLFolderViewFolder::refresh(); } +void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id) +{ + LLConversationItem* vmi = dynamic_cast(getViewModelItem()); + + if (vmi) + { + bool is_active = vmi->getUUID() == session_id; + bool is_nearby = vmi->getType() == LLConversationItem::CONV_SESSION_NEARBY; + + if (is_nearby) + { + mSpeakingIndicator->setSpeakerId(is_active ? gAgentID : LLUUID::null); + } + + mSpeakingIndicator->switchIndicator(is_active); + mCallIconLayoutPanel->setVisible(is_active); + } +} + // // Implementation of conversations list participant (avatar) widgets // diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index 0b98c34c73..dbc7f3b1bb 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -57,7 +57,7 @@ protected: LLIMFloaterContainer* mContainer; public: - virtual ~LLConversationViewSession( void ) { } + virtual ~LLConversationViewSession(); virtual void selectItem(); /*virtual*/ BOOL postBuild(); @@ -71,8 +71,15 @@ public: virtual void refresh(); private: - LLPanel* mItemPanel; - LLTextBox* mSessionTitle; + + void onCurrentVoiceSessionChanged(const LLUUID& session_id); + + LLPanel* mItemPanel; + LLPanel* mCallIconLayoutPanel; + LLTextBox* mSessionTitle; + LLOutputMonitorCtrl* mSpeakingIndicator; + + boost::signals2::connection mActiveVoiceChannelConnection; }; // Implementation of conversations list participant (avatar) widgets diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 71c4938ae9..75c691d99b 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -110,6 +110,7 @@ BOOL LLNearbyChat::postBuild() mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); mInputEditor->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); mInputEditor->setFocusReceivedCallback(boost::bind(&LLNearbyChat::onChatBoxFocusReceived, this)); + mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle")); // mOutputMonitor = getChild("chat_zone_indicator"); // mOutputMonitor->setVisible(FALSE); diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 096e714981..d48826779c 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -74,7 +74,8 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p) mSpeakerId(p.speaker_id), mIsAgentControl(false), mIsSwitchDirty(false), - mShouldSwitchOn(false) + mShouldSwitchOn(false), + mShowParticipantsTalking(false) { //static LLUIColor output_monitor_muted_color = LLUIColorTable::instance().getColor("OutputMonitorMutedColor", LLColor4::orange); //static LLUIColor output_monitor_overdriven_color = LLUIColorTable::instance().getColor("OutputMonitorOverdrivenColor", LLColor4::red); @@ -157,6 +158,23 @@ void LLOutputMonitorCtrl::draw() } } + if ((mPower == 0.f && !mIsTalking) && mShowParticipantsTalking) + { + std::set participant_uuids; + LLVoiceClient::instance().getParticipantList(participant_uuids); + std::set::const_iterator part_it = participant_uuids.begin(); + + F32 power = 0; + for (; part_it != participant_uuids.end(); ++part_it) + { + if (power = LLVoiceClient::instance().getCurrentPower(*part_it)) + { + mPower = power; + break; + } + } + } + LLPointer icon; if (mIsMuted) { diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 7b02e84744..44d4d6f64b 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -82,6 +82,8 @@ public: void setIsTalking(bool val) { mIsTalking = val; } + void setShowParticipantsTalking(bool show) { mShowParticipantsTalking = show; } + /** * Sets avatar UUID to interact with voice channel. * @@ -132,6 +134,7 @@ private: bool mIsAgentControl; bool mIsMuted; bool mIsTalking; + bool mShowParticipantsTalking; LLPointer mImageMute; LLPointer mImageOff; LLPointer mImageOn; diff --git a/indra/newview/skins/default/textures/icons/nearby_chat_icon.png b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png new file mode 100644 index 0000000000..7c3ad40381 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png differ diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 47b0c12fa0..a124041565 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -363,6 +363,8 @@ with the same filename but different name + + diff --git a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml index 375ea79ebe..56056ed560 100644 --- a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml @@ -13,13 +13,33 @@ layout="topleft" left="5" top="2" + visible="false" width="20" /> + + -- cgit v1.3 From 78e6b910a5012352ab265e52a78b0d340ac7ce27 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Thu, 27 Sep 2012 20:04:26 +0300 Subject: CHUI-370, CHUI-372 FIXED Viewer crash when voice calling another user from people panel then opening conversation floater or when starting a IM chat when conversation floater has not been previously opened: connecting floater to the host postponed until the end of its initialization --- indra/newview/llnearbychat.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 75c691d99b..2982f30102 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -122,8 +122,6 @@ BOOL LLNearbyChat::postBuild() // it is used for show the item's name in the conversations list setTitle(LLTrans::getString("NearbyChatTitle")); - addToHost(); - //for menu LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; @@ -312,6 +310,7 @@ void LLNearbyChat::addToHost() // virtual void LLNearbyChat::onOpen(const LLSD& key) { + addToHost(); LLIMConversation::onOpen(key); showTranslationCheckbox(LLTranslate::isTranslationConfigured()); } -- cgit v1.3 From 8b2ed2e2050fcb8836be120902f44417613adb13 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Fri, 28 Sep 2012 18:22:00 +0300 Subject: CHUI-370, CHUI-372 ADDITIONAL FIX (Viewer crash when voice calling another user from people panel then opening conversation floater or when starting a IM chat when conversation floater has not been previously opened): fixed nearby chat positioning --- indra/newview/llimfloatercontainer.cpp | 1 + indra/newview/llnearbychat.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 indra/newview/llimfloatercontainer.cpp (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp old mode 100755 new mode 100644 index e64247cd60..a74ebdae7a --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -432,6 +432,7 @@ void LLIMFloaterContainer::setVisible(BOOL visible) // *TODO: find a way to move this to XML as a default panel or something like that LLSD name("nearby_chat"); LLFloaterReg::toggleInstanceOrBringToFront(name); + LLFloaterReg::getTypedInstance("nearby_chat")->addToHost(); } } diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 2982f30102..4b35092f2d 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -310,7 +310,6 @@ void LLNearbyChat::addToHost() // virtual void LLNearbyChat::onOpen(const LLSD& key) { - addToHost(); LLIMConversation::onOpen(key); showTranslationCheckbox(LLTranslate::isTranslationConfigured()); } -- cgit v1.3 From a8159facdeb478abf094d8ba8ad229c0e282f9f6 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Tue, 2 Oct 2012 19:36:46 +0300 Subject: CHUI-374 FIXED (Nearby chat is torn off and cannot be docked if nearby chat is received while conversation floater is closed) Nearby_chat is created only after the creation of the im_container --- indra/newview/llimfloater.cpp | 28 +++++++++++++++------------- indra/newview/llimfloatercontainer.cpp | 11 ++++++++--- indra/newview/llnearbychat.cpp | 16 +++++++++++++++- indra/newview/llnearbychat.h | 5 +++++ indra/newview/llnearbychathandler.cpp | 1 + indra/newview/llviewerfloaterreg.cpp | 2 +- 6 files changed, 45 insertions(+), 18 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index d11504d312..99337bd5f3 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -111,23 +111,25 @@ void LLIMFloater::onClickCloseBtn() { LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); - if (session == NULL) + if (session != NULL) { - llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; - return; - } + bool is_call_with_chat = session->isGroupSessionType() + || session->isAdHocSessionType() || session->isP2PSessionType(); - bool is_call_with_chat = session->isGroupSessionType() - || session->isAdHocSessionType() || session->isP2PSessionType(); + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - - if (is_call_with_chat && voice_channel != NULL - && voice_channel->isActive()) + if (is_call_with_chat && voice_channel != NULL + && voice_channel->isActive()) + { + LLSD payload; + payload["session_id"] = mSessionID; + LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + return; + } + } + else { - LLSD payload; - payload["session_id"] = mSessionID; - LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; return; } diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index a33fc2c57e..237748179c 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -463,21 +463,26 @@ void LLIMFloaterContainer::tabClose() } void LLIMFloaterContainer::setVisible(BOOL visible) -{ +{ LLNearbyChat* nearby_chat; if (visible) { // Make sure we have the Nearby Chat present when showing the conversation container - LLIMConversation* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); if (nearby_chat == NULL) { // If not found, force the creation of the nearby chat conversation panel // *TODO: find a way to move this to XML as a default panel or something like that LLSD name("nearby_chat"); LLFloaterReg::toggleInstanceOrBringToFront(name); - LLFloaterReg::getTypedInstance("nearby_chat")->addToHost(); } } + nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (nearby_chat && !nearby_chat->isHostSet()) + { + nearby_chat->addToHost(); + } + // We need to show/hide all the associated conversations that have been torn off // (and therefore, are not longer managed by the multifloater), // so that they show/hide with the conversations manager. diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 4b35092f2d..b96b486868 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -91,7 +91,8 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) : LLIMConversation(llsd), //mOutputMonitor(NULL), mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT), + mIsHostSet(false) { mIsP2PChat = false; mIsNearbyChat = true; @@ -100,6 +101,12 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) mSessionID = LLUUID(); } +//static +LLNearbyChat* LLNearbyChat::buildFloater(const LLSD& key) +{ + LLFloaterReg::getInstance("im_container"); + return new LLNearbyChat(key); +} //virtual BOOL LLNearbyChat::postBuild() @@ -304,9 +311,16 @@ void LLNearbyChat::addToHost() setHost(NULL); } } + + mIsHostSet = true; } } +bool LLNearbyChat::isHostSet() +{ + return mIsHostSet; +} + // virtual void LLNearbyChat::onOpen(const LLSD& key) { diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 3987212e4c..93168ba96a 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -48,6 +48,8 @@ public: LLNearbyChat(const LLSD& key = LLSD(LLUUID())); ~LLNearbyChat() {} + static LLNearbyChat* buildFloater(const LLSD& key); + /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); @@ -76,6 +78,8 @@ public: static void startChat(const char* line); static void stopChat(); + bool isHostSet(); + static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); @@ -117,6 +121,7 @@ private: LLHandle mPopupMenuHandle; std::vector mMessageArchive; + bool mIsHostSet; }; #endif diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index f3e17ea61b..7834f6d320 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -487,6 +487,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, if(chat_msg.mText.empty()) return;//don't process empty messages + LLFloaterReg::getInstance("im_container"); LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); // Build notification data diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 4cd5ecc754..c751550523 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -194,7 +194,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLNearbyChat::buildFloater); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); -- cgit v1.3 From 8aa424128ad06567f4c32c6672dc63d9b2111efa Mon Sep 17 00:00:00 2001 From: Gilbert Gonzales Date: Thu, 11 Oct 2012 10:38:34 -0700 Subject: CHUI-380: Before code review changes. Now the user selects a conversation or participant of a conversation the correct chat floater will appear. Focus will be redirected to the chat input text box automatically allowing the user to type into the input text box. --- indra/newview/llconversationview.cpp | 28 ++++++++++++++++++++++++--- indra/newview/llconversationview.h | 1 + indra/newview/llimfloater.cpp | 37 +++++++++++++++++++++--------------- indra/newview/llimfloater.h | 1 + indra/newview/llnearbychat.cpp | 20 +++++++++++++++++++ indra/newview/llnearbychat.h | 2 +- 6 files changed, 70 insertions(+), 19 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index d4eb551f7a..112c38d8b8 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -32,6 +32,8 @@ #include #include "llagentdata.h" #include "llconversationmodel.h" +#include "llimfloater.h" +#include "llnearbychat.h" #include "llimconversation.h" #include "llimfloatercontainer.h" #include "llfloaterreg.h" @@ -407,21 +409,41 @@ void LLConversationViewParticipant::draw() static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); - const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled const LLFontGL* font = getLabelFontForStyle(mLabelStyle); F32 right_x = 0; F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; F32 text_left = (F32)getLabelXPos(); - LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; + LLColor4 color = mIsSelected ? sHighlightFgColor : sFgColor; - drawHighlight(show_context, filled, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); + drawHighlight(show_context, true, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); drawLabel(font, text_left, y, color, right_x); LLView::draw(); } +void LLConversationViewParticipant::selectItem() +{ + LLConversationItem* vmi = this->getParentFolder() ? static_cast(this->getParentFolder()->getViewModelItem()) : NULL; + + if(vmi) + { + //When null, show the nearby chat conversation floater + if(vmi->getUUID().isNull()) + { + LLNearbyChat* nearbyChat = LLFloaterReg::findTypedInstance("nearby_chat"); + nearbyChat->show(); + } + //Otherwise, show the IM conversation floater + else + { + LLIMFloater::show(vmi->getUUID()); + } + } + + LLFolderViewItem::selectItem(); +} void LLConversationViewParticipant::refresh() { diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index c81c70b456..bd95387bbe 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -113,6 +113,7 @@ public: }; virtual ~LLConversationViewParticipant( void ) { } + void selectItem(); bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); } virtual void refresh(); void addToFolder(LLFolderViewFolder* folder); diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 99337bd5f3..990b9c45f9 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -719,6 +719,27 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock) } } +void LLIMFloater::setFocus(BOOL focusFlag) +{ + LLTransientDockableFloater::setFocus(focusFlag); + + BOOL is_minimized = focusFlag && isChatMultiTab() + ? LLIMFloaterContainer::getInstance()->isMinimized() + : !focusFlag; + + //Redirect focus to input editor + if (!is_minimized && mChatHistory && mInputEditor) + { + //only if floater was construced and initialized from xml + updateMessages(); + //prevent stealing focus when opening a background IM tab (EXT-5387, checking focus for EXT-6781) + if (!isChatMultiTab() || hasFocus()) + { + mInputEditor->setFocus(TRUE); + } + } +} + void LLIMFloater::setVisible(BOOL visible) { LLNotificationsUI::LLScreenChannel* channel = static_cast @@ -734,21 +755,6 @@ void LLIMFloater::setVisible(BOOL visible) channel->redrawToasts(); } - BOOL is_minimized = visible && isChatMultiTab() - ? LLIMFloaterContainer::getInstance()->isMinimized() - : !visible; - - if (!is_minimized && mChatHistory && mInputEditor) - { - //only if floater was construced and initialized from xml - updateMessages(); - //prevent stealing focus when opening a background IM tab (EXT-5387, checking focus for EXT-6781) - if (!isChatMultiTab() || hasFocus()) - { - mInputEditor->setFocus(TRUE); - } - } - if(!visible) { LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet(mSessionID); @@ -761,6 +767,7 @@ void LLIMFloater::setVisible(BOOL visible) if (visible && isInVisibleChain()) { sIMFloaterShowedSignal(mSessionID); + setFocus(TRUE); } } diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 5ed1d1ab35..26daf00afd 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -65,6 +65,7 @@ public: // LLView overrides /*virtual*/ BOOL postBuild(); + /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); // Check typing timeout timer. diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index b96b486868..e0778afd86 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -271,11 +271,31 @@ void LLNearbyChat::removeScreenChat() } } +void LLNearbyChat::setFocus(BOOL focusFlag) +{ + LLTransientDockableFloater::setFocus(focusFlag); + + BOOL is_minimized = focusFlag && isChatMultiTab() + ? LLIMFloaterContainer::getInstance()->isMinimized() + : !focusFlag; + + //Redirect focus to input editor + if (!is_minimized && mChatHistory && mInputEditor) + { + //prevent stealing focus when opening a background IM tab (EXT-5387, checking focus for EXT-6781) + if (!isChatMultiTab() || hasFocus()) + { + mInputEditor->setFocus(TRUE); + } + } +} + void LLNearbyChat::setVisible(BOOL visible) { if(visible) { removeScreenChat(); + setFocus(TRUE); } LLIMConversation::setVisible(visible); diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 93168ba96a..7ada4daea8 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -52,7 +52,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - + /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); void loadHistory(); -- cgit v1.3 From 375f380ea388b37c34ace9570822d0c117b3d2f1 Mon Sep 17 00:00:00 2001 From: Gilbert Gonzales Date: Thu, 11 Oct 2012 12:17:46 -0700 Subject: CHUI-380: Code cleanup after code review. --- indra/newview/llimfloater.cpp | 13 ++----------- indra/newview/llnearbychat.cpp | 12 ++---------- 2 files changed, 4 insertions(+), 21 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 990b9c45f9..a1ed1e0b01 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -723,20 +723,11 @@ void LLIMFloater::setFocus(BOOL focusFlag) { LLTransientDockableFloater::setFocus(focusFlag); - BOOL is_minimized = focusFlag && isChatMultiTab() - ? LLIMFloaterContainer::getInstance()->isMinimized() - : !focusFlag; - //Redirect focus to input editor - if (!is_minimized && mChatHistory && mInputEditor) + if (focusFlag) { - //only if floater was construced and initialized from xml updateMessages(); - //prevent stealing focus when opening a background IM tab (EXT-5387, checking focus for EXT-6781) - if (!isChatMultiTab() || hasFocus()) - { - mInputEditor->setFocus(TRUE); - } + mInputEditor->setFocus(TRUE); } } diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index e0778afd86..a0ddc9b52b 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -275,18 +275,10 @@ void LLNearbyChat::setFocus(BOOL focusFlag) { LLTransientDockableFloater::setFocus(focusFlag); - BOOL is_minimized = focusFlag && isChatMultiTab() - ? LLIMFloaterContainer::getInstance()->isMinimized() - : !focusFlag; - //Redirect focus to input editor - if (!is_minimized && mChatHistory && mInputEditor) + if (focusFlag) { - //prevent stealing focus when opening a background IM tab (EXT-5387, checking focus for EXT-6781) - if (!isChatMultiTab() || hasFocus()) - { - mInputEditor->setFocus(TRUE); - } + mInputEditor->setFocus(TRUE); } } -- cgit v1.3 From f4b10aaebbaf8eb66d41ffa703f6ed7b909ba7e7 Mon Sep 17 00:00:00 2001 From: Gilbert Gonzales Date: Thu, 11 Oct 2012 17:48:39 -0700 Subject: CHUI-380: Fixing a bug that occurred when right clicking nearby chat and then right clicking a participant under a conversation. This would result in both the participant and conversation to be highlighted. Still a work in progress. This change involves introducing an old bug CHUI 289 and finding a different approach to solving it. --- indra/newview/llconversationview.cpp | 2 +- indra/newview/llimconversation.cpp | 4 ++-- indra/newview/llimfloater.cpp | 5 ++++- indra/newview/llnearbychat.cpp | 15 ++++++++------- 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index f9a3a05e59..70f2446752 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -415,7 +415,7 @@ void LLConversationViewParticipant::draw() F32 text_left = (F32)getLabelXPos(); LLColor4 color = mIsSelected ? sHighlightFgColor : sFgColor; - drawHighlight(show_context, true, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); + drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); drawLabel(font, text_left, y, color, right_x); LLView::draw(); diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 9f3c6d0f3d..2027f79eea 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -222,8 +222,8 @@ void LLIMConversation::onFocusReceived() if (! mHadFocus) { - LLIMFloaterContainer* container = LLIMFloaterContainer::getInstance(); - container->setConvItemSelect(mSessionID); + // LLIMFloaterContainer* container = LLIMFloaterContainer::getInstance(); + // container->setConvItemSelect(mSessionID); } } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index a8add9c6ab..e4032738a7 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -725,6 +725,7 @@ void LLIMFloater::setFocus(BOOL focusFlag) updateMessages(); mInputEditor->setFocus(TRUE); } + } void LLIMFloater::setVisible(BOOL visible) @@ -754,8 +755,10 @@ void LLIMFloater::setVisible(BOOL visible) if (visible && isInVisibleChain()) { sIMFloaterShowedSignal(mSessionID); - setFocus(TRUE); + } + + setFocus(visible); } BOOL LLIMFloater::getVisible() diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index a0ddc9b52b..a89ae4a2dc 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -274,23 +274,24 @@ void LLNearbyChat::removeScreenChat() void LLNearbyChat::setFocus(BOOL focusFlag) { LLTransientDockableFloater::setFocus(focusFlag); - + //Redirect focus to input editor if (focusFlag) { mInputEditor->setFocus(TRUE); } + } void LLNearbyChat::setVisible(BOOL visible) { - if(visible) - { - removeScreenChat(); - setFocus(TRUE); - } - LLIMConversation::setVisible(visible); + + if(visible) + { + removeScreenChat(); + } + setFocus(visible); } -- cgit v1.3 From 0d25cca2e1e117962aad57dc951c102bec52cb50 Mon Sep 17 00:00:00 2001 From: William Todd Stinson Date: Mon, 22 Oct 2012 15:01:03 -0700 Subject: Backed out changeset: 4202e227f8e4 --- indra/llui/llfloaterreg.cpp | 2 +- indra/llui/llfloaterreg.h | 24 ++++++++++++------------ indra/newview/llnearbychat.cpp | 12 ++++++------ 3 files changed, 19 insertions(+), 19 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 920525448c..9115eb7174 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -318,7 +318,7 @@ void LLFloaterReg::showInitialVisibleInstances() BOOL isvis = LLFloater::getControlGroup()->getBOOL(controlname); if (isvis) { - showInstance(name, LLSD(LLUUID())); // keyed floaters shouldn't set save_vis to true + showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true } } } diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index 7924b2a7b8..a1e1f8a988 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -90,23 +90,23 @@ public: static LLFloater* getLastFloaterCascading(); // Find / get (create) / remove / destroy - static LLFloater* findInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); - static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); - static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); - static bool destroyInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); + static LLFloater* findInstance(const std::string& name, const LLSD& key = LLSD()); + static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD()); + static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD()); + static bool destroyInstance(const std::string& name, const LLSD& key = LLSD()); // Iterators static const_instance_list_t& getFloaterList(const std::string& name); // Visibility Management // return NULL if instance not found or can't create instance (no builder) - static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(LLUUID()), BOOL focus = FALSE); + static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), BOOL focus = FALSE); // Close a floater (may destroy or set invisible) // return false if can't find instance - static bool hideInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); + static bool hideInstance(const std::string& name, const LLSD& key = LLSD()); // return true if instance is visible: - static bool toggleInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())); - static bool instanceVisible(const std::string& name, const LLSD& key = LLSD(LLUUID())); + static bool toggleInstance(const std::string& name, const LLSD& key = LLSD()); + static bool instanceVisible(const std::string& name, const LLSD& key = LLSD()); static void showInitialVisibleInstances(); static void hideVisibleInstances(const std::set& exceptions = std::set()); @@ -126,23 +126,23 @@ public: static void registerControlVariables(); // Callback wrappers - static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD(LLUUID())); + static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD()); // Typed find / get / show template - static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())) + static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD()) { return dynamic_cast(findInstance(name, key)); } template - static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD(LLUUID())) + static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD()) { return dynamic_cast(getInstance(name, key)); } template - static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(LLUUID()), BOOL focus = FALSE) + static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), BOOL focus = FALSE) { return dynamic_cast(showInstance(name, key, focus)); } diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index a89ae4a2dc..5b274dd389 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -287,10 +287,10 @@ void LLNearbyChat::setVisible(BOOL visible) { LLIMConversation::setVisible(visible); - if(visible) - { - removeScreenChat(); - } + if(visible) + { + removeScreenChat(); + } setFocus(visible); } @@ -327,7 +327,7 @@ void LLNearbyChat::addToHost() mIsHostSet = true; } -} + } bool LLNearbyChat::isHostSet() { @@ -722,7 +722,7 @@ void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BO send_chat_from_viewer(utf8_out_text, type, channel); } -// static +// static bool LLNearbyChat::isWordsName(const std::string& name) { // checking to see if it's display name plus username in parentheses -- cgit v1.3 From a8ca9dc5a98d0bd99581d17be45a36c602ce87fd Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Wed, 31 Oct 2012 17:51:55 +0200 Subject: CHUI-374 ADD. FIX, CHUI-442 FIXED (Nearby chat is torn off and cannot be docked if nearby chat is received while conversation floater is closed) - implement. lazy creating of container --- indra/newview/llchicletbar.cpp | 2 +- indra/newview/llimconversation.cpp | 49 ++++++++++++++++++++++++++++++++++ indra/newview/llimconversation.h | 11 ++++++++ indra/newview/llimfloater.cpp | 34 ++--------------------- indra/newview/llimfloater.h | 1 - indra/newview/llimfloatercontainer.cpp | 8 +++--- indra/newview/llnearbychat.cpp | 34 ++--------------------- indra/newview/llnearbychat.h | 5 +--- 8 files changed, 70 insertions(+), 74 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llchicletbar.cpp b/indra/newview/llchicletbar.cpp index 39f5d0b8f6..3ebb83b336 100644 --- a/indra/newview/llchicletbar.cpp +++ b/indra/newview/llchicletbar.cpp @@ -97,7 +97,7 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam // Do not spawn chiclet when using the new multitab conversation UI if (LLIMConversation::isChatMultiTab()) { - LLIMFloater::addToHost(session_id); + LLIMConversation::addToHost(session_id); return; } diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 74bf8cb6fe..b687e18cae 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -116,6 +116,55 @@ LLIMConversation* LLIMConversation::getConversation(const LLUUID& uuid) return conv; }; +void LLIMConversation::setVisible(BOOL visible) +{ + LLTransientDockableFloater::setVisible(visible); + + if(visible) + { + LLIMConversation::addToHost(mSessionID); + } + setFocus(visible); +} + + + +void LLIMConversation::addToHost(const LLUUID& session_id) +{ + if ((session_id.notNull() && !gIMMgr->hasSession(session_id)) + || !LLIMConversation::isChatMultiTab()) + { + return; + } + + // Get the floater: this will create the instance if it didn't exist + LLIMConversation* conversp = LLIMConversation::getConversation(session_id); + if (conversp) + { + LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); + + // Do not add again existing floaters + if (floater_container && !conversp->isHostAttached()) + { + conversp->setHostAttached(true); + + if (!conversp->isNearbyChat() + || gSavedSettings.getBOOL("NearbyChatIsNotTornOff")) + { + floater_container->addFloater(conversp, TRUE, LLTabContainer::END); + } + else + { + // setting of the "potential" host for Nearby Chat: this sequence sets + // LLFloater::mHostHandle = NULL (a current host), but + // LLFloater::mLastHostHandle = floater_container (a "future" host) + conversp->setHost(floater_container); + conversp->setHost(NULL); + } + + } + } +} BOOL LLIMConversation::postBuild() { diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index 603e0d0197..bff4cb4a31 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -59,17 +59,27 @@ public: */ static bool isChatMultiTab(); + // add conversation to container + static void addToHost(const LLUUID& session_id); + + bool isHostAttached() {return mIsHostAttached;} + void setHostAttached(bool is_attached) {mIsHostAttached = is_attached;} + static LLIMConversation* findConversation(const LLUUID& uuid); static LLIMConversation* getConversation(const LLUUID& uuid); // show/hide the translation check box void showTranslationCheckbox(const BOOL visible = FALSE); + bool isNearbyChat() {return mIsNearbyChat;} + // LLFloater overrides /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void onClose(bool app_quitting); /*virtual*/ BOOL postBuild(); /*virtual*/ void draw(); + /*virtual*/ void setVisible(BOOL visible); + protected: @@ -139,6 +149,7 @@ private: void reshapeChatHistory(); bool checkIfTornOff(); + bool mIsHostAttached; LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. }; diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 1af5def5f0..3545b8ff18 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -582,37 +582,6 @@ void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) } } -void LLIMFloater::addToHost(const LLUUID& session_id) -{ - if (!LLIMConversation::isChatMultiTab() || !gIMMgr->hasSession(session_id)) - { - return; - } - - // Test the existence of the floater before we try to create it - bool exist = findInstance(session_id); - - // Get the floater: this will create the instance if it didn't exist - LLIMFloater* floater = getInstance(session_id); - if (floater) - { - - LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); - - // Do not add again existing floaters - if (!exist) - { - // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; - // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists - LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; - if (floater_container) - { - floater_container->addFloater(floater, FALSE, i_pt); - } - } - } -} - //static LLIMFloater* LLIMFloater::show(const LLUUID& session_id) @@ -721,7 +690,7 @@ void LLIMFloater::setVisible(BOOL visible) (LLNotificationsUI::LLChannelManager::getInstance()-> findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); - LLTransientDockableFloater::setVisible(visible); + LLIMConversation::setVisible(visible); // update notification channel state if(channel) @@ -1309,6 +1278,7 @@ void LLIMFloater::sRemoveTypingIndicator(const LLSD& data) floater->removeTypingIndicator(); } +// static void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) { LLIMFloater::addToHost(session_id); diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 8a0d6f10e0..6c69ed3462 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -72,7 +72,6 @@ public: 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 onClose(bool app_quitting); diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 00ae0b8fd8..65dc024aea 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -98,7 +98,7 @@ LLIMFloaterContainer::~LLIMFloaterContainer() void LLIMFloaterContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) { - LLIMFloater::addToHost(session_id); + LLIMConversation::addToHost(session_id); addConversationListItem(session_id); } @@ -109,7 +109,7 @@ void LLIMFloaterContainer::sessionActivated(const LLUUID& session_id, const std: void LLIMFloaterContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) { - LLIMFloater::addToHost(session_id); + LLIMConversation::addToHost(session_id); addConversationListItem(session_id); } @@ -490,9 +490,9 @@ void LLIMFloaterContainer::setVisible(BOOL visible) } nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); - if (nearby_chat && !nearby_chat->isHostSet()) + if (nearby_chat) { - nearby_chat->addToHost(); + LLIMConversation::addToHost(LLUUID()); } // We need to show/hide all the associated conversations that have been torn off diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 5b274dd389..d1c7c6bfd7 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -91,8 +91,7 @@ LLNearbyChat::LLNearbyChat(const LLSD& llsd) : LLIMConversation(llsd), //mOutputMonitor(NULL), mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT), - mIsHostSet(false) + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) { mIsP2PChat = false; mIsNearbyChat = true; @@ -283,7 +282,7 @@ void LLNearbyChat::setFocus(BOOL focusFlag) } -void LLNearbyChat::setVisible(BOOL visible) +void LLNearbyChat::setVisible(BOOL visible) { LLIMConversation::setVisible(visible); @@ -304,35 +303,6 @@ void LLNearbyChat::onTearOffClicked() gSavedSettings.setBOOL("NearbyChatIsNotTornOff", in_the_multifloater); } -void LLNearbyChat::addToHost() -{ - if ( LLIMConversation::isChatMultiTab()) - { - LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); - if (im_box) - { - if (gSavedSettings.getBOOL("NearbyChatIsNotTornOff")) - { - im_box->addFloater(this, TRUE, LLTabContainer::END); - } - else - { - // setting of the "potential" host: this sequence sets - // LLFloater::mHostHandle = NULL (a current host), but - // LLFloater::mLastHostHandle = im_box (a "future" host) - setHost(im_box); - setHost(NULL); - } - } - - mIsHostSet = true; - } - } - -bool LLNearbyChat::isHostSet() -{ - return mIsHostSet; -} // virtual void LLNearbyChat::onOpen(const LLSD& key) diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 7ada4daea8..b155fd3c26 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -53,7 +53,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void setFocus(BOOL focusFlag); - /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setVisible(BOOL visible); void loadHistory(); void reloadMessages(); @@ -78,8 +78,6 @@ public: static void startChat(const char* line); static void stopChat(); - bool isHostSet(); - static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); @@ -121,7 +119,6 @@ private: LLHandle mPopupMenuHandle; std::vector mMessageArchive; - bool mIsHostSet; }; #endif -- cgit v1.3 From 990a8c6085b43cb0290a631b664b3160938b0536 Mon Sep 17 00:00:00 2001 From: MaximB ProductEngine Date: Tue, 6 Nov 2012 22:44:22 +0200 Subject: CHUI-462 (Torn-off Nearby Chat can't be closed) --- indra/newview/llimconversation.cpp | 4 ---- indra/newview/llimfloatercontainer.cpp | 7 +++++++ indra/newview/llimfloatercontainer.h | 2 ++ indra/newview/llnearbychat.cpp | 22 +++++++++++++++++++++- indra/newview/llnearbychat.h | 2 ++ 5 files changed, 32 insertions(+), 5 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index b687e18cae..44cf300930 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -202,10 +202,6 @@ BOOL LLIMConversation::postBuild() if (isChatMultiTab()) { - if (mIsNearbyChat) - { - setCanClose(FALSE); - } result = LLFloater::postBuild(); } else diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index f85aa9a353..c9cd013317 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -1475,4 +1475,11 @@ void LLIMFloaterContainer::openNearbyChat() } } +void LLIMFloaterContainer::onNearbyChatClosed() +{ + // If nearby chat is the only remaining conversation and it is closed, close whole conversation floater as well + if (mConversationsItems.size() == 1) + closeFloater(); +} + // EOF diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 05ea94019b..16218fc287 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -92,6 +92,8 @@ public: LLUUID getSelectedSession() { return mSelectedSession; } void setSelectedSession(LLUUID sessionID) { mSelectedSession = sessionID; } + void onNearbyChatClosed(); + private: typedef std::map avatarID_panel_map_t; avatarID_panel_map_t mSessions; diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index d1c7c6bfd7..cf42fcb91a 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -293,7 +293,7 @@ void LLNearbyChat::setVisible(BOOL visible) setFocus(visible); } - +// virtual void LLNearbyChat::onTearOffClicked() { LLIMConversation::onTearOffClicked(); @@ -311,6 +311,26 @@ void LLNearbyChat::onOpen(const LLSD& key) showTranslationCheckbox(LLTranslate::isTranslationConfigured()); } +// virtual +void LLNearbyChat::onClose(bool app_quitting) +{ + // Override LLIMConversation::onClose() so that Nearby Chat is not removed from the conversation floater +} + +// virtual +void LLNearbyChat::onClickCloseBtn() +{ + if (!isTornOff()) + return; + onTearOffClicked(); + + LLIMFloaterContainer *im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->onNearbyChatClosed(); + } +} + void LLNearbyChat::onChatFontChange(LLFontGL* fontp) { // Update things with the new font whohoo diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index b155fd3c26..a6c4439b1d 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -52,6 +52,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); @@ -96,6 +97,7 @@ protected: void onChatFontChange(LLFontGL* fontp); /*virtual*/ void onTearOffClicked(); + /*virtual*/ void onClickCloseBtn(); static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); EChatType processChatTypeTriggers(EChatType type, std::string &str); -- cgit v1.3 From d1f45654d91762af4e614de2156304d7acad6619 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Wed, 7 Nov 2012 18:45:02 +0200 Subject: CHUI-473, CHUI-482 FIXED (Clicking on nearby chat toast to open Conversation floater does not show Nearby Chat conversation selected in list; Nearby chat conversation is not selected in list by default when it is the only conversation ): implement. new logic in LLIMFloaterContainer for the syncronous select the conv. list's item and corresponding convers. floater; removed floater selecting from conv. items; fixed bug with item select --- indra/llui/llfolderviewitem.cpp | 2 +- indra/newview/llconversationview.cpp | 76 +++++++++++++--------------------- indra/newview/llconversationview.h | 6 ++- indra/newview/llimconversation.cpp | 23 +++++++++- indra/newview/llimconversation.h | 2 + indra/newview/llimfloater.cpp | 13 ------ indra/newview/llimfloater.h | 3 +- indra/newview/llimfloatercontainer.cpp | 41 ++++++++++++++++++ indra/newview/llimfloatercontainer.h | 2 + indra/newview/llnearbychat.cpp | 18 +++----- indra/newview/llnearbychat.h | 1 - 11 files changed, 107 insertions(+), 80 deletions(-) (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 7c63cad1b7..21fb9bff90 100755 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -410,8 +410,8 @@ void LLFolderViewItem::selectItem(void) { if (mIsSelected == FALSE) { - getViewModelItem()->selectItem(); mIsSelected = TRUE; + getViewModelItem()->selectItem(); } } diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index 2d3a008bf4..34b5976e3e 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -203,6 +203,17 @@ void LLConversationViewSession::draw() LLView::draw(); } +BOOL LLConversationViewSession::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + LLConversationItem* item = dynamic_cast(getViewModelItem()); + LLUUID session_id = item? item->getUUID() : LLUUID(); + + (LLFloaterReg::getTypedInstance("im_container"))-> + selectConversationPair(session_id, false); + + return LLFolderViewFolder::handleMouseDown(x, y, mask); +} + // virtual S32 LLConversationViewSession::arrange(S32* width, S32* height) { @@ -233,29 +244,6 @@ void LLConversationViewSession::toggleOpen() } } -void LLConversationViewSession::selectItem() -{ - - LLConversationItem* item = dynamic_cast(mViewModelItem); - LLFloater* session_floater = LLIMConversation::getConversation(item->getUUID()); - LLMultiFloater* host_floater = session_floater->getHost(); - - if (host_floater == mContainer) - { - // Always expand the message pane if the panel is hosted by the container - mContainer->collapseMessagesPane(false); - // Switch to the conversation floater that is being selected - mContainer->selectFloater(session_floater); - } - - // Set the focus on the selected floater - session_floater->setFocus(TRUE); - // Store the active session - LLIMFloaterContainer::getInstance()->setSelectedSession(item->getUUID()); - - - LLFolderViewItem::selectItem(); -} void LLConversationViewSession::toggleMinimizedMode(bool is_minimized) { @@ -441,31 +429,6 @@ void LLConversationViewParticipant::draw() LLView::draw(); } -void LLConversationViewParticipant::selectItem() -{ - LLConversationItem* vmi = this->getParentFolder() ? static_cast(this->getParentFolder()->getViewModelItem()) : NULL; - LLIMFloaterContainer* container = LLIMFloaterContainer::getInstance(); - LLFloater* session_floater; - - if(vmi) - { - session_floater = LLIMConversation::getConversation(vmi->getUUID()); - - //Only execute when switching floaters (conversations) - if(vmi->getUUID() != container->getSelectedSession()) - { - container->selectFloater(session_floater); - // Store the active session - container->setSelectedSession(vmi->getUUID()); - } - - //Redirect focus to the conversation floater - session_floater->setFocus(TRUE); - } - - LLFolderViewItem::selectItem(); -} - void LLConversationViewParticipant::refresh() { // Refresh the participant view from its model data @@ -514,6 +477,23 @@ void LLConversationViewParticipant::onMouseLeave(S32 x, S32 y, MASK mask) LLFolderViewItem::onMouseLeave(x, y, mask); } +BOOL LLConversationViewParticipant::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + LLConversationItem* item = NULL; + LLConversationViewSession* session_widget = + dynamic_cast(this->getParentFolder()); + if (session_widget) + { + item = dynamic_cast(session_widget->getViewModelItem()); + } + LLUUID session_id = item? item->getUUID() : LLUUID(); + + (LLFloaterReg::getTypedInstance("im_container"))-> + selectConversationPair(session_id, false); + + return LLFolderViewItem::handleMouseDown(x, y, mask); +} + S32 LLConversationViewParticipant::getLabelXPos() { return mAvatarIcon->getRect().mRight + mIconPad; diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index aeca747260..ac91d2d26f 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -60,10 +60,10 @@ protected: public: virtual ~LLConversationViewSession(); - virtual void selectItem(); /*virtual*/ BOOL postBuild(); /*virtual*/ void draw(); + /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); /*virtual*/ S32 arrange(S32* width, S32* height); @@ -117,11 +117,13 @@ public: }; virtual ~LLConversationViewParticipant( void ) { } - void selectItem(); + bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); } virtual void refresh(); void addToFolder(LLFolderViewFolder* folder); + /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + void onMouseEnter(S32 x, S32 y, MASK mask); void onMouseLeave(S32 x, S32 y, MASK mask); diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 833feff3c4..827059513f 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -128,6 +128,22 @@ void LLIMConversation::setVisible(BOOL visible) setFocus(visible); } +/*virtual*/ +void LLIMConversation::setFocus(BOOL focus) +{ + LLTransientDockableFloater::setFocus(focus); + + //Redirect focus to input editor + if (focus) + { + updateMessages(); + + if (mInputEditor) + { + mInputEditor->setFocus(TRUE); + } + } +} void LLIMConversation::addToHost(const LLUUID& session_id) @@ -247,7 +263,6 @@ void LLIMConversation::enableDisableCallBtn() && mSession->mCallBackEnabled); } - void LLIMConversation::onFocusReceived() { setBackgroundOpaque(true); @@ -258,6 +273,12 @@ void LLIMConversation::onFocusReceived() } LLTransientDockableFloater::onFocusReceived(); + + LLIMFloaterContainer* container = LLFloaterReg::getTypedInstance("im_container"); + if (container) + { + container->selectConversationPair(mSessionID, true); + } } void LLIMConversation::onFocusLost() diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index bff4cb4a31..d1e2bfff55 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -79,7 +79,9 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void draw(); /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setFocus(BOOL focus); + virtual void updateMessages() {} protected: diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 3545b8ff18..2ff883da67 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -671,19 +671,6 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock) } } -void LLIMFloater::setFocus(BOOL focusFlag) -{ - LLTransientDockableFloater::setFocus(focusFlag); - - //Redirect focus to input editor - if (focusFlag) - { - updateMessages(); - mInputEditor->setFocus(TRUE); - } - -} - void LLIMFloater::setVisible(BOOL visible) { LLNotificationsUI::LLScreenChannel* channel = static_cast diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 6c69ed3462..1fae3cff50 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -65,7 +65,6 @@ public: // LLView overrides /*virtual*/ BOOL postBuild(); - /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); // Check typing timeout timer. @@ -86,7 +85,7 @@ public: void sessionInitReplyReceived(const LLUUID& im_session_id); // get new messages from LLIMModel - void updateMessages(); + /*virtual*/ void updateMessages(); void reloadMessages(); static void onSendMsg(LLUICtrl*, void*); void sendMsgFromInputEditor(); diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index af43b1ac38..c2ad94c423 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -1091,6 +1091,47 @@ void LLIMFloaterContainer::selectConversation(const LLUUID& session_id) } } +// Synchronous select the conversation item and the conversation floater +BOOL LLIMFloaterContainer::selectConversationPair(const LLUUID& session_id, bool select_widget) +{ + BOOL handled = TRUE; + + /* widget processing */ + if (select_widget) + { + LLFolderViewItem* widget = mConversationsWidgets[session_id]; + if (widget && widget->getParentFolder()) + { + widget->getParentFolder()->setSelection(widget, FALSE, FALSE); + } + } + + /* floater processing */ + + if (session_id != getSelectedSession()) + { + // Store the active session + setSelectedSession(session_id); + + LLIMConversation* session_floater = LLIMConversation::getConversation(session_id); + + if (session_floater->getHost()) + { + // Always expand the message pane if the panel is hosted by the container + collapseMessagesPane(false); + // Switch to the conversation floater that is being selected + selectFloater(session_floater); + } + + // Set the focus on the selected floater + if (!session_floater->hasFocus()) + { + session_floater->setFocus(TRUE); + } + } + + return handled; +} void LLIMFloaterContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) { diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 05ea94019b..25387fe83b 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -68,6 +68,8 @@ public: void showConversation(const LLUUID& session_id); void selectConversation(const LLUUID& session_id); + BOOL selectConversationPair(const LLUUID& session_id, bool select_widget); + /*virtual*/ void tabClose(); static LLFloater* getCurrentVoiceFloater(); diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index d1c7c6bfd7..a30d8b685e 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -195,8 +195,13 @@ BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. if(mChatHistory) + { mChatHistory->setFocus(TRUE); - return LLPanel::handleMouseDown(x, y, mask); + } + + BOOL handled = LLPanel::handleMouseDown(x, y, mask); + setFocus(handled); + return handled; } void LLNearbyChat::reloadMessages() @@ -270,17 +275,6 @@ void LLNearbyChat::removeScreenChat() } } -void LLNearbyChat::setFocus(BOOL focusFlag) -{ - LLTransientDockableFloater::setFocus(focusFlag); - - //Redirect focus to input editor - if (focusFlag) - { - mInputEditor->setFocus(TRUE); - } - -} void LLNearbyChat::setVisible(BOOL visible) { diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index b155fd3c26..11346a313c 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -52,7 +52,6 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); void loadHistory(); -- cgit v1.3 From 486bdf32845e248ec4923224f1f4ea5d239ac0f3 Mon Sep 17 00:00:00 2001 From: AlexanderP ProductEngine Date: Fri, 9 Nov 2012 12:45:36 +0200 Subject: CHUI-337 FIXED: To avoid confusion with a classes "...conversation..." and in accordance with the naming convention in the project, some classes and corresponding files should be renamed: LLIMConversation -> LLFloaterIMSessionTab LLIMFloater -> LLFloaterIMSession LLNearbyChat -> LLFloaterIMNearbyChat LLIMFloaterContainer -> LLFloaterIMContainer LLNearbyChatBarListener -> LLFloaterIMNearbyChatListener LLNearbyChatHandler -> LLFloaterIMNearbyChatHandler --- indra/newview/CMakeLists.txt | 24 +- indra/newview/llagent.cpp | 6 +- indra/newview/llappviewer.cpp | 4 +- indra/newview/llavataractions.cpp | 6 +- indra/newview/llavatariconctrl.cpp | 2 +- indra/newview/llchatbar.cpp | 2 +- indra/newview/llchatitemscontainerctrl.cpp | 38 +- indra/newview/llchatitemscontainerctrl.h | 8 +- indra/newview/llchiclet.cpp | 16 +- indra/newview/llchiclet.h | 2 +- indra/newview/llchicletbar.cpp | 10 +- indra/newview/llconversationlog.cpp | 6 +- indra/newview/llconversationlog.h | 4 +- indra/newview/llconversationloglistitem.cpp | 6 +- indra/newview/llconversationloglistitem.h | 2 +- indra/newview/llconversationview.cpp | 14 +- indra/newview/llconversationview.h | 8 +- indra/newview/llfloaterconversationpreview.cpp | 4 +- indra/newview/llfloaterimcontainer.cpp | 1557 +++++++++++++++++++++++ indra/newview/llfloaterimcontainer.h | 178 +++ indra/newview/llfloaterimnearbychat.cpp | 867 +++++++++++++ indra/newview/llfloaterimnearbychat.h | 125 ++ indra/newview/llfloaterimnearbychathandler.cpp | 630 +++++++++ indra/newview/llfloaterimnearbychathandler.h | 54 + indra/newview/llfloaterimnearbychatlistener.cpp | 100 ++ indra/newview/llfloaterimnearbychatlistener.h | 50 + indra/newview/llfloaterimsession.cpp | 1202 +++++++++++++++++ indra/newview/llfloaterimsession.h | 196 +++ indra/newview/llfloaterimsessiontab.cpp | 743 +++++++++++ indra/newview/llfloaterimsessiontab.h | 176 +++ indra/newview/llfloaterpreference.cpp | 6 +- indra/newview/llfloatertranslationsettings.cpp | 4 +- indra/newview/llgesturemgr.cpp | 4 +- indra/newview/llgroupactions.cpp | 4 +- indra/newview/llgroupiconctrl.cpp | 2 +- indra/newview/llimconversation.cpp | 743 ----------- indra/newview/llimconversation.h | 176 --- indra/newview/llimfloater.cpp | 1202 ----------------- indra/newview/llimfloater.h | 196 --- indra/newview/llimfloatercontainer.cpp | 1557 ----------------------- indra/newview/llimfloatercontainer.h | 178 --- indra/newview/llimpanel.cpp | 2 +- indra/newview/llimview.cpp | 36 +- indra/newview/llinventorybridge.cpp | 4 +- indra/newview/llinventorypanel.cpp | 4 +- indra/newview/llnearbychat.cpp | 867 ------------- indra/newview/llnearbychat.h | 125 -- indra/newview/llnearbychatbarlistener.cpp | 100 -- indra/newview/llnearbychatbarlistener.h | 50 - indra/newview/llnearbychathandler.cpp | 630 --------- indra/newview/llnearbychathandler.h | 54 - indra/newview/llnotificationhandler.h | 2 +- indra/newview/llnotificationhandlerutil.cpp | 10 +- indra/newview/llnotificationmanager.cpp | 4 +- indra/newview/llnotificationmanager.h | 2 +- indra/newview/llnotificationtiphandler.cpp | 6 +- indra/newview/llparticipantlist.cpp | 6 +- indra/newview/llscreenchannel.cpp | 2 +- indra/newview/llscriptfloater.cpp | 2 +- indra/newview/llstartup.cpp | 8 +- indra/newview/lltoastnotifypanel.cpp | 2 +- indra/newview/llviewerfloaterreg.cpp | 12 +- indra/newview/llviewergesture.cpp | 4 +- indra/newview/llviewerkeyboard.cpp | 10 +- indra/newview/llviewermessage.cpp | 6 +- indra/newview/llviewerwindow.cpp | 6 +- 66 files changed, 6033 insertions(+), 6033 deletions(-) create mode 100644 indra/newview/llfloaterimcontainer.cpp create mode 100644 indra/newview/llfloaterimcontainer.h create mode 100644 indra/newview/llfloaterimnearbychat.cpp create mode 100644 indra/newview/llfloaterimnearbychat.h create mode 100644 indra/newview/llfloaterimnearbychathandler.cpp create mode 100644 indra/newview/llfloaterimnearbychathandler.h create mode 100644 indra/newview/llfloaterimnearbychatlistener.cpp create mode 100644 indra/newview/llfloaterimnearbychatlistener.h create mode 100644 indra/newview/llfloaterimsession.cpp create mode 100644 indra/newview/llfloaterimsession.h create mode 100644 indra/newview/llfloaterimsessiontab.cpp create mode 100644 indra/newview/llfloaterimsessiontab.h delete mode 100644 indra/newview/llimconversation.cpp delete mode 100644 indra/newview/llimconversation.h delete mode 100644 indra/newview/llimfloater.cpp delete mode 100644 indra/newview/llimfloater.h delete mode 100644 indra/newview/llimfloatercontainer.cpp delete mode 100644 indra/newview/llimfloatercontainer.h delete mode 100644 indra/newview/llnearbychat.cpp delete mode 100644 indra/newview/llnearbychat.h delete mode 100644 indra/newview/llnearbychatbarlistener.cpp delete mode 100644 indra/newview/llnearbychatbarlistener.h delete mode 100644 indra/newview/llnearbychathandler.cpp delete mode 100644 indra/newview/llnearbychathandler.h (limited to 'indra/newview/llnearbychat.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 2c7e96f1e4..45c719f28d 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -299,9 +299,9 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp - llimconversation.cpp - llimfloater.cpp - llimfloatercontainer.cpp + llfloaterimsessiontab.cpp + llfloaterimsession.cpp + llfloaterimcontainer.cpp llimhandler.cpp llimview.cpp llinspect.cpp @@ -353,9 +353,9 @@ set(viewer_SOURCE_FILES llnameeditor.cpp llnamelistctrl.cpp llnavigationbar.cpp - llnearbychat.cpp - llnearbychathandler.cpp - llnearbychatbarlistener.cpp + llfloaterimnearbychat.cpp + llfloaterimnearbychathandler.cpp + llfloaterimnearbychatlistener.cpp llnetmap.cpp llnotificationalerthandler.cpp llnotificationgrouphandler.cpp @@ -882,9 +882,9 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h - llimconversation.h - llimfloater.h - llimfloatercontainer.h + llfloaterimsessiontab.h + llfloaterimsession.h + llfloaterimcontainer.h llimview.h llinspect.h llinspectavatar.h @@ -936,9 +936,9 @@ set(viewer_HEADER_FILES llnameeditor.h llnamelistctrl.h llnavigationbar.h - llnearbychat.h - llnearbychathandler.h - llnearbychatbarlistener.h + llfloaterimnearbychat.h + llfloaterimnearbychathandler.h + llfloaterimnearbychatlistener.h llnetmap.h llnotificationhandler.h llnotificationmanager.h diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index cefd5c72e8..9c3a7ac45b 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -54,7 +54,7 @@ #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llnotificationsutil.h" #include "llpanelpathfindingrebakenavmesh.h" #include "llpaneltopinfobar.h" @@ -1899,7 +1899,7 @@ void LLAgent::startTyping() { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - (LLFloaterReg::getTypedInstance("nearby_chat"))-> + (LLFloaterReg::getTypedInstance("nearby_chat"))-> sendChatFromViewer("", CHAT_TYPE_START, FALSE); } @@ -1912,7 +1912,7 @@ void LLAgent::stopTyping() { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - (LLFloaterReg::getTypedInstance("nearby_chat"))-> + (LLFloaterReg::getTypedInstance("nearby_chat"))-> sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index b23e5866dc..8abe9bcfc1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -42,7 +42,7 @@ #include "llagentcamera.h" #include "llagentlanguage.h" #include "llagentwearables.h" -#include "llimfloatercontainer.h" +#include "llfloaterimcontainer.h" #include "llwindow.h" #include "llviewerstats.h" #include "llviewerstatsrecorder.h" @@ -1204,7 +1204,7 @@ bool LLAppViewer::mainLoop() LLVoiceChannel::initClass(); LLVoiceClient::getInstance()->init(gServicePump); - LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLIMFloaterContainer::onCurrentChannelChanged, _1), true); + LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true); LLTimer frameTimer,idleTimer; LLTimer debugTime; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 130428f3c0..4f57498506 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -56,7 +56,7 @@ #include "llinventorybridge.h" #include "llinventorymodel.h" // for gInventory.findCategoryUUIDForType #include "llinventorypanel.h" -#include "llimfloatercontainer.h" +#include "llfloaterimcontainer.h" #include "llimview.h" // for gIMMgr #include "llmutelist.h" #include "llnotificationsutil.h" // for LLNotificationsUtil @@ -184,7 +184,7 @@ static void on_avatar_name_cache_start_im(const LLUUID& agent_id, LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id); if (session_id != LLUUID::null) { - LLIMFloaterContainer::getInstance()->showConversation(session_id); + LLFloaterIMContainer::getInstance()->showConversation(session_id); } make_ui_sound("UISndStartIM"); } @@ -302,7 +302,7 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& float return; } - LLIMFloaterContainer::getInstance()->showConversation(session_id); + LLFloaterIMContainer::getInstance()->showConversation(session_id); make_ui_sound("UISndStartIM"); } diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index 6355f0db56..b7278d4a3a 100755 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -38,7 +38,7 @@ #include "llmenugl.h" #include "lluictrlfactory.h" #include "llagentdata.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" // library includes #include "llavatarnamecache.h" diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index d6095cce07..27138e6c06 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -672,7 +672,7 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl) /* Cruft - global gChatHandler declared below has been commented out, - so this class is never used. See similar code in llnearbychatbar.cpp + so this class is never used. See similar code in llfloaterimnearbychatbar.cpp class LLChatHandler : public LLCommandHandler { public: diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index f1b5c42ef3..a1a9463d43 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -35,7 +35,7 @@ #include "llfloaterreg.h" #include "lllocalcliprect.h" #include "lltrans.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llviewercontrol.h" #include "llagentdata.h" @@ -81,18 +81,18 @@ public: LLObjectHandler gObjectHandler; //******************************************************************************************************************* -//LLNearbyChatToastPanel +//LLFloaterIMNearbyChatToastPanel //******************************************************************************************************************* -LLNearbyChatToastPanel* LLNearbyChatToastPanel::createInstance() +LLFloaterIMNearbyChatToastPanel* LLFloaterIMNearbyChatToastPanel::createInstance() { - LLNearbyChatToastPanel* item = new LLNearbyChatToastPanel(); + LLFloaterIMNearbyChatToastPanel* item = new LLFloaterIMNearbyChatToastPanel(); item->buildFromFile("panel_chat_item.xml"); item->setFollows(FOLLOWS_NONE); return item; } -void LLNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_parent ) +void LLFloaterIMNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_parent ) { LLPanel::reshape(width, height,called_from_parent); @@ -121,12 +121,12 @@ void LLNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_p msg_text->setRect(msg_text_rect); } -BOOL LLNearbyChatToastPanel::postBuild() +BOOL LLFloaterIMNearbyChatToastPanel::postBuild() { return LLPanel::postBuild(); } -void LLNearbyChatToastPanel::addMessage(LLSD& notification) +void LLFloaterIMNearbyChatToastPanel::addMessage(LLSD& notification) { std::string messageText = notification["message"].asString(); // UTF-8 line of text @@ -178,7 +178,7 @@ void LLNearbyChatToastPanel::addMessage(LLSD& notification) } -void LLNearbyChatToastPanel::init(LLSD& notification) +void LLFloaterIMNearbyChatToastPanel::init(LLSD& notification) { std::string messageText = notification["message"].asString(); // UTF-8 line of text std::string fromName = notification["from"].asString(); // agent or object name @@ -273,7 +273,7 @@ void LLNearbyChatToastPanel::init(LLSD& notification) mIsDirty = true;//will set Avatar Icon in draw } -void LLNearbyChatToastPanel::snapToMessageHeight () +void LLFloaterIMNearbyChatToastPanel::snapToMessageHeight () { LLChatMsgBox* text_box = getChild("msg_text", false); S32 new_height = llmax (text_box->getTextPixelHeight() + 2*text_box->getVPad() + 2*msg_height_pad, 25); @@ -288,22 +288,22 @@ void LLNearbyChatToastPanel::snapToMessageHeight () } -void LLNearbyChatToastPanel::onMouseLeave (S32 x, S32 y, MASK mask) +void LLFloaterIMNearbyChatToastPanel::onMouseLeave (S32 x, S32 y, MASK mask) { } -void LLNearbyChatToastPanel::onMouseEnter (S32 x, S32 y, MASK mask) +void LLFloaterIMNearbyChatToastPanel::onMouseEnter (S32 x, S32 y, MASK mask) { if(mSourceType != CHAT_SOURCE_AGENT) return; } -BOOL LLNearbyChatToastPanel::handleMouseDown (S32 x, S32 y, MASK mask) +BOOL LLFloaterIMNearbyChatToastPanel::handleMouseDown (S32 x, S32 y, MASK mask) { return LLPanel::handleMouseDown(x,y,mask); } -BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) +BOOL LLFloaterIMNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) { /* fix for request EXT-4780 @@ -323,16 +323,16 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); + (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); return FALSE; } } - (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); + (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } -void LLNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e) +void LLFloaterIMNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e) { LLUICtrl* icon = getChild("avatar_icon", false); if(icon) @@ -340,7 +340,7 @@ void LLNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e) } -bool LLNearbyChatToastPanel::canAddText () +bool LLFloaterIMNearbyChatToastPanel::canAddText () { LLChatMsgBox* msg_text = findChild("msg_text"); if(!msg_text) @@ -348,7 +348,7 @@ bool LLNearbyChatToastPanel::canAddText () return msg_text->getLineCount()<10; } -BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask) +BOOL LLFloaterIMNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask) { LLUICtrl* avatar_icon = getChild("avatar_icon", false); @@ -360,7 +360,7 @@ BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask) return TRUE; return LLPanel::handleRightMouseDown(x,y,mask); } -void LLNearbyChatToastPanel::draw() +void LLFloaterIMNearbyChatToastPanel::draw() { LLPanel::draw(); diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h index 89b0c4f37a..54b6499d52 100644 --- a/indra/newview/llchatitemscontainerctrl.h +++ b/indra/newview/llchatitemscontainerctrl.h @@ -40,18 +40,18 @@ typedef enum e_show_item_header CHATITEMHEADER_SHOW_BOTH } EShowItemHeader; -class LLNearbyChatToastPanel : public LLPanel +class LLFloaterIMNearbyChatToastPanel : public LLPanel { protected: - LLNearbyChatToastPanel() + LLFloaterIMNearbyChatToastPanel() : mIsDirty(false), mSourceType(CHAT_SOURCE_OBJECT) {}; public: - ~LLNearbyChatToastPanel(){} + ~LLFloaterIMNearbyChatToastPanel(){} - static LLNearbyChatToastPanel* createInstance(); + static LLFloaterIMNearbyChatToastPanel* createInstance(); const LLUUID& getFromID() const { return mFromID;} const std::string& getFromName() const { return mFromName; } diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index e328186fd6..64d8a68a99 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -33,8 +33,8 @@ #include "lleventtimer.h" #include "llgroupactions.h" #include "lliconctrl.h" -#include "llimfloater.h" -#include "llimfloatercontainer.h" +#include "llfloaterimsession.h" +#include "llfloaterimcontainer.h" #include "llimview.h" #include "llfloaterreg.h" #include "lllocalcliprect.h" @@ -605,7 +605,7 @@ bool LLIMChiclet::getShowNewMessagesIcon() void LLIMChiclet::onMouseDown() { - LLIMFloater::toggle(getSessionId()); + LLFloaterIMSession::toggle(getSessionId()); setCounter(0); } @@ -754,7 +754,7 @@ void LLIMP2PChiclet::updateMenuItems() if(getSessionId().isNull()) return; - LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId()); + LLFloaterIMSession* open_im_floater = LLFloaterIMSession::findInstance(getSessionId()); bool open_window_exists = open_im_floater && open_im_floater->getVisible(); mPopupMenu->getChild("Send IM")->setEnabled(!open_window_exists); @@ -1030,7 +1030,7 @@ void LLIMGroupChiclet::updateMenuItems() if(getSessionId().isNull()) return; - LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId()); + LLFloaterIMSession* open_im_floater = LLFloaterIMSession::findInstance(getSessionId()); bool open_window_exists = open_im_floater && open_im_floater->getVisible(); mPopupMenu->getChild("Chat")->setEnabled(!open_window_exists); } @@ -1116,7 +1116,7 @@ void LLChicletPanel::onMessageCountChanged(const LLSD& data) LLUUID session_id = data["session_id"].asUUID(); S32 unread = data["participant_unread"].asInteger(); - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); if (im_floater && im_floater->getVisible() && im_floater->hasFocus()) { unread = 0; @@ -1197,7 +1197,7 @@ void LLChicletPanel::onCurrentVoiceChannelChanged(const LLUUID& session_id) chiclet->setShowSpeaker(true); if (gSavedSettings.getBOOL("OpenIMOnVoice")) { - LLIMFloaterContainer::getInstance()->showConversation(session_id); + LLFloaterIMContainer::getInstance()->showConversation(session_id); } } } @@ -1688,7 +1688,7 @@ bool LLChicletPanel::isAnyIMFloaterDoked() for (chiclet_list_t::iterator it = mChicletList.begin(); it != mChicletList.end(); it++) { - LLIMFloater* im_floater = LLFloaterReg::findTypedInstance( + LLFloaterIMSession* im_floater = LLFloaterReg::findTypedInstance( "impanel", (*it)->getSessionId()); if (im_floater != NULL && im_floater->getVisible() && !im_floater->isMinimized() && im_floater->isDocked()) diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 6395f5b694..3c8389e20d 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -37,7 +37,7 @@ #include "llnotifications.h" class LLMenuGL; -class LLIMFloater; +class LLFloaterIMSession; /** * Class for displaying amount of messages/notifications(unread). diff --git a/indra/newview/llchicletbar.cpp b/indra/newview/llchicletbar.cpp index 3ebb83b336..ad7890b47a 100644 --- a/indra/newview/llchicletbar.cpp +++ b/indra/newview/llchicletbar.cpp @@ -34,7 +34,7 @@ // newview includes #include "llchiclet.h" -#include "llimfloater.h" // for LLIMFloater +#include "llfloaterimsession.h" // for LLFloaterIMSession #include "llpaneltopinfobar.h" #include "llsyswellwindow.h" @@ -95,9 +95,9 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam if (session->isP2P() && session->isOtherParticipantAvaline()) return; // Do not spawn chiclet when using the new multitab conversation UI - if (LLIMConversation::isChatMultiTab()) + if (LLFloaterIMSessionTab::isChatMultiTab()) { - LLIMConversation::addToHost(session_id); + LLFloaterIMSessionTab::addToHost(session_id); return; } @@ -109,7 +109,7 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam chiclet->setIMSessionName(name); chiclet->setOtherParticipantId(other_participant_id); - LLIMFloater::onIMChicletCreated(session_id); + LLFloaterIMSession::onIMChicletCreated(session_id); } else @@ -124,7 +124,7 @@ void LLChicletBar::sessionRemoved(const LLUUID& session_id) if(getChicletPanel()) { // IM floater should be closed when session removed and associated chiclet closed - LLIMFloater* im_floater = LLFloaterReg::findTypedInstance("impanel", session_id); + LLFloaterIMSession* im_floater = LLFloaterReg::findTypedInstance("impanel", session_id); if (im_floater != NULL && !im_floater->getStartConferenceInSameFloater()) { // Close the IM floater only if we are not planning to close the P2P chat diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index 7a5a476efb..3d2b6a5c00 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -140,15 +140,15 @@ bool LLConversation::isOlderThan(U32 days) const void LLConversation::setListenIMFloaterOpened() { - LLIMFloater* floater = LLIMFloater::findInstance(mSessionID); + LLFloaterIMSession* floater = LLFloaterIMSession::findInstance(mSessionID); - bool offline_ims_visible = LLIMFloater::isVisible(floater) && floater->hasFocus(); + bool offline_ims_visible = LLFloaterIMSession::isVisible(floater) && floater->hasFocus(); // we don't need to listen for im floater with this conversation is opened // if floater is already opened or this conversation doesn't have unread offline messages if (mHasOfflineIMs && !offline_ims_visible) { - mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1)); + mIMFloaterShowedConnection = LLFloaterIMSession::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1)); } else { diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h index b92cf0f5e2..7d0b9113c6 100644 --- a/indra/newview/llconversationlog.h +++ b/indra/newview/llconversationlog.h @@ -27,7 +27,7 @@ #define LLCONVERSATIONLOG_H_ #include "llcallingcard.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" #include "llimview.h" class LLConversationLogObserver; @@ -80,7 +80,7 @@ public: private: /* - * If conversation has unread offline messages sets callback for opening LLIMFloater + * If conversation has unread offline messages sets callback for opening LLFloaterIMSession * with this conversation. */ void setListenIMFloaterOpened(); diff --git a/indra/newview/llconversationloglistitem.cpp b/indra/newview/llconversationloglistitem.cpp index b4ae5f19da..9fad0e603e 100644 --- a/indra/newview/llconversationloglistitem.cpp +++ b/indra/newview/llconversationloglistitem.cpp @@ -47,13 +47,13 @@ LLConversationLogListItem::LLConversationLogListItem(const LLConversation* conve { buildFromFile("panel_conversation_log_list_item.xml"); - LLIMFloater* floater = LLIMFloater::findInstance(mConversation->getSessionID()); + LLFloaterIMSession* floater = LLFloaterIMSession::findInstance(mConversation->getSessionID()); - bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus(); + bool ims_are_read = LLFloaterIMSession::isVisible(floater) && floater->hasFocus(); if (mConversation->hasOfflineMessages() && !ims_are_read) { - mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversationLogListItem::onIMFloaterShown, this, _1)); + mIMFloaterShowedConnection = LLFloaterIMSession::setIMFloaterShowedCallback(boost::bind(&LLConversationLogListItem::onIMFloaterShown, this, _1)); } } diff --git a/indra/newview/llconversationloglistitem.h b/indra/newview/llconversationloglistitem.h index 1bf7a0ed93..57f72db382 100644 --- a/indra/newview/llconversationloglistitem.h +++ b/indra/newview/llconversationloglistitem.h @@ -26,7 +26,7 @@ #ifndef LLCONVERSATIONLOGLISTITEM_H_ #define LLCONVERSATIONLOGLISTITEM_H_ -#include "llimfloater.h" +#include "llfloaterimsession.h" #include "llpanel.h" class LLTextBox; diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index ac5b2ad6ac..3495d74191 100755 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -32,10 +32,10 @@ #include #include "llagentdata.h" #include "llconversationmodel.h" -#include "llimfloater.h" -#include "llnearbychat.h" -#include "llimconversation.h" -#include "llimfloatercontainer.h" +#include "llfloaterimsession.h" +#include "llfloaterimnearbychat.h" +#include "llfloaterimsessiontab.h" +#include "llfloaterimcontainer.h" #include "llfloaterreg.h" #include "llgroupiconctrl.h" #include "lluictrlfactory.h" @@ -208,7 +208,7 @@ BOOL LLConversationViewSession::handleMouseDown( S32 x, S32 y, MASK mask ) LLConversationItem* item = dynamic_cast(getViewModelItem()); LLUUID session_id = item? item->getUUID() : LLUUID(); - (LLFloaterReg::getTypedInstance("im_container"))-> + (LLFloaterReg::getTypedInstance("im_container"))-> selectConversationPair(session_id, false); return LLFolderViewFolder::handleMouseDown(x, y, mask); @@ -262,7 +262,7 @@ void LLConversationViewSession::setVisibleIfDetached(BOOL visible) // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here LLFolderViewModelItem* item = mViewModelItem; LLUUID session_uuid = dynamic_cast(item)->getUUID(); - LLFloater* session_floater = LLIMConversation::getConversation(session_uuid); + LLFloater* session_floater = LLFloaterIMSessionTab::getConversation(session_uuid); if (session_floater && !session_floater->getHost() && !session_floater->isMinimized()) { @@ -505,7 +505,7 @@ BOOL LLConversationViewParticipant::handleMouseDown( S32 x, S32 y, MASK mask ) } LLUUID session_id = item? item->getUUID() : LLUUID(); - (LLFloaterReg::getTypedInstance("im_container"))-> + (LLFloaterReg::getTypedInstance("im_container"))-> selectConversationPair(session_id, false); return LLFolderViewItem::handleMouseDown(x, y, mask); diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h index 43547d155b..cc6995c207 100755 --- a/indra/newview/llconversationview.h +++ b/indra/newview/llconversationview.h @@ -34,7 +34,7 @@ #include "lloutputmonitorctrl.h" class LLTextBox; -class LLIMFloaterContainer; +class LLFloaterIMContainer; class LLConversationViewSession; class LLConversationViewParticipant; @@ -47,7 +47,7 @@ class LLConversationViewSession : public LLFolderViewFolder public: struct Params : public LLInitParam::Block { - Optional container; + Optional container; Params(); }; @@ -56,7 +56,7 @@ protected: friend class LLUICtrlFactory; LLConversationViewSession( const Params& p ); - LLIMFloaterContainer* mContainer; + LLFloaterIMContainer* mContainer; public: virtual ~LLConversationViewSession(); @@ -107,7 +107,7 @@ public: struct Params : public LLInitParam::Block { - Optional container; + Optional container; Optional participant_id; Optional avatar_icon; Optional info_button; diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp index a3825eafc8..c93181c0a1 100644 --- a/indra/newview/llfloaterconversationpreview.cpp +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -29,7 +29,7 @@ #include "llfloaterconversationpreview.h" #include "llimview.h" #include "lllineeditor.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llspinctrl.h" #include "lltrans.h" @@ -149,7 +149,7 @@ void LLFloaterConversationPreview::showHistory() } else if (from_id.isNull()) { - chat.mSourceType = LLNearbyChat::isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; + chat.mSourceType = LLFloaterIMNearbyChat::isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; } mChatHistory->appendMessage(chat); diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp new file mode 100644 index 0000000000..b20d19d0fd --- /dev/null +++ b/indra/newview/llfloaterimcontainer.cpp @@ -0,0 +1,1557 @@ +/** + * @file llfloaterimcontainer.cpp + * @brief Multifloater containing active IM sessions in separate tab container tabs + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" + +#include "llfloaterimsession.h" +#include "llfloaterimcontainer.h" + +#include "llfloaterreg.h" +#include "lllayoutstack.h" +#include "llfloaterimnearbychat.h" + +#include "llagent.h" +#include "llavataractions.h" +#include "llavatariconctrl.h" +#include "llavatarnamecache.h" +#include "llcallbacklist.h" +#include "llgroupactions.h" +#include "llgroupiconctrl.h" +#include "llfloateravatarpicker.h" +#include "llfloaterpreference.h" +#include "llimview.h" +#include "llnotificationsutil.h" +#include "lltransientfloatermgr.h" +#include "llviewercontrol.h" +#include "llconversationview.h" +#include "llcallbacklist.h" +#include "llworld.h" + +#include "llsdserialize.h" +// +// LLFloaterIMContainer +// +LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed) +: LLMultiFloater(seed), + mExpandCollapseBtn(NULL), + mConversationsRoot(NULL), + mConversationsEventStream("ConversationsEvents"), + mInitialized(false) +{ + mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2)); + mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2)); + + mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLFloaterIMContainer::checkContextMenuItem, this, _2)); + mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMContainer::enableContextMenuItem, this, _2)); + mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMContainer::doToSelected, this, _2)); + + mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&LLFloaterIMContainer::doToSelectedGroup, this, _2)); + + // Firstly add our self to IMSession observers, so we catch session events + LLIMMgr::getInstance()->addSessionObserver(this); + + mAutoResize = FALSE; + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); +} + +LLFloaterIMContainer::~LLFloaterIMContainer() +{ + mConversationsEventStream.stopListening("ConversationsRefresh"); + + gIdleCallbacks.deleteFunction(idle, this); + + mNewMessageConnection.disconnect(); + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); + + gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed()); + gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed()); + + if (!LLSingleton::destroyed()) + { + LLIMMgr::getInstance()->removeSessionObserver(this); + } +} + +void LLFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + addConversationListItem(session_id); + LLFloaterIMSessionTab::addToHost(session_id); +} + +void LLFloaterIMContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + selectConversation(session_id); +} + +void LLFloaterIMContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) +{ + addConversationListItem(session_id); + LLFloaterIMSessionTab::addToHost(session_id); +} + +void LLFloaterIMContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) +{ + // *TODO: We should do this *without* delete and recreate + addConversationListItem(new_session_id, removeConversationListItem(old_session_id)); +} + +void LLFloaterIMContainer::sessionRemoved(const LLUUID& session_id) +{ + removeConversationListItem(session_id); +} + +// static +void LLFloaterIMContainer::onCurrentChannelChanged(const LLUUID& session_id) +{ + if (session_id != LLUUID::null) + { + LLFloaterIMContainer::getInstance()->showConversation(session_id); + } +} + + +BOOL LLFloaterIMContainer::postBuild() +{ + mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLFloaterIMContainer::onNewMessageReceived, this, _1)); + // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button + // mTabContainer will be initialized in LLMultiFloater::addChild() + + setTabContainer(getChild("im_box_tab_container")); + + mConversationsStack = getChild("conversations_stack"); + mConversationsPane = getChild("conversations_layout_panel"); + mMessagesPane = getChild("messages_layout_panel"); + + mConversationsListPanel = getChild("conversations_list_panel"); + + // Open IM session with selected participant on double click event + mConversationsListPanel->setDoubleClickCallback(boost::bind(&LLFloaterIMContainer::doToSelected, this, LLSD("im"))); + + // Create the root model and view for all conversation sessions + LLConversationItem* base_item = new LLConversationItem(getRootViewModel()); + + LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); + p.name = getName(); + p.title = getLabel(); + p.rect = LLRect(0, 0, getRect().getWidth(), 0); + p.parent_panel = mConversationsListPanel; + p.tool_tip = p.name; + p.listener = base_item; + p.view_model = &mConversationViewModel; + p.root = NULL; + p.use_ellipses = true; + p.options_menu = "menu_conversation.xml"; + mConversationsRoot = LLUICtrlFactory::create(p); + mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); + + // Add listener to conversation model events + mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLFloaterIMContainer::onConversationModelEvent, this, _1)); + + // a scroller for folder view + LLRect scroller_view_rect = mConversationsListPanel->getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams()); + scroller_params.rect(scroller_view_rect); + + LLScrollContainer* scroller = LLUICtrlFactory::create(scroller_params); + scroller->setFollowsAll(); + mConversationsListPanel->addChild(scroller); + scroller->addChild(mConversationsRoot); + mConversationsRoot->setScrollContainer(scroller); + mConversationsRoot->setFollowsAll(); + mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox); + + addConversationListItem(LLUUID()); // manually add nearby chat + + mExpandCollapseBtn = getChild("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this)); + + childSetAction("add_btn", boost::bind(&LLFloaterIMContainer::onAddButtonClicked, this)); + + collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); + collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed")); + LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate)); + + if (! mMessagesPane->isCollapsed()) + { + S32 list_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth"); + LLRect list_size = mConversationsPane->getRect(); + S32 left_pad = mConversationsListPanel->getRect().mLeft; + list_size.mRight = list_size.mLeft + list_width - left_pad; + + mConversationsPane->handleReshape(list_size, TRUE); + } + + // Init the sort order now that the root had been created + setSortOrder(LLConversationSort(gSavedSettings.getU32("ConversationSortOrder"))); + + mInitialized = true; + + // Add callbacks: + // We'll take care of view updates on idle + gIdleCallbacks.addFunction(idle, this); + // When display name option change, we need to reload all participant names + LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLFloaterIMContainer::processParticipantsStyleUpdate, this)); + + return TRUE; +} + +void LLFloaterIMContainer::onOpen(const LLSD& key) +{ + LLMultiFloater::onOpen(key); + openNearbyChat(); +} + +// virtual +void LLFloaterIMContainer::addFloater(LLFloater* floaterp, + BOOL select_added_floater, + LLTabContainer::eInsertionPoint insertion_point) +{ + if(!floaterp) return; + + // already here + if (floaterp->getHost() == this) + { + openFloater(floaterp->getKey()); + return; + } + + // Make sure the message panel is open when adding a floater or it stays mysteriously hidden + collapseMessagesPane(false); + + // Add the floater + LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); + + LLUUID session_id = floaterp->getKey(); + + LLIconCtrl* icon = 0; + + if(gAgent.isInGroup(session_id, TRUE)) + { + LLGroupIconCtrl::Params icon_params; + icon_params.group_id = session_id; + icon = LLUICtrlFactory::instance().create(icon_params); + + mSessions[session_id] = floaterp; + floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id)); + } + else + { LLUUID avatar_id = session_id.notNull()? + LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID(); + + LLAvatarIconCtrl::Params icon_params; + icon_params.avatar_id = avatar_id; + icon = LLUICtrlFactory::instance().create(icon_params); + + mSessions[session_id] = floaterp; + floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id)); + } + + // forced resize of the floater + LLRect wrapper_rect = this->mTabContainer->getLocalRect(); + floaterp->setRect(wrapper_rect); + + mTabContainer->setTabImage(floaterp, icon); +} + + +void LLFloaterIMContainer::onCloseFloater(LLUUID& id) +{ + mSessions.erase(id); + setFocus(TRUE); +} + +// virtual +void LLFloaterIMContainer::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + // possibly increase floater's minimum height according to children's minimums + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (floaterp) + { + new_min_height = llmax(new_min_height, floaterp->getMinHeight()); + } + } + + S32 conversations_pane_min_dim = mConversationsPane->getRelevantMinDim(); + S32 messages_pane_min_dim = mMessagesPane->getRelevantMinDim(); + + // set floater's minimum width according to relevant minimal children's dimensionals + new_min_width = conversations_pane_min_dim + messages_pane_min_dim + LLPANEL_BORDER_WIDTH*2; +} + +void LLFloaterIMContainer::onNewMessageReceived(const LLSD& data) +{ + LLUUID session_id = data["session_id"].asUUID(); + LLFloater* floaterp = get_ptr_in_map(mSessions, session_id); + LLFloater* current_floater = LLMultiFloater::getActiveFloater(); + + if(floaterp && current_floater && floaterp != current_floater) + { + if(LLMultiFloater::isFloaterFlashing(floaterp)) + LLMultiFloater::setFloaterFlashing(floaterp, FALSE); + LLMultiFloater::setFloaterFlashing(floaterp, TRUE); + } +} + +void LLFloaterIMContainer::onExpandCollapseButtonClicked() +{ + if (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed() + && gSavedPerAccountSettings.getBOOL("ConversationsExpandMessagePaneFirst")) + { + // Expand the messages pane from ultra minimized state + // if it was collapsed last in order. + collapseMessagesPane(false); + } + else + { + collapseConversationsPane(!mConversationsPane->isCollapsed()); + } + selectConversation(mSelectedSession); +} + +LLFloaterIMContainer* LLFloaterIMContainer::findInstance() +{ + return LLFloaterReg::findTypedInstance("im_container"); +} + +LLFloaterIMContainer* LLFloaterIMContainer::getInstance() +{ + return LLFloaterReg::getTypedInstance("im_container"); +} + +// Update all participants in the conversation lists +void LLFloaterIMContainer::processParticipantsStyleUpdate() +{ + // On each session in mConversationsItems + for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) + { + // Get the current session descriptors + LLConversationItem* session_model = it_session->second; + // Iterate through each model participant child + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = session_model->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); + // Get the avatar name for this participant id from the cache and update the model + participant_model->fetchAvatarName(); + // Next participant + current_participant_model++; + } + } +} + +// static +void LLFloaterIMContainer::idle(void* user_data) +{ + LLFloaterIMContainer* self = static_cast(user_data); + + // Update the distance to agent in the nearby chat session if required + // Note: it makes no sense of course to update the distance in other session + if (self->mConversationViewModel.getSorter().getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE) + { + self->setNearbyDistances(); + } + self->mConversationsRoot->update(); +} + +bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event) +{ + // For debug only + //std::ostringstream llsd_value; + //llsd_value << LLSDOStreamer(event) << std::endl; + //llinfos << "LLFloaterIMContainer::onConversationModelEvent, event = " << llsd_value.str() << llendl; + // end debug + + // Note: In conversations, the model is not responsible for creating the view, which is a good thing. This means that + // the model could change substantially and the view could echo only a portion of this model (though currently the + // conversation view does echo the conversation model 1 to 1). + // Consequently, the participant views need to be created either by the session view or by the container panel. + // For the moment, we create them here, at the container level, to conform to the pattern implemented in llinventorypanel.cpp + // (see LLInventoryPanel::buildNewViews()). + + std::string type = event.get("type").asString(); + LLUUID session_id = event.get("session_uuid").asUUID(); + LLUUID participant_id = event.get("participant_uuid").asUUID(); + + LLConversationViewSession* session_view = dynamic_cast(get_ptr_in_map(mConversationsWidgets,session_id)); + if (!session_view) + { + // We skip events that are not associated with a session + return false; + } + LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id); + LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id))); + + if (type == "remove_participant") + { + // Remove a participant view from the hierarchical conversation list + if (participant_view) + { + session_view->extractItem(participant_view); + delete participant_view; + session_view->refresh(); + mConversationsRoot->arrangeAll(); + } + // Remove a participant view from the conversation floater + if (conversation_floater) + { + conversation_floater->removeConversationViewParticipant(participant_id); + } + } + else if (type == "add_participant") + { + LLConversationItemSession* session_model = dynamic_cast(mConversationsItems[session_id]); + LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL); + if (!participant_view && session_model && participant_model) + { + LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id); + if (session_id.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType())) + { + participant_view = createConversationViewParticipant(participant_model); + participant_view->addToFolder(session_view); + participant_view->setVisible(TRUE); + } + } + // Add a participant view to the conversation floater + if (conversation_floater && participant_model) + { + conversation_floater->addConversationViewParticipant(participant_model); + } + } + else if (type == "update_participant") + { + // Update the participant view in the hierarchical conversation list + if (participant_view) + { + participant_view->refresh(); + } + // Update the participant view in the conversation floater + if (conversation_floater) + { + conversation_floater->updateConversationViewParticipant(participant_id); + } + } + else if (type == "update_session") + { + session_view->refresh(); + if (conversation_floater) + { + conversation_floater->refreshConversation(); + } + } + + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + + return false; +} + +void LLFloaterIMContainer::draw() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } + LLFloater::draw(); +} + +void LLFloaterIMContainer::tabClose() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } +} + +void LLFloaterIMContainer::setVisible(BOOL visible) +{ LLFloaterIMNearbyChat* nearby_chat; + if (visible) + { + // Make sure we have the Nearby Chat present when showing the conversation container + nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (nearby_chat == NULL) + { + // If not found, force the creation of the nearby chat conversation panel + // *TODO: find a way to move this to XML as a default panel or something like that + LLSD name("nearby_chat"); + LLFloaterReg::toggleInstanceOrBringToFront(name); + } + openNearbyChat(); + } + + nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (nearby_chat) + { + LLFloaterIMSessionTab::addToHost(LLUUID()); + } + + // We need to show/hide all the associated conversations that have been torn off + // (and therefore, are not longer managed by the multifloater), + // so that they show/hide with the conversations manager. + conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + for (;widget_it != mConversationsWidgets.end(); ++widget_it) + { + LLConversationViewSession* widget = dynamic_cast(widget_it->second); + if (widget) + { + widget->setVisibleIfDetached(visible); + } + } + + // Now, do the normal multifloater show/hide + LLMultiFloater::setVisible(visible); + +} + +void LLFloaterIMContainer::collapseMessagesPane(bool collapse) +{ + if (mMessagesPane->isCollapsed() == collapse) + { + return; + } + + if (collapse) + { + // Save the messages pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", mMessagesPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed()); + } + + // Save left pane rectangle before collapsing/expanding right pane. + LLRect prevRect = mConversationsPane->getRect(); + + // Show/hide the messages pane. + mConversationsStack->collapsePanel(mMessagesPane, collapse); + + if (!collapse) + { + // Make sure layout is updated before resizing conversation pane. + mConversationsStack->updateLayout(); + } + + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth")); + if (!collapse) + { + // Restore conversation's pane previous width after expanding messages pane. + mConversationsPane->setTargetDim(prevRect.getWidth()); + } +} +void LLFloaterIMContainer::collapseConversationsPane(bool collapse) +{ + if (mConversationsPane->isCollapsed() == collapse) + { + return; + } + + LLView* button_panel = getChild("conversations_pane_buttons_expanded"); + button_panel->setVisible(!collapse); + mExpandCollapseBtn->setImageOverlay(getString(collapse ? "expand_icon" : "collapse_icon")); + + if (collapse) + { + // Save the conversations pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", mConversationsPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed()); + } + + mConversationsStack->collapsePanel(mConversationsPane, collapse); + + S32 collapsed_width = mConversationsPane->getMinDim(); + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - collapsed_width); + + for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + widget_it != mConversationsWidgets.end(); ++widget_it) + { + LLConversationViewSession* widget = dynamic_cast(widget_it->second); + if (widget) + { + widget->toggleMinimizedMode(collapse); + + // force closing all open conversations when collapsing to minimized state + if (collapse) + { + widget->setOpen(false); + } +} + } +} + +void LLFloaterIMContainer::updateState(bool collapse, S32 delta_width) +{ + LLRect floater_rect = getRect(); + floater_rect.mRight += ((collapse ? -1 : 1) * delta_width); + + // Set by_user = true so that reshaped rect is saved in user_settings. + setShape(floater_rect, true); + + updateResizeLimits(); + + bool is_left_pane_expanded = !mConversationsPane->isCollapsed(); + bool is_right_pane_expanded = !mMessagesPane->isCollapsed(); + + setCanResize(is_left_pane_expanded || is_right_pane_expanded); + setCanMinimize(is_left_pane_expanded || is_right_pane_expanded); + + // force set correct size for the title after show/hide minimize button + LLRect cur_rect = getRect(); + LLRect force_rect = cur_rect; + force_rect.mRight = cur_rect.mRight + 1; + setRect(force_rect); + setRect(cur_rect); + + // restore floater's resize limits (prevent collapse when left panel is expanded) + if (is_left_pane_expanded && !is_right_pane_expanded) + { + S32 expanded_min_size = mConversationsPane->getExpandedMinDim(); + setResizeLimits(expanded_min_size, expanded_min_size); + } + +} + +void LLFloaterIMContainer::onAddButtonClicked() +{ + LLView * button = findChild("conversations_pane_buttons_expanded")->findChild("add_btn"); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterIMContainer::onAvatarPicked, this, _1), TRUE, TRUE, TRUE, root_floater->getName(), button); + + if (picker && root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +void LLFloaterIMContainer::onAvatarPicked(const uuid_vec_t& ids) +{ + if (ids.size() == 1) + { + LLAvatarActions::startIM(ids.back()); + } + else + { + LLAvatarActions::startConference(ids); + } +} + +void LLFloaterIMContainer::onCustomAction(const LLSD& userdata) +{ + std::string command = userdata.asString(); + + if ("sort_sessions_by_type" == command) + { + setSortOrderSessions(LLConversationFilter::SO_SESSION_TYPE); + } + if ("sort_sessions_by_name" == command) + { + setSortOrderSessions(LLConversationFilter::SO_NAME); + } + if ("sort_sessions_by_recent" == command) + { + setSortOrderSessions(LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_name" == command) + { + setSortOrderParticipants(LLConversationFilter::SO_NAME); + } + if ("sort_participants_by_recent" == command) + { + setSortOrderParticipants(LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_distance" == command) + { + setSortOrderParticipants(LLConversationFilter::SO_DISTANCE); + } + if ("chat_preferences" == command) + { + LLFloaterPreference* floater_prefs = LLFloaterReg::showTypedInstance("preferences"); + if (floater_prefs) + { + LLTabContainer* tab_container = floater_prefs->getChild("pref core"); + LLPanel* chat_panel = tab_container->getPanelByName("chat"); + if (tab_container && chat_panel) + { + tab_container->selectTabPanel(chat_panel); + } + } + } +} + +BOOL LLFloaterIMContainer::isActionChecked(const LLSD& userdata) +{ + LLConversationSort order = mConversationViewModel.getSorter(); + std::string command = userdata.asString(); + if ("sort_sessions_by_type" == command) + { + return (order.getSortOrderSessions() == LLConversationFilter::SO_SESSION_TYPE); + } + if ("sort_sessions_by_name" == command) + { + return (order.getSortOrderSessions() == LLConversationFilter::SO_NAME); + } + if ("sort_sessions_by_recent" == command) + { + return (order.getSortOrderSessions() == LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_name" == command) + { + return (order.getSortOrderParticipants() == LLConversationFilter::SO_NAME); + } + if ("sort_participants_by_recent" == command) + { + return (order.getSortOrderParticipants() == LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_distance" == command) + { + return (order.getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE); + } + + return FALSE; +} + +void LLFloaterIMContainer::setSortOrderSessions(const LLConversationFilter::ESortOrderType order) +{ + LLConversationSort old_order = mConversationViewModel.getSorter(); + if (order != old_order.getSortOrderSessions()) + { + old_order.setSortOrderSessions(order); + setSortOrder(old_order); + } +} + +void LLFloaterIMContainer::setSortOrderParticipants(const LLConversationFilter::ESortOrderType order) +{ + LLConversationSort old_order = mConversationViewModel.getSorter(); + if (order != old_order.getSortOrderParticipants()) + { + old_order.setSortOrderParticipants(order); + setSortOrder(old_order); + } +} + +void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order) +{ + mConversationViewModel.setSorter(order); + mConversationsRoot->arrangeAll(); + // try to keep selection onscreen, even if it wasn't to start with + mConversationsRoot->scrollToShowSelection(); + + // Notify all conversation (torn off or not) of the change to the sort order + // Note: For the moment, the sort order is *unique* across all conversations. That might change in the future. + for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) + { + LLUUID session_id = it_session->first; + LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id))); + if (conversation_floater) + { + conversation_floater->setSortOrder(order); + } + } + + gSavedSettings.setU32("ConversationSortOrder", (U32)order); +} + +void LLFloaterIMContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids) +{ + const std::set selectedItems = mConversationsRoot->getSelectionList(); + + std::set::const_iterator it = selectedItems.begin(); + const std::set::const_iterator it_end = selectedItems.end(); + LLConversationItem * conversationItem; + + for (; it != it_end; ++it) + { + conversationItem = static_cast((*it)->getViewModelItem()); + selected_uuids.push_back(conversationItem->getUUID()); + } +} + +const LLConversationItem * LLFloaterIMContainer::getCurSelectedViewModelItem() +{ + LLConversationItem * conversationItem = NULL; + + if(mConversationsRoot && + mConversationsRoot->getCurSelectedItem() && + mConversationsRoot->getCurSelectedItem()->getViewModelItem()) + { + conversationItem = static_cast(mConversationsRoot->getCurSelectedItem()->getViewModelItem()); + } + + return conversationItem; +} + +void LLFloaterIMContainer::getParticipantUUIDs(uuid_vec_t& selected_uuids) +{ + //Find the conversation floater associated with the selected id + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + + if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) + { + getSelectedUUIDs(selected_uuids); + } + //When a one-on-one conversation exists, retrieve the participant id from the conversation floater + else if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1) + { + LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(conversationItem->getUUID()); + LLUUID participantID = conversationFloater->getOtherParticipantUUID(); + selected_uuids.push_back(participantID); + } +} + +void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec_t& selectedIDS) +{ + if(selectedIDS.size() > 0) + { + const LLUUID& userID = selectedIDS.front(); + if(gAgent.getID() != userID) + { + if ("view_profile" == command) + { + LLAvatarActions::showProfile(userID); + } + else if("im" == command) + { + LLAvatarActions::startIM(userID); + } + else if("offer_teleport" == command) + { + LLAvatarActions::offerTeleport(selectedIDS); + } + else if("voice_call" == command) + { + LLAvatarActions::startCall(userID); + } + else if("chat_history" == command) + { + LLAvatarActions::viewChatHistory(userID); + } + else if("add_friend" == command) + { + LLAvatarActions::requestFriendshipDialog(userID); + } + else if("remove_friend" == command) + { + LLAvatarActions::removeFriendDialog(userID); + } + else if("invite_to_group" == command) + { + LLAvatarActions::inviteToGroup(userID); + } + else if("map" == command) + { + LLAvatarActions::showOnMap(userID); + } + else if("share" == command) + { + LLAvatarActions::share(userID); + } + else if("pay" == command) + { + LLAvatarActions::pay(userID); + } + else if("block_unblock" == command) + { + LLAvatarActions::toggleBlock(userID); + } + else if("selected" == command || "mute_all" == command || "unmute_all" == command) + { + moderateVoice(command, userID); + } + else if ("toggle_allow_text_chat" == command) + { + toggleAllowTextChat(userID); + } + } + } +} + +void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS) +{ + //Find the conversation floater associated with the selected id + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(conversationItem->getUUID()); + + if(conversationFloater) + { + //Close the selected conversation + if("close_conversation" == command) + { + LLFloater::onClickClose(conversationFloater); + } + else if("open_voice_conversation" == command) + { + gIMMgr->startCall(conversationItem->getUUID()); + } + else if("disconnect_from_voice" == command) + { + gIMMgr->endCall(conversationItem->getUUID()); + } + else if("chat_history" == command) + { + const LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(conversationItem->getUUID()); + + if (NULL != session) + { + const LLUUID session_id = session->isOutgoingAdHoc() ? session->generateOutgouigAdHocHash() : session->mSessionID; + LLFloaterReg::showInstance("preview_conversation", session_id, true); + } + } + else + { + doToParticipants(command, selectedIDS); + } + } +} + +void LLFloaterIMContainer::doToSelected(const LLSD& userdata) +{ + std::string command = userdata.asString(); + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + uuid_vec_t selected_uuids; + + if(conversationItem != NULL) + { + getParticipantUUIDs(selected_uuids); + + if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) + { + doToParticipants(command, selected_uuids); + } + else + { + doToSelectedConversation(command, selected_uuids); + } + } +} + +void LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata) +{ + std::string action = userdata.asString(); + LLUUID selected_group = getCurSelectedViewModelItem()->getUUID(); + + if (action == "group_profile") + { + LLGroupActions::show(selected_group); + } + else if (action == "activate_group") + { + LLGroupActions::activate(selected_group); + } + else if (action == "leave_group") + { + LLGroupActions::leave(selected_group); + } +} + +bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + uuid_vec_t uuids; + getParticipantUUIDs(uuids); + + if(item == std::string("can_activate_group")) + { + LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID(); + return gAgent.getGroupID() != selected_group_id; + } + + if(uuids.size() <= 0) + { + return false; + } + + // Note: can_block and can_delete is used only for one person selected menu + // so we don't need to go over all uuids. + + if (item == std::string("can_block")) + { + const LLUUID& id = uuids.front(); + return LLAvatarActions::canBlock(id); + } + else if (item == std::string("can_add")) + { + // We can add friends if: + // - there are selected people + // - and there are no friends among selection yet. + + //EXT-7389 - disable for more than 1 + if(uuids.size() > 1) + { + return false; + } + + bool result = true; + + uuid_vec_t::const_iterator + id = uuids.begin(), + uuids_end = uuids.end(); + + for (;id != uuids_end; ++id) + { + if ( LLAvatarActions::isFriend(*id) ) + { + result = false; + break; + } + } + + return result; + } + else if (item == std::string("can_delete")) + { + // We can remove friends if: + // - there are selected people + // - and there are only friends among selection. + + bool result = (uuids.size() > 0); + + uuid_vec_t::const_iterator + id = uuids.begin(), + uuids_end = uuids.end(); + + for (;id != uuids_end; ++id) + { + if ( !LLAvatarActions::isFriend(*id) ) + { + result = false; + break; + } + } + + return result; + } + else if (item == std::string("can_call")) + { + return LLAvatarActions::canCall(); + } + else if (item == std::string("can_show_on_map")) + { + const LLUUID& id = uuids.front(); + + return (LLAvatarTracker::instance().isBuddyOnline(id) && is_agent_mappable(id)) + || gAgent.isGodlike(); + } + else if(item == std::string("can_offer_teleport")) + { + return LLAvatarActions::canOfferTeleport(uuids); + } + else if("can_moderate_voice" == item || "can_allow_text_chat" == item || "can_mute" == item || "can_unmute" == item) + { + return enableModerateContextMenuItem(item); + } + + return false; +} + +bool LLFloaterIMContainer::checkContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + uuid_vec_t mUUIDs; + getParticipantUUIDs(mUUIDs); + + if(mUUIDs.size() > 0 ) + { + if ("is_blocked" == item) + { + return LLAvatarActions::isBlocked(mUUIDs.front()); + } + else if ("is_allowed_text_chat" == item) + { + const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); + + if (NULL != speakerp) + { + return !speakerp->mModeratorMutedText; + } + } + } + + return false; +} + +void LLFloaterIMContainer::showConversation(const LLUUID& session_id) +{ + setVisibleAndFrontmost(false); + selectConversation(session_id); +} + +// Will select only the conversation item +void LLFloaterIMContainer::selectConversation(const LLUUID& session_id) +{ + LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,session_id); + if (widget) + { + (widget->getRoot())->setSelection(widget, FALSE, FALSE); + } +} + +// Synchronous select the conversation item and the conversation floater +BOOL LLFloaterIMContainer::selectConversationPair(const LLUUID& session_id, bool select_widget) +{ + BOOL handled = TRUE; + + /* widget processing */ + if (select_widget) + { + LLFolderViewItem* widget = mConversationsWidgets[session_id]; + if (widget && widget->getParentFolder()) + { + widget->getParentFolder()->setSelection(widget, FALSE, FALSE); + } + } + + /* floater processing */ + + if (session_id != getSelectedSession()) + { + // Store the active session + setSelectedSession(session_id); + + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id); + + if (session_floater->getHost()) + { + // Always expand the message pane if the panel is hosted by the container + collapseMessagesPane(false); + // Switch to the conversation floater that is being selected + selectFloater(session_floater); + } + + // Set the focus on the selected floater + if (!session_floater->hasFocus()) + { + session_floater->setFocus(TRUE); + } + } + + return handled; +} + +void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) +{ + LLConversationItemSession* item = dynamic_cast(get_ptr_in_map(mConversationsItems,session_id)); + if (item) + { + item->setTimeNow(participant_id); + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + } +} + +void LLFloaterIMContainer::setNearbyDistances() +{ + // Get the nearby chat session: that's the one with uuid nul + LLConversationItemSession* item = dynamic_cast(get_ptr_in_map(mConversationsItems,LLUUID())); + if (item) + { + // Get the positions of the nearby avatars and their ids + std::vector positions; + uuid_vec_t avatar_ids; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); + // Get the position of the agent + const LLVector3d& me_pos = gAgent.getPositionGlobal(); + // For each nearby avatar, compute and update the distance + int avatar_count = positions.size(); + for (int i = 0; i < avatar_count; i++) + { + F64 dist = dist_vec_squared(positions[i], me_pos); + item->setDistance(avatar_ids[i],dist); + } + // Also does it for the agent itself + item->setDistance(gAgent.getID(),0.0f); + // Request resort + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + } +} + +LLConversationItem* LLFloaterIMContainer::addConversationListItem(const LLUUID& uuid, bool isWidgetSelected /*= false*/) +{ + bool is_nearby_chat = uuid.isNull(); + + // Stores the display name for the conversation line item + std::string display_name = is_nearby_chat ? LLTrans::getString("NearbyChatLabel") : LLIMModel::instance().getName(uuid); + + // Check if the item is not already in the list, exit (nothing to do) + // Note: this happens often, when reattaching a torn off conversation for instance + conversations_items_map::iterator item_it = mConversationsItems.find(uuid); + if (item_it != mConversationsItems.end()) + { + return item_it->second; + } + + // Remove the conversation item that might exist already: it'll be recreated anew further down anyway + // and nothing wrong will happen removing it if it doesn't exist + removeConversationListItem(uuid,false); + + // Create a conversation session model + LLConversationItemSession* item = NULL; + LLSpeakerMgr* speaker_manager = (is_nearby_chat ? (LLSpeakerMgr*)(LLLocalSpeakerMgr::getInstance()) : LLIMModel::getInstance()->getSpeakerManager(uuid)); + if (speaker_manager) + { + item = new LLParticipantList(speaker_manager, getRootViewModel()); + } + if (!item) + { + llwarns << "Couldn't create conversation session item : " << display_name << llendl; + return NULL; + } + item->renameItem(display_name); + item->updateParticipantName(NULL); + + mConversationsItems[uuid] = item; + + // Create a widget from it + LLConversationViewSession* widget = createConversationItemWidget(item); + mConversationsWidgets[uuid] = widget; + + // Add a new conversation widget to the root folder of the folder view + widget->addToFolder(mConversationsRoot); + widget->requestArrange(); + + LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(uuid); + + // Create the participants widgets now + // Note: usually, we do not get an updated avatar list at that point + if (uuid.isNull() || im_sessionp && !im_sessionp->isP2PSessionType()) + { + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItem* participant_model = dynamic_cast(*current_participant_model); + LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); + participant_view->addToFolder(widget); + current_participant_model++; + } + } + // Do that too for the conversation dialog + LLFloaterIMSessionTab *conversation_floater = (uuid.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(uuid))); + if (conversation_floater) + { + conversation_floater->buildConversationViewParticipant(); + } + + // set the widget to minimized mode if conversations pane is collapsed + widget->toggleMinimizedMode(mConversationsPane->isCollapsed()); + + if (isWidgetSelected) + { + selectConversation(uuid); + // scroll to newly added item + mConversationsRoot->scrollToShowSelection(); + } + + return item; +} + +bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool change_focus) +{ + // Delete the widget and the associated conversation item + // Note : since the mConversationsItems is also the listener to the widget, deleting + // the widget will also delete its listener + bool isWidgetSelected = false; + LLFolderViewItem* new_selection = NULL; + LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid); + if (widget) + { + isWidgetSelected = widget->isSelected(); + new_selection = mConversationsRoot->getNextFromChild(widget); + if(new_selection == NULL) + { + new_selection = mConversationsRoot->getPreviousFromChild(widget); + } + widget->destroyView(); + } + + // Suppress the conversation items and widgets from their respective maps + mConversationsItems.erase(uuid); + mConversationsWidgets.erase(uuid); + + // Don't let the focus fall IW, select and refocus on the first conversation in the list + if (change_focus) + { + setFocus(TRUE); + if(new_selection != NULL) + { + LLConversationItem* vmi = dynamic_cast(new_selection->getViewModelItem()); + if(vmi != NULL) + { + selectConversation(vmi->getUUID()); + } + } + } + return isWidgetSelected; +} + +LLConversationViewSession* LLFloaterIMContainer::createConversationItemWidget(LLConversationItem* item) +{ + LLConversationViewSession::Params params; + + params.name = item->getDisplayName(); + params.root = mConversationsRoot; + params.listener = item; + params.tool_tip = params.name; + params.container = this; + + return LLUICtrlFactory::create(params); +} + +LLConversationViewParticipant* LLFloaterIMContainer::createConversationViewParticipant(LLConversationItem* item) +{ + LLConversationViewParticipant::Params params; + LLRect panel_rect = mConversationsListPanel->getRect(); + + params.name = item->getDisplayName(); + params.root = mConversationsRoot; + params.listener = item; + + //24 is the the current hight of an item (itemHeight) loaded from conversation_view_participant.xml. + params.rect = LLRect (0, 24, panel_rect.getWidth(), 0); + params.tool_tip = params.name; + params.participant_id = item->getUUID(); + params.folder_indentation = 42; + + return LLUICtrlFactory::create(params); +} + +bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& userdata) +{ + // only group moderators can perform actions related to this "enable callback" + if (!isGroupModerator()) + { + return false; + } + + LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); + if (NULL == speakerp) + { + return false; + } + + bool voice_channel = speakerp->isInVoiceChannel(); + + if ("can_moderate_voice" == userdata) + { + return voice_channel; + } + else if ("can_mute" == userdata) + { + return voice_channel && !isMuted(getCurSelectedViewModelItem()->getUUID()); + } + else if ("can_unmute" == userdata) + { + return voice_channel && isMuted(getCurSelectedViewModelItem()->getUUID()); + } + + // The last invoke is used to check whether the "can_allow_text_chat" will enabled + return LLVoiceClient::getInstance()->isParticipantAvatar(getCurSelectedViewModelItem()->getUUID()); +} + +bool LLFloaterIMContainer::isGroupModerator() +{ + LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant(); + if (NULL == speaker_manager) + { + llwarns << "Speaker manager is missing" << llendl; + return false; + } + + // Is session a group call/chat? + if(gAgent.isInGroup(speaker_manager->getSessionID())) + { + LLSpeaker * speaker = speaker_manager->findSpeaker(gAgentID).get(); + + // Is agent a moderator? + return speaker && speaker->mIsModerator; + } + + return false; +} + +void LLFloaterIMContainer::moderateVoice(const std::string& command, const LLUUID& userID) +{ + if (!gAgent.getRegion()) return; + + if (command.compare("selected")) + { + moderateVoiceAllParticipants(command.compare("mute_all")); + } + else + { + moderateVoiceParticipant(userID, isMuted(userID)); + } +} + +bool LLFloaterIMContainer::isMuted(const LLUUID& avatar_id) +{ + const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); + return NULL == speakerp ? true : speakerp->mStatus == LLSpeaker::STATUS_MUTED; +} + +void LLFloaterIMContainer::moderateVoiceAllParticipants(bool unmute) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); + + if (NULL != speaker_managerp) + { + if (!unmute) + { + LLSD payload; + payload["session_id"] = speaker_managerp->getSessionID(); + LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback); + return; + } + + speaker_managerp->moderateVoiceAllParticipants(unmute); + } +} + +// static +void LLFloaterIMContainer::confirmMuteAllCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + // if Cancel pressed + if (option == 1) + { + return; + } + + const LLSD& payload = notification["payload"]; + const LLUUID& session_id = payload["session_id"]; + + LLIMSpeakerMgr * speaker_manager = dynamic_cast ( + LLIMModel::getInstance()->getSpeakerManager(session_id)); + if (speaker_manager) + { + speaker_manager->moderateVoiceAllParticipants(false); + } + + return; +} + +void LLFloaterIMContainer::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); + + if (NULL != speaker_managerp) + { + speaker_managerp->moderateVoiceParticipant(avatar_id, unmute); + } +} + +LLSpeakerMgr * LLFloaterIMContainer::getSpeakerMgrForSelectedParticipant() +{ + LLFolderViewItem * selected_folder_itemp = mConversationsRoot->getCurSelectedItem(); + if (NULL == selected_folder_itemp) + { + llwarns << "Current selected item is null" << llendl; + return NULL; + } + + LLFolderViewFolder * conversation_itemp = selected_folder_itemp->getParentFolder(); + + conversations_widgets_map::const_iterator iter = mConversationsWidgets.begin(); + conversations_widgets_map::const_iterator end = mConversationsWidgets.end(); + const LLUUID * conversation_uuidp = NULL; + while(iter != end) + { + if (iter->second == conversation_itemp) + { + conversation_uuidp = &iter->first; + break; + } + ++iter; + } + if (NULL == conversation_uuidp) + { + llwarns << "Cannot find conversation item widget" << llendl; + return NULL; + } + + return conversation_uuidp->isNull() ? (LLSpeakerMgr *)LLLocalSpeakerMgr::getInstance() + : LLIMModel::getInstance()->getSpeakerManager(*conversation_uuidp); +} + +LLSpeaker * LLFloaterIMContainer::getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp) +{ + if (NULL == speaker_managerp) + { + llwarns << "Speaker manager is missing" << llendl; + return NULL; + } + + const LLConversationItem * participant_itemp = getCurSelectedViewModelItem(); + if (NULL == participant_itemp) + { + llwarns << "Cannot evaluate current selected view model item" << llendl; + return NULL; + } + + return speaker_managerp->findSpeaker(participant_itemp->getUUID()); +} + +void LLFloaterIMContainer::toggleAllowTextChat(const LLUUID& participant_uuid) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); + if (NULL != speaker_managerp) + { + speaker_managerp->toggleAllowTextChat(participant_uuid); + } +} + +void LLFloaterIMContainer::openNearbyChat() +{ + // If there's only one conversation in the container and that conversation is the nearby chat + //(which it should be...), open it so to make the list of participants visible. This happens to be the most common case when opening the Chat floater. + if(mConversationsItems.size() == 1) + { + LLConversationViewSession* nearby_chat = dynamic_cast(mConversationsWidgets[LLUUID()]); + if (nearby_chat) + { + nearby_chat->setOpen(TRUE); + } + } +} + +void LLFloaterIMContainer::onNearbyChatClosed() +{ + // If nearby chat is the only remaining conversation and it is closed, close whole conversation floater as well + if (mConversationsItems.size() == 1) + closeFloater(); +} + +// EOF diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h new file mode 100644 index 0000000000..f65e946dad --- /dev/null +++ b/indra/newview/llfloaterimcontainer.h @@ -0,0 +1,178 @@ +/** + * @file llfloaterimcontainer.h + * @brief Multifloater containing active IM sessions in separate tab container tabs + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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_LLFLOATERIMCONTAINER_H +#define LL_LLFLOATERIMCONTAINER_H + +#include +#include + +#include "llimview.h" +#include "llevents.h" +#include "llfloater.h" +#include "llmultifloater.h" +#include "llavatarpropertiesprocessor.h" +#include "llgroupmgr.h" +#include "lltrans.h" +#include "llconversationmodel.h" +#include "llconversationview.h" + +class LLButton; +class LLLayoutPanel; +class LLLayoutStack; +class LLTabContainer; +class LLFloaterIMContainer; +class LLSpeaker; +class LLSpeakerMgr; + +class LLFloaterIMContainer + : public LLMultiFloater + , public LLIMSessionObserver +{ +public: + LLFloaterIMContainer(const LLSD& seed); + virtual ~LLFloaterIMContainer(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void draw(); + /*virtual*/ void setVisible(BOOL visible); + void onCloseFloater(LLUUID& id); + + /*virtual*/ void addFloater(LLFloater* floaterp, + BOOL select_added_floater, + LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + + void showConversation(const LLUUID& session_id); + void selectConversation(const LLUUID& session_id); + BOOL selectConversationPair(const LLUUID& session_id, bool select_widget); + + /*virtual*/ void tabClose(); + + static LLFloater* getCurrentVoiceFloater(); + static LLFloaterIMContainer* findInstance(); + static LLFloaterIMContainer* getInstance(); + + static void onCurrentChannelChanged(const LLUUID& session_id); + + void collapseMessagesPane(bool collapse); + + // Callbacks + static void idle(void* user_data); + + // LLIMSessionObserver observe triggers + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id); + /*virtual*/ void sessionRemoved(const LLUUID& session_id); + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); + + LLConversationViewModel& getRootViewModel() { return mConversationViewModel; } + LLUUID getSelectedSession() { return mSelectedSession; } + void setSelectedSession(LLUUID sessionID) { mSelectedSession = sessionID; } + LLConversationItem* getSessionModel(const LLUUID& session_id) { return get_ptr_in_map(mConversationsItems,session_id); } + LLConversationSort& getSortOrder() { return mConversationViewModel.getSorter(); } + + void onNearbyChatClosed(); + +private: + typedef std::map avatarID_panel_map_t; + avatarID_panel_map_t mSessions; + boost::signals2::connection mNewMessageConnection; + + /*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height); + + void onNewMessageReceived(const LLSD& data); + + void onExpandCollapseButtonClicked(); + void processParticipantsStyleUpdate(); + + void collapseConversationsPane(bool collapse); + + void updateState(bool collapse, S32 delta_width); + + void onAddButtonClicked(); + void onAvatarPicked(const uuid_vec_t& ids); + + BOOL isActionChecked(const LLSD& userdata); + void onCustomAction (const LLSD& userdata); + void setSortOrderSessions(const LLConversationFilter::ESortOrderType order); + void setSortOrderParticipants(const LLConversationFilter::ESortOrderType order); + void setSortOrder(const LLConversationSort& order); + + void getSelectedUUIDs(uuid_vec_t& selected_uuids); + const LLConversationItem * getCurSelectedViewModelItem(); + void getParticipantUUIDs(uuid_vec_t& selected_uuids); + void doToSelected(const LLSD& userdata); + void doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS); + void doToParticipants(const std::string& item, uuid_vec_t& selectedIDS); + void doToSelectedGroup(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); + bool enableContextMenuItem(const LLSD& userdata); + + static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); + bool enableModerateContextMenuItem(const std::string& userdata); + LLSpeaker * getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp); + LLSpeakerMgr * getSpeakerMgrForSelectedParticipant(); + bool isGroupModerator(); + bool isMuted(const LLUUID& avatar_id); + void moderateVoice(const std::string& command, const LLUUID& userID); + void moderateVoiceAllParticipants(bool unmute); + void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute); + void toggleAllowTextChat(const LLUUID& participant_uuid); + void openNearbyChat(); + + LLButton* mExpandCollapseBtn; + LLLayoutPanel* mMessagesPane; + LLLayoutPanel* mConversationsPane; + LLLayoutStack* mConversationsStack; + + bool mInitialized; + + LLUUID mSelectedSession; + + // Conversation list implementation +public: + bool removeConversationListItem(const LLUUID& uuid, bool change_focus = true); + LLConversationItem* addConversationListItem(const LLUUID& uuid, bool isWidgetSelected = false); + void setTimeNow(const LLUUID& session_id, const LLUUID& participant_id); + void setNearbyDistances(); + +private: + LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); + LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item); + bool onConversationModelEvent(const LLSD& event); + + // Conversation list data + LLPanel* mConversationsListPanel; // This is the main widget we add conversation widget to + conversations_items_map mConversationsItems; + conversations_widgets_map mConversationsWidgets; + LLConversationViewModel mConversationViewModel; + LLFolderView* mConversationsRoot; + LLEventStream mConversationsEventStream; +}; + +#endif // LL_LLFLOATERIMCONTAINER_H diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp new file mode 100644 index 0000000000..5867eb3e84 --- /dev/null +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -0,0 +1,867 @@ +/** + * @file LLFloaterIMNearbyChat.cpp + * @brief LLFloaterIMNearbyChat class implementation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" + +#include "message.h" + +#include "lliconctrl.h" +#include "llappviewer.h" +#include "llchatentry.h" +#include "llfloaterreg.h" +#include "lltrans.h" +#include "llfloaterimcontainer.h" +#include "llfloatersidepanelcontainer.h" +#include "llfocusmgr.h" +#include "lllogchat.h" +#include "llresizebar.h" +#include "llresizehandle.h" +#include "lldraghandle.h" +#include "llmenugl.h" +#include "llviewermenu.h" // for gMenuHolder +#include "llfloaterimnearbychathandler.h" +#include "llchannelmanager.h" +#include "llchathistory.h" +#include "llstylemap.h" +#include "llavatarnamecache.h" +#include "llfloaterreg.h" +#include "lltrans.h" + +#include "llfirstuse.h" +#include "llfloaterimnearbychat.h" +#include "llagent.h" // gAgent +#include "llgesturemgr.h" +#include "llmultigesture.h" +#include "llkeyboard.h" +#include "llanimationstates.h" +#include "llviewerstats.h" +#include "llcommandhandler.h" +#include "llviewercontrol.h" +#include "llnavigationbar.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llrootview.h" +#include "llviewerchat.h" +#include "lltranslate.h" + +S32 LLFloaterIMNearbyChat::sLastSpecialChatChannel = 0; + +const S32 EXPANDED_HEIGHT = 266; +const S32 COLLAPSED_HEIGHT = 60; +const S32 EXPANDED_MIN_HEIGHT = 150; + +// legacy callback glue +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); + +struct LLChatTypeTrigger { + std::string name; + EChatType type; +}; + +static LLChatTypeTrigger sChatTypeTriggers[] = { + { "/whisper" , CHAT_TYPE_WHISPER}, + { "/shout" , CHAT_TYPE_SHOUT} +}; + + +LLFloaterIMNearbyChat::LLFloaterIMNearbyChat(const LLSD& llsd) +: LLFloaterIMSessionTab(llsd), + //mOutputMonitor(NULL), + mSpeakerMgr(NULL), + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) +{ + mIsP2PChat = false; + mIsNearbyChat = true; + setIsChrome(TRUE); + mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); + mSessionID = LLUUID(); +} + +//static +LLFloaterIMNearbyChat* LLFloaterIMNearbyChat::buildFloater(const LLSD& key) +{ + LLFloaterReg::getInstance("im_container"); + return new LLFloaterIMNearbyChat(key); +} + +//virtual +BOOL LLFloaterIMNearbyChat::postBuild() +{ + setIsSingleInstance(TRUE); + BOOL result = LLFloaterIMSessionTab::postBuild(); + mInputEditor->setCommitCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxCommit, this)); + mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); + mInputEditor->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); + mInputEditor->setFocusReceivedCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusReceived, this)); + mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle")); + +// mOutputMonitor = getChild("chat_zone_indicator"); +// mOutputMonitor->setVisible(FALSE); + + // Register for font change notifications + LLViewerChat::setFontChangedCallback(boost::bind(&LLFloaterIMNearbyChat::onChatFontChange, this, _1)); + + // title must be defined BEFORE call addConversationListItem() because + // it is used for show the item's name in the conversations list + setTitle(LLTrans::getString("NearbyChatTitle")); + + //for menu + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + enable_registrar.add("NearbyChat.Check", boost::bind(&LLFloaterIMNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); + registrar.add("NearbyChat.Action", boost::bind(&LLFloaterIMNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); + + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(menu) + { + mPopupMenuHandle = menu->getHandle(); + } + + // obsolete, but may be needed for backward compatibility? + gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", true); + + if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) + { + loadHistory(); + } + + return result; +} + +// virtual +void LLFloaterIMNearbyChat::refresh() +{ + displaySpeakingIndicator(); + updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); + + // *HACK: Update transparency type depending on whether our children have focus. + // This is needed because this floater is chrome and thus cannot accept focus, so + // the transparency type setting code from LLFloater::setFocus() isn't reached. + if (getTransparencyType() != TT_DEFAULT) + { + setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); + } +} + +void LLFloaterIMNearbyChat::onNearbySpeakers() +{ + LLSD param; + param["people_panel_tab_name"] = "nearby_panel"; + LLFloaterSidePanelContainer::showPanel("people", "panel_people", param); +} + +void LLFloaterIMNearbyChat::onNearbyChatContextMenuItemClicked(const LLSD& userdata) +{ +} + +bool LLFloaterIMNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) +{ + std::string str = userdata.asString(); + if(str == "nearby_people") + onNearbySpeakers(); + return false; +} + + +BOOL LLFloaterIMNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) +{ + //fix for EXT-6625 + //highlight NearbyChat history whenever mouseclick happen in NearbyChat + //setting focus to eidtor will force onFocusLost() call that in its turn will change + //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + + if(mChatHistory) + { + mChatHistory->setFocus(TRUE); + } + + BOOL handled = LLPanel::handleMouseDown(x, y, mask); + setFocus(handled); + return handled; +} + +void LLFloaterIMNearbyChat::reloadMessages() +{ + mChatHistory->clear(); + + LLSD do_not_log; + do_not_log["do_not_log"] = true; + for(std::vector::iterator it = mMessageArchive.begin();it!=mMessageArchive.end();++it) + { + // Update the messages without re-writing them to a log file. + addMessage(*it,false, do_not_log); + } +} + +void LLFloaterIMNearbyChat::loadHistory() +{ + LLSD do_not_log; + do_not_log["do_not_log"] = true; + + std::list history; + LLLogChat::loadChatHistory("chat", history); + + std::list::const_iterator it = history.begin(); + while (it != history.end()) + { + const LLSD& msg = *it; + + std::string from = msg[IM_FROM]; + LLUUID from_id; + if (msg[IM_FROM_ID].isDefined()) + { + from_id = msg[IM_FROM_ID].asUUID(); + } + else + { + std::string legacy_name = gCacheName->buildLegacyName(from); + gCacheName->getUUID(legacy_name, from_id); + } + + LLChat chat; + chat.mFromName = from; + chat.mFromID = from_id; + chat.mText = msg[IM_TEXT].asString(); + chat.mTimeStr = msg[IM_TIME].asString(); + chat.mChatStyle = CHAT_STYLE_HISTORY; + + chat.mSourceType = CHAT_SOURCE_AGENT; + if (from_id.isNull() && SYSTEM_FROM == from) + { + chat.mSourceType = CHAT_SOURCE_SYSTEM; + + } + else if (from_id.isNull()) + { + chat.mSourceType = isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; + } + + addMessage(chat, true, do_not_log); + + it++; + } +} + +void LLFloaterIMNearbyChat::removeScreenChat() +{ + LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + if(chat_channel) + { + chat_channel->removeToastsFromChannel(); + } +} + + +void LLFloaterIMNearbyChat::setVisible(BOOL visible) +{ + LLFloaterIMSessionTab::setVisible(visible); + + if(visible) + { + removeScreenChat(); + } + setFocus(visible); +} + +// virtual +void LLFloaterIMNearbyChat::onTearOffClicked() +{ + LLFloaterIMSessionTab::onTearOffClicked(); + + // see CHUI-170: Save torn-off state of the nearby chat between sessions + BOOL in_the_multifloater = !isTornOff(); + gSavedSettings.setBOOL("NearbyChatIsNotTornOff", in_the_multifloater); +} + + +// virtual +void LLFloaterIMNearbyChat::onOpen(const LLSD& key) +{ + LLFloaterIMSessionTab::onOpen(key); + showTranslationCheckbox(LLTranslate::isTranslationConfigured()); +} + +// virtual +void LLFloaterIMNearbyChat::onClose(bool app_quitting) +{ + // Override LLFloaterIMSessionTab::onClose() so that Nearby Chat is not removed from the conversation floater +} + +// virtual +void LLFloaterIMNearbyChat::onClickCloseBtn() +{ + if (!isTornOff()) + return; + onTearOffClicked(); + + LLFloaterIMContainer *im_box = LLFloaterIMContainer::findInstance(); + if (im_box) + { + im_box->onNearbyChatClosed(); + } +} + +void LLFloaterIMNearbyChat::onChatFontChange(LLFontGL* fontp) +{ + // Update things with the new font whohoo + if (mInputEditor) + { + mInputEditor->setFont(fontp); + } +} + + +void LLFloaterIMNearbyChat::show() +{ + if (isChatMultiTab()) + { + openFloater(getKey()); + } +} + +bool LLFloaterIMNearbyChat::isChatVisible() const +{ + bool isVisible = false; + LLFloaterIMContainer* im_box = LLFloaterIMContainer::getInstance(); + // Is the IM floater container ever null? + llassert(im_box != NULL); + if (im_box != NULL) + { + isVisible = + isChatMultiTab() && gSavedSettings.getBOOL("NearbyChatIsNotTornOff")? + im_box->getVisible() && !im_box->isMinimized() : + getVisible() && !isMinimized(); + } + + return isVisible; +} + +void LLFloaterIMNearbyChat::showHistory() +{ + openFloater(); + setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); +} + +std::string LLFloaterIMNearbyChat::getCurrentChat() +{ + return mInputEditor ? mInputEditor->getText() : LLStringUtil::null; +} + +// virtual +BOOL LLFloaterIMNearbyChat::handleKeyHere( KEY key, MASK mask ) +{ + BOOL handled = FALSE; + + if( KEY_RETURN == key && mask == MASK_CONTROL) + { + // shout + sendChat(CHAT_TYPE_SHOUT); + handled = TRUE; + } + + return handled; +} + +BOOL LLFloaterIMNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) +{ + U32 in_len = in_str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + bool string_was_found = false; + + for (S32 n = 0; n < cnt && !string_was_found; n++) + { + if (in_len <= sChatTypeTriggers[n].name.length()) + { + 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 string_was_found; +} + +void LLFloaterIMNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userdata) +{ + LLFirstUse::otherAvatarChatFirst(false); + + LLFloaterIMNearbyChat* self = (LLFloaterIMNearbyChat *)userdata; + + LLWString raw_text = self->mInputEditor->getWText(); + + // Can't trim the end, because that will cause autocompletion + // to eat trailing spaces that might be part of a gesture. + LLWStringUtil::trimHead(raw_text); + + S32 length = raw_text.length(); + + if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences + { + gAgent.startTyping(); + } + else + { + gAgent.stopTyping(); + } + + /* Doesn't work -- can't tell the difference between a backspace + that killed the selection vs. backspace at the end of line. + if (length > 1 + && text[0] == '/' + && key == KEY_BACKSPACE) + { + // the selection will already be deleted, but we need to trim + // off the character before + std::string new_text = raw_text.substr(0, length-1); + self->mInputEditor->setText( new_text ); + self->mInputEditor->setCursorToEnd(); + length = length - 1; + } + */ + + KEY key = gKeyboard->currentKey(); + + // Ignore "special" keys, like backspace, arrows, etc. + if (length > 1 + && raw_text[0] == '/' + && key < KEY_SPECIAL) + { + // we're starting a gesture, attempt to autocomplete + + std::string utf8_trigger = wstring_to_utf8str(raw_text); + std::string utf8_out_str(utf8_trigger); + + if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part + + // Select to end of line, starting from the character + // after the last one the user typed. + self->mInputEditor->selectNext(rest_of_match, false); + } + else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part + self->mInputEditor->endOfDoc(); + } + + //llinfos << "GESTUREDEBUG " << trigger + // << " len " << length + // << " outlen " << out_str.getLength() + // << llendl; + } +} + +// static +void LLFloaterIMNearbyChat::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) +{ + // stop typing animation + gAgent.stopTyping(); +} + +void LLFloaterIMNearbyChat::onChatBoxFocusReceived() +{ + mInputEditor->setEnabled(!gDisconnected); +} + +EChatType LLFloaterIMNearbyChat::processChatTypeTriggers(EChatType type, std::string &str) +{ + U32 length = str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + for (S32 n = 0; n < cnt; n++) + { + if (length >= sChatTypeTriggers[n].name.length()) + { + std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); + + if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) + { + U32 trigger_length = sChatTypeTriggers[n].name.length(); + + // It's to remove space after trigger name + if (length > trigger_length && str[trigger_length] == ' ') + trigger_length++; + + str = str.substr(trigger_length, length); + + if (CHAT_TYPE_NORMAL == type) + return sChatTypeTriggers[n].type; + else + break; + } + } + } + + return type; +} + +void LLFloaterIMNearbyChat::sendChat( EChatType type ) +{ + if (mInputEditor) + { + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + if (!text.empty()) + { + // Check if this is destined for another channel + S32 channel = 0; + stripChannelNumber(text, &channel); + + std::string utf8text = wstring_to_utf8str(text); + // Try to trigger a gesture, if not chat to a script. + std::string utf8_revised_text; + if (0 == channel) + { + // discard returned "found" boolean + LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); + } + else + { + utf8_revised_text = utf8text; + } + + utf8_revised_text = utf8str_trim(utf8_revised_text); + + type = processChatTypeTriggers(type, utf8_revised_text); + + if (!utf8_revised_text.empty()) + { + // Chat with animation + sendChatFromViewer(utf8_revised_text, type, TRUE); + } + } + + mInputEditor->setText(LLStringExplicit("")); + } + + gAgent.stopTyping(); + + // If the user wants to stop chatting on hitting return, lose focus + // and go out of chat mode. + if (gSavedSettings.getBOOL("CloseChatOnReturn")) + { + stopChat(); + } +} + +void LLFloaterIMNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +{ + appendMessage(chat, args); + + if(archive) + { + mMessageArchive.push_back(chat); + if(mMessageArchive.size()>200) + mMessageArchive.erase(mMessageArchive.begin()); + } + + // logging + if (!args["do_not_log"].asBoolean() + && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) + { + std::string from_name = chat.mFromName; + + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // if the chat is coming from an agent, log the complete name + LLAvatarName av_name; + LLAvatarNameCache::get(chat.mFromID, &av_name); + + if (!av_name.mIsDisplayNameDefault) + { + from_name = av_name.getCompleteName(); + } + } + + LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + } +} + + +void LLFloaterIMNearbyChat::onChatBoxCommit() +{ + if (mInputEditor->getText().length() > 0) + { + sendChat(CHAT_TYPE_NORMAL); + } + + gAgent.stopTyping(); +} + +void LLFloaterIMNearbyChat::displaySpeakingIndicator() +{ + LLSpeakerMgr::speaker_list_t speaker_list; + LLUUID id; + + id.setNull(); + mSpeakerMgr->update(TRUE); + mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); + + for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) + { + LLPointer s = *i; + if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) + { + id = s->mID; + break; + } + } + + if (!id.isNull()) + { + //mOutputMonitor->setVisible(TRUE); + //mOutputMonitor->setSpeakerId(id); + } + else + { + //mOutputMonitor->setVisible(FALSE); + } +} + +void LLFloaterIMNearbyChat::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) +{ + sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); +} + +void LLFloaterIMNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) +{ + // Look for "/20 foo" channel chats. + S32 channel = 0; + LLWString out_text = stripChannelNumber(wtext, &channel); + std::string utf8_out_text = wstring_to_utf8str(out_text); + std::string utf8_text = wstring_to_utf8str(wtext); + + utf8_text = utf8str_trim(utf8_text); + if (!utf8_text.empty()) + { + utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); + } + + // Don't animate for chats people can't hear (chat to scripts) + if (animate && (channel == 0)) + { + if (type == CHAT_TYPE_WHISPER) + { + lldebugs << "You whisper " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_NORMAL) + { + lldebugs << "You say " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_SHOUT) + { + lldebugs << "You shout " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); + } + else + { + llinfos << "send_chat_from_viewer() - invalid volume" << llendl; + return; + } + } + else + { + if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) + { + lldebugs << "Channel chat: " << utf8_text << llendl; + } + } + + send_chat_from_viewer(utf8_out_text, type, channel); +} + +// static +bool LLFloaterIMNearbyChat::isWordsName(const std::string& name) +{ + // checking to see if it's display name plus username in parentheses + S32 open_paren = name.find(" (", 0); + S32 close_paren = name.find(')', 0); + + if (open_paren != std::string::npos && + close_paren == name.length()-1) + { + return true; + } + else + { + //checking for a single space + S32 pos = name.find(' ', 0); + return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + } +} + +// static +void LLFloaterIMNearbyChat::startChat(const char* line) +{ + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) + { + nearby_chat->show(); + nearby_chat->setVisible(TRUE); + nearby_chat->setFocus(TRUE); + nearby_chat->mInputEditor->setFocus(TRUE); + + if (line) + { + std::string line_string(line); + nearby_chat->mInputEditor->setText(line_string); + } + + nearby_chat->mInputEditor->endOfDoc(); + } +} + +// Exit "chat mode" and do the appropriate focus changes +// static +void LLFloaterIMNearbyChat::stopChat() +{ + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) + { + nearby_chat->mInputEditor->setFocus(FALSE); + gAgent.stopTyping(); + } +} + +// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. +// Otherwise returns input and channel 0. +LLWString LLFloaterIMNearbyChat::stripChannelNumber(const LLWString &mesg, S32* channel) +{ + if (mesg[0] == '/' + && mesg[1] == '/') + { + // This is a "repeat channel send" + *channel = sLastSpecialChatChannel; + return mesg.substr(2, mesg.length() - 2); + } + else if (mesg[0] == '/' + && mesg[1] + && LLStringOps::isDigit(mesg[1])) + { + // This a special "/20" speak on a channel + S32 pos = 0; + + // Copy the channel number into a string + LLWString channel_string; + llwchar c; + do + { + c = mesg[pos+1]; + channel_string.push_back(c); + pos++; + } + while(c && pos < 64 && LLStringOps::isDigit(c)); + + // Move the pointer forward to the first non-whitespace char + // Check isspace before looping, so we can handle "/33foo" + // as well as "/33 foo" + while(c && iswspace(c)) + { + c = mesg[pos+1]; + pos++; + } + + sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); + *channel = sLastSpecialChatChannel; + return mesg.substr(pos, mesg.length() - pos); + } + else + { + // This is normal chat. + *channel = 0; + return mesg; + } +} + +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ChatFromViewer); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ChatData); + msg->addStringFast(_PREHASH_Message, utf8_out_text); + msg->addU8Fast(_PREHASH_Type, type); + msg->addS32("Channel", channel); + + gAgent.sendReliableMessage(); + + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); +} + +class LLChatCommandHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } + + // Your code here + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + bool retval = false; + // Need at least 2 tokens to have a valid message. + if (tokens.size() < 2) + { + retval = false; + } + else + { + S32 channel = tokens[0].asInteger(); + // VWR-19499 Restrict function to chat channels greater than 0. + if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) + { + retval = true; + // Send unescaped message, see EXT-6353. + std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); + send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); + } + else + { + retval = false; + // Tell us this is an unsupported SLurl. + } + } + return retval; + } +}; + +// Creating the object registers with the dispatcher. +LLChatCommandHandler gChatHandler; diff --git a/indra/newview/llfloaterimnearbychat.h b/indra/newview/llfloaterimnearbychat.h new file mode 100644 index 0000000000..1479746fbd --- /dev/null +++ b/indra/newview/llfloaterimnearbychat.h @@ -0,0 +1,125 @@ +/** + * @file llfloaterimnearbychat.h + * @brief LLFloaterIMNearbyChat class definition + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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_LLFLOATERIMNEARBYCHAT_H +#define LL_LLFLOATERIMNEARBYCHAT_H + +#include "llfloaterimsessiontab.h" +#include "llcombobox.h" +#include "llgesturemgr.h" +#include "llchat.h" +#include "llvoiceclient.h" +#include "lloutputmonitorctrl.h" +#include "llspeakers.h" +#include "llscrollbar.h" +#include "llviewerchat.h" +#include "llpanel.h" + +class LLResizeBar; + +class LLFloaterIMNearbyChat + : public LLFloaterIMSessionTab +{ +public: + // constructor for inline chat-bars (e.g. hosted in chat history window) + LLFloaterIMNearbyChat(const LLSD& key = LLSD(LLUUID())); + ~LLFloaterIMNearbyChat() {} + + static LLFloaterIMNearbyChat* buildFloater(const LLSD& key); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ void setVisible(BOOL visible); + + void loadHistory(); + void reloadMessages(); + void removeScreenChat(); + + void addToHost(); + void show(); + bool isChatVisible() const; + + /** @param archive true - to save a message to the chat history log */ + void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); + void onNearbyChatContextMenuItemClicked(const LLSD& userdata); + bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); + + LLChatEntry* getChatBox() { return mInputEditor; } + + std::string getCurrentChat(); + + virtual BOOL handleKeyHere( KEY key, MASK mask ); + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + + static void startChat(const char* line); + static void stopChat(); + + static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); + static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); + + static bool isWordsName(const std::string& name); + + void showHistory(); + +protected: + static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); + static void onChatBoxKeystroke(LLTextEditor* caller, void* userdata); + static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); + void onChatBoxFocusReceived(); + + void sendChat( EChatType type ); + void onChatBoxCommit(); + void onChatFontChange(LLFontGL* fontp); + + /*virtual*/ void onTearOffClicked(); + /*virtual*/ void onClickCloseBtn(); + + static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); + EChatType processChatTypeTriggers(EChatType type, std::string &str); + + void displaySpeakingIndicator(); + + // Which non-zero channel did we last chat on? + static S32 sLastSpecialChatChannel; + + LLOutputMonitorCtrl* mOutputMonitor; + LLLocalSpeakerMgr* mSpeakerMgr; + + S32 mExpandedHeight; + +private: + + void onNearbySpeakers (); + + /*virtual*/ void refresh(); + + LLHandle mPopupMenuHandle; + std::vector mMessageArchive; + +}; + +#endif // LL_LLFLOATERIMNEARBYCHAT_H diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp new file mode 100644 index 0000000000..0dfaa9174b --- /dev/null +++ b/indra/newview/llfloaterimnearbychathandler.cpp @@ -0,0 +1,630 @@ +/** + * @file LLFloaterIMNearbyChatHandler.cpp + * @brief Nearby chat chat managment + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" + +#include "llagentdata.h" // for gAgentID +#include "llfloaterimnearbychathandler.h" + +#include "llchatitemscontainerctrl.h" +#include "llfirstuse.h" +#include "llfloaterscriptdebug.h" +#include "llhints.h" +#include "llfloaterimnearbychat.h" +#include "llrecentpeople.h" + +#include "llviewercontrol.h" + +#include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance +#include "llviewerwindow.h"//for screen channel position +#include "llfloaterimnearbychat.h" +#include "llrootview.h" +#include "lllayoutstack.h" + +//add LLFloaterIMNearbyChatHandler to LLNotificationsUI namespace +using namespace LLNotificationsUI; + +static LLFloaterIMNearbyChatToastPanel* createToastPanel() +{ + LLFloaterIMNearbyChatToastPanel* item = LLFloaterIMNearbyChatToastPanel::createInstance(); + return item; +} + + +//----------------------------------------------------------------------------------------------- +//LLFloaterIMNearbyChatScreenChannel +//----------------------------------------------------------------------------------------------- + +class LLFloaterIMNearbyChatScreenChannel: public LLScreenChannelBase +{ + LOG_CLASS(LLFloaterIMNearbyChatScreenChannel); +public: + typedef std::vector > toast_vec_t; + typedef std::list > toast_list_t; + + LLFloaterIMNearbyChatScreenChannel(const Params& p) + : LLScreenChannelBase(p) + { + mStopProcessing = false; + + LLControlVariable* ctrl = gSavedSettings.getControl("NearbyToastLifeTime").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloaterIMNearbyChatScreenChannel::updateToastsLifetime, this)); + } + + ctrl = gSavedSettings.getControl("NearbyToastFadingTime").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloaterIMNearbyChatScreenChannel::updateToastFadingTime, this)); + } + } + + void addChat (LLSD& chat); + void arrangeToasts (); + + typedef boost::function create_toast_panel_callback_t; + void setCreatePanelCallback(create_toast_panel_callback_t value) { m_create_toast_panel_callback_t = value;} + + void onToastDestroyed (LLToast* toast, bool app_quitting); + void onToastFade (LLToast* toast); + + void redrawToasts() + { + arrangeToasts(); + } + + // hide all toasts from screen, but not remove them from a channel + // removes all toasts from a channel + virtual void removeToastsFromChannel() + { + for(toast_vec_t::iterator it = m_active_toasts.begin(); it != m_active_toasts.end(); ++it) + { + addToToastPool(it->get()); + } + m_active_toasts.clear(); + }; + + virtual void deleteAllChildren() + { + LL_DEBUGS("NearbyChat") << "Clearing toast pool" << llendl; + m_toast_pool.clear(); + m_active_toasts.clear(); + LLScreenChannelBase::deleteAllChildren(); + } + +protected: + void deactivateToast(LLToast* toast); + void addToToastPool(LLToast* toast) + { + if (!toast) return; + LL_DEBUGS("NearbyChat") << "Pooling toast" << llendl; + toast->setVisible(FALSE); + toast->stopTimer(); + toast->setIsHidden(true); + + // Nearby chat toasts that are hidden, not destroyed. They are collected to the toast pool, so that + // they can be used next time, this is done for performance. But if the toast lifetime was changed + // (from preferences floater (STORY-36)) while it was shown (at this moment toast isn't in the pool yet) + // changes don't take affect. + // So toast's lifetime should be updated each time it's added to the pool. Otherwise viewer would have + // to be restarted so that changes take effect. + toast->setLifetime(gSavedSettings.getS32("NearbyToastLifeTime")); + toast->setFadingTime(gSavedSettings.getS32("NearbyToastFadingTime")); + m_toast_pool.push_back(toast->getHandle()); + } + + void createOverflowToast(S32 bottom, F32 timer); + + void updateToastsLifetime(); + + void updateToastFadingTime(); + + create_toast_panel_callback_t m_create_toast_panel_callback_t; + + bool createPoolToast(); + + toast_vec_t m_active_toasts; + toast_list_t m_toast_pool; + + bool mStopProcessing; + bool mChannelRect; +}; + + + +//----------------------------------------------------------------------------------------------- +// LLFloaterIMNearbyChatToast +//----------------------------------------------------------------------------------------------- + +// We're deriving from LLToast to be able to override onClose() +// in order to handle closing nearby chat toasts properly. +class LLFloaterIMNearbyChatToast : public LLToast +{ + LOG_CLASS(LLFloaterIMNearbyChatToast); +public: + LLFloaterIMNearbyChatToast(const LLToast::Params& p, LLFloaterIMNearbyChatScreenChannel* nc_channelp) + : LLToast(p), + mNearbyChatScreenChannelp(nc_channelp) + { + } + + /*virtual*/ void onClose(bool app_quitting); + +private: + LLFloaterIMNearbyChatScreenChannel* mNearbyChatScreenChannelp; +}; + +//----------------------------------------------------------------------------------------------- +// LLFloaterIMNearbyChatScreenChannel +//----------------------------------------------------------------------------------------------- + +void LLFloaterIMNearbyChatScreenChannel::deactivateToast(LLToast* toast) +{ + toast_vec_t::iterator pos = std::find(m_active_toasts.begin(), m_active_toasts.end(), toast->getHandle()); + + if (pos == m_active_toasts.end()) + { + llassert(pos == m_active_toasts.end()); + return; + } + + LL_DEBUGS("NearbyChat") << "Deactivating toast" << llendl; + m_active_toasts.erase(pos); +} + +void LLFloaterIMNearbyChatScreenChannel::createOverflowToast(S32 bottom, F32 timer) +{ + //we don't need overflow toast in nearby chat +} + +void LLFloaterIMNearbyChatScreenChannel::onToastDestroyed(LLToast* toast, bool app_quitting) +{ + LL_DEBUGS("NearbyChat") << "Toast destroyed (app_quitting=" << app_quitting << ")" << llendl; + + if (app_quitting) + { + // Viewer is quitting. + // Immediately stop processing chat messages (EXT-1419). + mStopProcessing = true; +} + else + { + // The toast is being closed by user (STORM-192). + // Remove it from the list of active toasts to prevent + // further references to the invalid pointer. + deactivateToast(toast); + } +} + +void LLFloaterIMNearbyChatScreenChannel::onToastFade(LLToast* toast) +{ + LL_DEBUGS("NearbyChat") << "Toast fading" << llendl; + + //fade mean we put toast to toast pool + if(!toast) + return; + + deactivateToast(toast); + + addToToastPool(toast); + + arrangeToasts(); +} + +void LLFloaterIMNearbyChatScreenChannel::updateToastsLifetime() +{ + S32 seconds = gSavedSettings.getS32("NearbyToastLifeTime"); + toast_list_t::iterator it; + + for(it = m_toast_pool.begin(); it != m_toast_pool.end(); ++it) + { + (*it).get()->setLifetime(seconds); + } +} + +void LLFloaterIMNearbyChatScreenChannel::updateToastFadingTime() +{ + S32 seconds = gSavedSettings.getS32("NearbyToastFadingTime"); + toast_list_t::iterator it; + + for(it = m_toast_pool.begin(); it != m_toast_pool.end(); ++it) + { + (*it).get()->setFadingTime(seconds); + } +} + +bool LLFloaterIMNearbyChatScreenChannel::createPoolToast() +{ + LLFloaterIMNearbyChatToastPanel* panel= m_create_toast_panel_callback_t(); + if(!panel) + return false; + + LLToast::Params p; + p.panel = panel; + p.lifetime_secs = gSavedSettings.getS32("NearbyToastLifeTime"); + p.fading_time_secs = gSavedSettings.getS32("NearbyToastFadingTime"); + + LLToast* toast = new LLFloaterIMNearbyChatToast(p, this); + + + toast->setOnFadeCallback(boost::bind(&LLFloaterIMNearbyChatScreenChannel::onToastFade, this, _1)); + + // If the toast gets somehow prematurely destroyed, deactivate it to prevent crash (STORM-1352). + toast->setOnToastDestroyedCallback(boost::bind(&LLFloaterIMNearbyChatScreenChannel::onToastDestroyed, this, _1, false)); + + LL_DEBUGS("NearbyChat") << "Creating and pooling toast" << llendl; + m_toast_pool.push_back(toast->getHandle()); + return true; +} + +void LLFloaterIMNearbyChatScreenChannel::addChat(LLSD& chat) +{ + //look in pool. if there is any message + if(mStopProcessing) + return; + + /* + find last toast and check ID + */ + + if(m_active_toasts.size()) + { + LLUUID fromID = chat["from_id"].asUUID(); // agent id or object id + std::string from = chat["from"].asString(); + LLToast* toast = m_active_toasts[0].get(); + if (toast) + { + LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); + + if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText()) + { + panel->addMessage(chat); + toast->reshapeToPanel(); + toast->startTimer(); + + arrangeToasts(); + return; + } + } + } + + + + if(m_toast_pool.empty()) + { + //"pool" is empty - create one more panel + LL_DEBUGS("NearbyChat") << "Empty pool" << llendl; + if(!createPoolToast())//created toast will go to pool. so next call will find it + return; + addChat(chat); + return; + } + + int chat_type = chat["chat_type"].asInteger(); + + if( ((EChatType)chat_type == CHAT_TYPE_DEBUG_MSG)) + { + if(gSavedSettings.getBOOL("ShowScriptErrors") == FALSE) + return; + if(gSavedSettings.getS32("ShowScriptErrorsLocation")== 1) + return; + } + + + //take 1st element from pool, (re)initialize it, put it in active toasts + + LL_DEBUGS("NearbyChat") << "Getting toast from pool" << llendl; + LLToast* toast = m_toast_pool.back().get(); + + m_toast_pool.pop_back(); + + + LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); + if(!panel) + return; + panel->init(chat); + + toast->reshapeToPanel(); + toast->startTimer(); + + m_active_toasts.push_back(toast->getHandle()); + + arrangeToasts(); +} + +static bool sort_toasts_predicate(LLHandle first, LLHandle second) +{ + if (!first.get() || !second.get()) return false; // STORM-1352 + + F32 v1 = first.get()->getTimeLeftToLive(); + F32 v2 = second.get()->getTimeLeftToLive(); + return v1 > v2; +} + +void LLFloaterIMNearbyChatScreenChannel::arrangeToasts() +{ + if(mStopProcessing || isHovering()) + return; + + if (mFloaterSnapRegion == NULL) + { + mFloaterSnapRegion = gViewerWindow->getRootView()->getChildView("floater_snap_region"); + } + + if (!getParent()) + { + // connect to floater snap region just to get resize events, we don't care about being a proper widget + mFloaterSnapRegion->addChild(this); + setFollows(FOLLOWS_ALL); + } + + LLRect toast_rect; + updateRect(); + + LLRect channel_rect; + mFloaterSnapRegion->localRectToOtherView(mFloaterSnapRegion->getLocalRect(), &channel_rect, gFloaterView); + channel_rect.mLeft += 10; + channel_rect.mRight = channel_rect.mLeft + 300; + + S32 channel_bottom = channel_rect.mBottom; + + S32 bottom = channel_bottom + 80; + S32 margin = gSavedSettings.getS32("ToastGap"); + + //sort active toasts + std::sort(m_active_toasts.begin(),m_active_toasts.end(),sort_toasts_predicate); + + //calc max visible item and hide other toasts. + + for(toast_vec_t::iterator it = m_active_toasts.begin(); it != m_active_toasts.end(); ++it) + { + LLToast* toast = it->get(); + if (!toast) + { + llwarns << "NULL found in the active chat toasts list!" << llendl; + continue; + } + + S32 toast_top = bottom + toast->getRect().getHeight() + margin; + + if(toast_top > channel_rect.getHeight()) + { + while(it!=m_active_toasts.end()) + { + addToToastPool(it->get()); + it=m_active_toasts.erase(it); + } + break; + } + + toast_rect = toast->getRect(); + toast_rect.setLeftTopAndSize(channel_rect.mLeft , bottom + toast_rect.getHeight(), toast_rect.getWidth() ,toast_rect.getHeight()); + + toast->setRect(toast_rect); + bottom += toast_rect.getHeight() - toast->getTopPad() + margin; + } + + // use reverse order to provide correct z-order and avoid toast blinking + + for(toast_vec_t::reverse_iterator it = m_active_toasts.rbegin(); it != m_active_toasts.rend(); ++it) + { + LLToast* toast = it->get(); + if (toast) + { + toast->setIsHidden(false); + toast->setVisible(TRUE); + } + } + +} + + + +//----------------------------------------------------------------------------------------------- +//LLFloaterIMNearbyChatHandler +//----------------------------------------------------------------------------------------------- +boost::scoped_ptr LLFloaterIMNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat")); + +LLFloaterIMNearbyChatHandler::LLFloaterIMNearbyChatHandler() +{ + // Getting a Channel for our notifications + LLFloaterIMNearbyChatScreenChannel::Params p; + p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); + LLFloaterIMNearbyChatScreenChannel* channel = new LLFloaterIMNearbyChatScreenChannel(p); + + LLFloaterIMNearbyChatScreenChannel::create_toast_panel_callback_t callback = createToastPanel; + + channel->setCreatePanelCallback(callback); + + LLChannelManager::getInstance()->addChannel(channel); + + mChannel = channel->getHandle(); +} + +LLFloaterIMNearbyChatHandler::~LLFloaterIMNearbyChatHandler() +{ +} + + +void LLFloaterIMNearbyChatHandler::initChannel() +{ + //LLRect snap_rect = gFloaterView->getSnapRect(); + //mChannel->init(snap_rect.mLeft, snap_rect.mLeft + 200); +} + + + +void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg, + const LLSD &args) +{ + if(chat_msg.mMuted == TRUE) + return; + + if(chat_msg.mText.empty()) + return;//don't process empty messages + + LLFloaterReg::getInstance("im_container"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + + // Build notification data + LLSD chat; + chat["message"] = chat_msg.mText; + chat["from"] = chat_msg.mFromName; + chat["from_id"] = chat_msg.mFromID; + chat["time"] = chat_msg.mTime; + chat["source"] = (S32)chat_msg.mSourceType; + chat["chat_type"] = (S32)chat_msg.mChatType; + chat["chat_style"] = (S32)chat_msg.mChatStyle; + // Pass sender info so that it can be rendered properly (STORM-1021). + chat["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); + + if (chat_msg.mChatType == CHAT_TYPE_DIRECT && + chat_msg.mText.length() > 0 && + chat_msg.mText[0] == '@') + { + // Send event on to LLEventStream and exit + sChatWatcher->post(chat); + return; + } + + // don't show toast and add message to chat history on receive debug message + // with disabled setting showing script errors or enabled setting to show script + // errors in separate window. + if (chat_msg.mChatType == CHAT_TYPE_DEBUG_MSG) + { + if(gSavedSettings.getBOOL("ShowScriptErrors") == FALSE) + return; + + // don't process debug messages from not owned objects, see EXT-7762 + if (gAgentID != chat_msg.mOwnerID) + { + return; + } + + if (gSavedSettings.getS32("ShowScriptErrorsLocation")== 1)// show error in window //("ScriptErrorsAsChat")) + { + + LLColor4 txt_color; + + LLViewerChat::getChatColor(chat_msg,txt_color); + + LLFloaterScriptDebug::addScriptLine(chat_msg.mText, + chat_msg.mFromName, + txt_color, + chat_msg.mFromID); + return; + } + } + + nearby_chat->addMessage(chat_msg, true, args); + + if(chat_msg.mSourceType == CHAT_SOURCE_AGENT + && chat_msg.mFromID.notNull() + && chat_msg.mFromID != gAgentID) + { + LLFirstUse::otherAvatarChatFirst(); + + // Add sender to the recent people list. + LLRecentPeople::instance().add(chat_msg.mFromID); + + } + + // Send event on to LLEventStream + sChatWatcher->post(chat); + + if( nearby_chat->isInVisibleChain() + || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT + && gSavedSettings.getBOOL("UseChatBubbles") ) + || mChannel.isDead() + || !mChannel.get()->getShowToasts() ) // to prevent toasts in Do Not Disturb mode + return;//no need in toast if chat is visible or if bubble chat is enabled + + // arrange a channel on a screen + if(!mChannel.get()->getVisible()) + { + initChannel(); + } + + /* + //comment all this due to EXT-4432 + ..may clean up after some time... + + //only messages from AGENTS + if(CHAT_SOURCE_OBJECT == chat_msg.mSourceType) + { + if(chat_msg.mChatType == CHAT_TYPE_DEBUG_MSG) + return;//ok for now we don't skip messeges from object, so skip only debug messages + } + */ + + LLFloaterIMNearbyChatScreenChannel* channel = dynamic_cast(mChannel.get()); + + if(channel) + { + // Handle IRC styled messages. + std::string toast_msg; + if (chat_msg.mChatStyle == CHAT_STYLE_IRC) + { + if (!chat_msg.mFromName.empty()) + { + toast_msg += chat_msg.mFromName; + } + toast_msg += chat_msg.mText.substr(3); + } + else + { + toast_msg = chat_msg.mText; + } + + // Add a nearby chat toast. + LLUUID id; + id.generate(); + chat["id"] = id; + std::string r_color_name = "White"; + F32 r_color_alpha = 1.0f; + LLViewerChat::getChatColor( chat_msg, r_color_name, r_color_alpha); + + chat["text_color"] = r_color_name; + chat["color_alpha"] = r_color_alpha; + chat["font_size"] = (S32)LLViewerChat::getChatFontSize() ; + chat["message"] = toast_msg; + channel->addChat(chat); + } +} + + +//----------------------------------------------------------------------------------------------- +// LLFloaterIMNearbyChatToast +//----------------------------------------------------------------------------------------------- + +// virtual +void LLFloaterIMNearbyChatToast::onClose(bool app_quitting) +{ + mNearbyChatScreenChannelp->onToastDestroyed(this, app_quitting); +} + +// EOF diff --git a/indra/newview/llfloaterimnearbychathandler.h b/indra/newview/llfloaterimnearbychathandler.h new file mode 100644 index 0000000000..5e6f8cde30 --- /dev/null +++ b/indra/newview/llfloaterimnearbychathandler.h @@ -0,0 +1,54 @@ +/** + * @file llfloaterimnearbychathandler.h + * @brief nearby chat notify + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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_LLFLOATERIMNEARBYCHATHANDLER_H +#define LL_LLFLOATERIMNEARBYCHATHANDLER_H + +#include "llnotificationhandler.h" + +class LLEventPump; + +//add LLFloaterIMNearbyChatHandler to LLNotificationsUI namespace +namespace LLNotificationsUI{ + +class LLFloaterIMNearbyChatHandler : public LLChatHandler +{ +public: + LLFloaterIMNearbyChatHandler(); + virtual ~LLFloaterIMNearbyChatHandler(); + + + virtual void processChat(const LLChat& chat_msg, const LLSD &args); + +protected: + virtual void initChannel(); + + static boost::scoped_ptr sChatWatcher; +}; + +} + +#endif /* LL_LLFLOATERIMNEARBYCHATHANDLER_H */ diff --git a/indra/newview/llfloaterimnearbychatlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp new file mode 100644 index 0000000000..14a22bcd84 --- /dev/null +++ b/indra/newview/llfloaterimnearbychatlistener.cpp @@ -0,0 +1,100 @@ +/** + * @file llfloaterimnearbychatlistener.cpp + * @author Dave Simmons + * @date 2011-03-15 + * @brief Implementation for LLFloaterIMNearbyChatListener. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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 "llviewerprecompiledheaders.h" + +#include "llfloaterimnearbychatlistener.h" +#include "llfloaterimnearbychat.h" + +#include "llagent.h" +#include "llchat.h" + + + +LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar) + : LLEventAPI("LLChatBar", + "LLChatBar listener to (e.g.) sendChat, etc."), + mChatbar(chatbar) +{ + add("sendChat", + "Send chat to the simulator:\n" + "[\"message\"] chat message text [required]\n" + "[\"channel\"] chat channel number [default = 0]\n" + "[\"type\"] chat type \"whisper\", \"normal\", \"shout\" [default = \"normal\"]", + &LLFloaterIMNearbyChatListener::sendChat); +} + + +// "sendChat" command +void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const +{ + // Extract the data + std::string chat_text = chat_data["message"].asString(); + + S32 channel = 0; + if (chat_data.has("channel")) + { + channel = chat_data["channel"].asInteger(); + if (channel < 0 || channel >= CHAT_CHANNEL_DEBUG) + { // Use 0 up to (but not including) CHAT_CHANNEL_DEBUG + channel = 0; + } + } + + EChatType type_o_chat = CHAT_TYPE_NORMAL; + if (chat_data.has("type")) + { + std::string type_string = chat_data["type"].asString(); + if (type_string == "whisper") + { + type_o_chat = CHAT_TYPE_WHISPER; + } + else if (type_string == "shout") + { + type_o_chat = CHAT_TYPE_SHOUT; + } + } + + // Have to prepend /42 style channel numbers + std::string chat_to_send; + if (channel == 0) + { + chat_to_send = chat_text; + } + else + { + chat_to_send += "/"; + chat_to_send += chat_data["channel"].asString(); + chat_to_send += " "; + chat_to_send += chat_text; + } + + // Send it as if it was typed in + mChatbar.sendChatFromViewer(chat_to_send, type_o_chat, (BOOL)(channel == 0)); +} + diff --git a/indra/newview/llfloaterimnearbychatlistener.h b/indra/newview/llfloaterimnearbychatlistener.h new file mode 100644 index 0000000000..1470a6dc1e --- /dev/null +++ b/indra/newview/llfloaterimnearbychatlistener.h @@ -0,0 +1,50 @@ +/** + * @file llfloaterimnearbychatlistener.h + * @author Dave Simmons + * @date 2011-03-15 + * @brief Class definition for LLFloaterIMNearbyChatListener. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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_LLFLOATERIMNEARBYCHATLISTENER_H +#define LL_LLFLOATERIMNEARBYCHATLISTENER_H + +#include "lleventapi.h" + +class LLSD; +class LLFloaterIMNearbyChat; + +class LLFloaterIMNearbyChatListener : public LLEventAPI +{ +public: + LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar); + +private: + void sendChat(LLSD const & chat_data) const; + + LLFloaterIMNearbyChat & mChatbar; +}; + +#endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H + diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp new file mode 100644 index 0000000000..0c622e07c4 --- /dev/null +++ b/indra/newview/llfloaterimsession.cpp @@ -0,0 +1,1202 @@ +/** + * @file llfloaterimsession.cpp + * @brief LLFloaterIMSession class definition + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" + +#include "llfloaterimsession.h" + +#include "lldraghandle.h" +#include "llnotificationsutil.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llavataractions.h" +#include "llavatarnamecache.h" +#include "llbutton.h" +#include "llchannelmanager.h" +#include "llchiclet.h" +#include "llchicletbar.h" +#include "llfloaterreg.h" +#include "llfloateravatarpicker.h" +#include "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container +#include "llinventoryfunctions.h" +//#include "lllayoutstack.h" +#include "llchatentry.h" +#include "lllogchat.h" +#include "llscreenchannel.h" +#include "llsyswellwindow.h" +#include "lltrans.h" +#include "llchathistory.h" +#include "llnotifications.h" +#include "llviewerwindow.h" +#include "lltransientfloatermgr.h" +#include "llinventorymodel.h" +#include "llrootview.h" +#include "llspeakers.h" +#include "llviewerchat.h" +#include "llnotificationmanager.h" +#include "llautoreplace.h" + +floater_showed_signal_t LLFloaterIMSession::sIMFloaterShowedSignal; + +LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id) + : LLFloaterIMSessionTab(session_id), + mLastMessageIndex(-1), + mDialog(IM_NOTHING_SPECIAL), + mSavedTitle(), + mTypingStart(), + mShouldSendTypingState(false), + mMeTyping(false), + mOtherTyping(false), + mTypingTimer(), + mTypingTimeoutTimer(), + mPositioned(false), + mSessionInitialized(false), + mStartConferenceInSameFloater(false) +{ + mIsNearbyChat = false; + + initIMSession(session_id); + + setOverlapsScreenChannel(true); + + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); + + setDocked(true); +} + + +// virtual +void LLFloaterIMSession::refresh() +{ + if (mMeTyping) +{ + // Time out if user hasn't typed for a while. + if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS) + { + setTyping(false); + } + } +} + +// virtual +void LLFloaterIMSession::onClickCloseBtn() +{ + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); + + if (session != NULL) + { + bool is_call_with_chat = session->isGroupSessionType() + || session->isAdHocSessionType() || session->isP2PSessionType(); + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + if (is_call_with_chat && voice_channel != NULL + && voice_channel->isActive()) + { + LLSD payload; + payload["session_id"] = mSessionID; + LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + return; + } + } + else + { + llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; + return; + } + + LLFloaterIMSessionTab::onClickCloseBtn(); +} + +/* static */ +void LLFloaterIMSession::newIMCallback(const LLSD& data) +{ + if (data["num_unread"].asInteger() > 0 || data["from_id"].asUUID().isNull()) + { + LLUUID session_id = data["session_id"].asUUID(); + + LLFloaterIMSession* floater = LLFloaterReg::findTypedInstance("impanel", session_id); + + // update if visible, otherwise will be updated when opened + if (floater && floater->getVisible()) + { + floater->updateMessages(); + } + } +} + +void LLFloaterIMSession::onVisibilityChange(const LLSD& new_visibility) +{ + bool visible = new_visibility.asBoolean(); + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + if (visible && voice_channel && + voice_channel->getState() == LLVoiceChannel::STATE_CONNECTED) + { + LLFloaterReg::showInstance("voice_call", mSessionID); + } + else + { + LLFloaterReg::hideInstance("voice_call", mSessionID); + } +} + +void LLFloaterIMSession::onSendMsg( LLUICtrl* ctrl, void* userdata ) +{ + LLFloaterIMSession* self = (LLFloaterIMSession*) userdata; + self->sendMsgFromInputEditor(); + self->setTyping(false); +} + +void LLFloaterIMSession::sendMsgFromInputEditor() +{ + if (gAgent.isGodlike() + || (mDialog != IM_NOTHING_SPECIAL) + || !mOtherParticipantUUID.isNull()) + { + if (mInputEditor) + { + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + if(!text.empty()) + { + // Truncate and convert to UTF8 for transport + std::string utf8_text = wstring_to_utf8str(text); + + sendMsg(utf8_text); + + mInputEditor->setText(LLStringUtil::null); + } + } + } + else + { + llinfos << "Cannot send IM to everyone unless you're a god." << llendl; + } +} + +void LLFloaterIMSession::sendMsg(const std::string& msg) +{ + const std::string utf8_text = utf8str_truncate(msg, MAX_MSG_BUF_SIZE - 1); + + 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); + } + + updateMessages(); +} + +LLFloaterIMSession::~LLFloaterIMSession() +{ + mVoiceChannelStateChangeConnection.disconnect(); + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver(this); + } + + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); +} + + +void LLFloaterIMSession::initIMSession(const LLUUID& session_id) +{ + // Change the floater key to bind it to a new session. + setKey(session_id); + + mSessionID = session_id; + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + + if (mSession) + { + mIsP2PChat = mSession->isP2PSessionType(); + mSessionInitialized = mSession->mSessionInitialized; + mDialog = mSession->mType; + } +} + +void LLFloaterIMSession::initIMFloater() +{ + const LLUUID& other_party_id = + LLIMModel::getInstance()->getOtherParticipantID(mSessionID); + if (other_party_id.notNull()) + { + mOtherParticipantUUID = other_party_id; + } + + boundVoiceChannel(); + + mTypingStart = LLTrans::getString("IM_typing_start_string"); + + // Show control panel in torn off floaters only. + mParticipantListPanel->setVisible(!getHost() && gSavedSettings.getBOOL("IMShowControlPanel")); + + // Disable input editor if session cannot accept text + if ( mSession && !mSession->mTextIMPossible ) + { + mInputEditor->setEnabled(FALSE); + mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); + } + + if (!mIsP2PChat) + { + std::string session_name(LLIMModel::instance().getName(mSessionID)); + updateSessionName(session_name); + } +} + +//virtual +BOOL LLFloaterIMSession::postBuild() +{ + BOOL result = LLFloaterIMSessionTab::postBuild(); + + mInputEditor->setMaxTextLength(1023); + // enable line history support for instant message bar + // XXX stinson TODO : resolve merge by adding autoreplace to text editors +#if 0 + // *TODO Establish LineEditor with autoreplace callback + mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); +#endif + + mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); + mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); + mInputEditor->setKeystrokeCallback( boost::bind(onInputEditorKeystroke, _1, this) ); + mInputEditor->setCommitCallback(boost::bind(onSendMsg, _1, this)); + + setDocked(true); + + LLButton* add_btn = getChild("add_btn"); + + // Allow to add chat participants depending on the session type + add_btn->setEnabled(isInviteAllowed()); + add_btn->setClickedCallback(boost::bind(&LLFloaterIMSession::onAddButtonClicked, this)); + + childSetAction("voice_call_btn", boost::bind(&LLFloaterIMSession::onCallButtonClicked, this)); + + LLVoiceClient::getInstance()->addObserver(this); + + //*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) + + initIMFloater(); + + return result; +} + +void LLFloaterIMSession::onAddButtonClicked() +{ + LLView * button = findChild("toolbar_panel")->findChild("add_btn"); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterIMSession::addSessionParticipants, this, _1), TRUE, TRUE, FALSE, root_floater->getName(), button); + if (!picker) + { + return; + } + + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&LLFloaterIMSession::canAddSelectedToChat, this, _1)); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +bool LLFloaterIMSession::canAddSelectedToChat(const uuid_vec_t& uuids) +{ + if (!mSession + || mDialog == IM_SESSION_GROUP_START + || mDialog == IM_SESSION_INVITE && gAgent.isInGroup(mSessionID)) + { + return false; + } + + if (mIsP2PChat) + { + // For a P2P session just check if we are not adding the other participant. + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + if (*id == mOtherParticipantUUID) + { + return false; + } + } + } + else + { + // For a conference session we need to check against the list from LLSpeakerMgr, + // because this list may change when participants join or leave the session. + + LLSpeakerMgr::speaker_list_t speaker_list; + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->getSpeakerList(&speaker_list, true); + } + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + for (LLSpeakerMgr::speaker_list_t::const_iterator it = speaker_list.begin(); + it != speaker_list.end(); ++it) + { + const LLPointer& speaker = *it; + if (*id == speaker->mID) + { + return false; + } + } + } + } + + return true; +} + +void LLFloaterIMSession::addSessionParticipants(const uuid_vec_t& uuids) +{ + if (mIsP2PChat) + { + LLSD payload; + LLSD args; + + LLNotificationsUtil::add("ConfirmAddingChatParticipants", args, payload, + boost::bind(&LLFloaterIMSession::addP2PSessionParticipants, this, _1, _2, uuids)); + } + else + { + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + + inviteToSession(uuids); + } +} + +void LLFloaterIMSession::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) + { + return; + } + + mStartConferenceInSameFloater = true; + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + // first check whether this is a voice session + bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); + + uuid_vec_t temp_ids; + + // Add the initial participant of a P2P session + temp_ids.push_back(mOtherParticipantUUID); + temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); + + // then we can close the current session + onClose(false); + + // we start a new session so reset the initialization flag + mSessionInitialized = false; + + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + + // Start a new ad hoc voice call if we invite new participants to a P2P call, + // or start a text chat otherwise. + if (is_voice_call) + { + LLAvatarActions::startAdhocCall(temp_ids, mSessionID); + } + else + { + LLAvatarActions::startConference(temp_ids, mSessionID); + } +} + +void LLFloaterIMSession::sendParticipantsAddedNotification(const uuid_vec_t& uuids) +{ + std::string names_string; + LLAvatarActions::buildResidentsString(uuids, names_string); + LLStringUtil::format_map_t args; + args["[NAME]"] = names_string; + + sendMsg(getString(uuids.size() > 1 ? "multiple_participants_added" : "participant_added", args)); +} + +void LLFloaterIMSession::boundVoiceChannel() +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if(voice_channel) + { + mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback( + boost::bind(&LLFloaterIMSession::onVoiceChannelStateChanged, this, _1, _2)); + + //call (either p2p, group or ad-hoc) can be already in started state + bool callIsActive = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); + } +} + +void LLFloaterIMSession::onCallButtonClicked() +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if (voice_channel) + { + bool is_call_active = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + if (is_call_active) + { + gIMMgr->endCall(mSessionID); + } + else + { + gIMMgr->startCall(mSessionID); + } + } +} + +void LLFloaterIMSession::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ + if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL) + { + enableDisableCallBtn(); + } +} + +void LLFloaterIMSession::onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) +{ + bool callIsActive = new_state >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); +} + +void LLFloaterIMSession::updateSessionName(const std::string& name) +{ + LLFloaterIMSessionTab::updateSessionName(name); + setTitle(name); + mTypingStart.setArg("[NAME]", name); +} + +//static +LLFloaterIMSession* LLFloaterIMSession::show(const LLUUID& session_id) +{ + closeHiddenIMToasts(); + + if (!gIMMgr->hasSession(session_id)) + return NULL; + + // Test the existence of the floater before we try to create it + bool exist = findInstance(session_id); + + // Get the floater: this will create the instance if it didn't exist + LLFloaterIMSession* floater = getInstance(session_id); + if (!floater) + return NULL; + + LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance(); + + // Do not add again existing floaters + if (!exist) + { + // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; + // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists + LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; + if (floater_container) + { + floater_container->addFloater(floater, TRUE, i_pt); + } + } + + floater->openFloater(floater->getKey()); + + floater->setVisible(TRUE); + + return floater; +} +//static +LLFloaterIMSession* LLFloaterIMSession::findInstance(const LLUUID& session_id) +{ + LLFloaterIMSession* conversation = + LLFloaterReg::findTypedInstance("impanel", session_id); + + return conversation; +} + +LLFloaterIMSession* LLFloaterIMSession::getInstance(const LLUUID& session_id) +{ + LLFloaterIMSession* conversation = + LLFloaterReg::getTypedInstance("impanel", session_id); + + return conversation; +} + +void LLFloaterIMSession::onClose(bool app_quitting) +{ + setTyping(false); + + // The source of much argument and design thrashing + // Should the window hide or the session close when the X is clicked? + // + // Last change: + // EXT-3516 X Button should end IM session, _ button should hide + gIMMgr->leaveSession(mSessionID); + + // Clean up the conversation *after* the session has been ended + LLFloaterIMSessionTab::onClose(app_quitting); +} + +void LLFloaterIMSession::setDocked(bool docked, bool pop_on_undock) +{ + // update notification channel state + LLNotificationsUI::LLScreenChannel* channel = static_cast + (LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); + + if(!isChatMultiTab()) + { + LLTransientDockableFloater::setDocked(docked, pop_on_undock); + } + + // update notification channel state + if(channel) + { + channel->updateShowToastsState(); + channel->redrawToasts(); + } +} + +void LLFloaterIMSession::setVisible(BOOL visible) +{ + LLNotificationsUI::LLScreenChannel* channel = static_cast + (LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); + + LLFloaterIMSessionTab::setVisible(visible); + + // update notification channel state + if(channel) + { + channel->updateShowToastsState(); + channel->redrawToasts(); + } + + if(!visible) + { + LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet(mSessionID); + if(chiclet) + { + chiclet->setToggleState(false); + } + } + + if (visible && isInVisibleChain()) + { + sIMFloaterShowedSignal(mSessionID); + + } + + setFocus(visible); +} + +BOOL LLFloaterIMSession::getVisible() +{ + bool visible; + + if(isChatMultiTab()) + { + LLFloaterIMContainer* im_container = + LLFloaterIMContainer::getInstance(); + + // Treat inactive floater as invisible. + bool is_active = im_container->getActiveFloater() == this; + + //torn off floater is always inactive + if (!is_active && getHost() != im_container) + { + visible = LLTransientDockableFloater::getVisible(); + } + else + { + // getVisible() returns TRUE when Tabbed IM window is minimized. + visible = is_active && !im_container->isMinimized() + && im_container->getVisible(); + } + } + else + { + visible = LLTransientDockableFloater::getVisible(); + } + + return visible; +} + +//static +bool LLFloaterIMSession::toggle(const LLUUID& session_id) +{ + if(!isChatMultiTab()) + { + LLFloaterIMSession* floater = LLFloaterReg::findTypedInstance( + "impanel", session_id); + if (floater && floater->getVisible() && floater->hasFocus()) + { + // clicking on chiclet to close floater just hides it to maintain existing + // scroll/text entry state + floater->setVisible(false); + return false; + } + else if(floater && (!floater->isDocked() || floater->getVisible() && !floater->hasFocus())) + { + floater->setVisible(TRUE); + floater->setFocus(TRUE); + return true; + } + } + + // ensure the list of messages is updated when floater is made visible + show(session_id); + return true; +} + +void LLFloaterIMSession::sessionInitReplyReceived(const LLUUID& im_session_id) +{ + mSessionInitialized = true; + + //will be different only for an ad-hoc im session + if (mSessionID != im_session_id) + { + initIMSession(im_session_id); + buildConversationViewParticipant(); + } + + initIMFloater(); + + //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) + + //need to send delayed messages collected while waiting for session initialization + if (mQueuedMsgsForInit.size()) + { + LLSD::array_iterator iter; + for ( iter = mQueuedMsgsForInit.beginArray(); + iter != mQueuedMsgsForInit.endArray(); ++iter) + { + LLIMModel::sendMessage(iter->asString(), mSessionID, + mOtherParticipantUUID, mDialog); + } + + mQueuedMsgsForInit.clear(); + } +} + +void LLFloaterIMSession::updateMessages() +{ + std::list messages; + + // we shouldn't reset unread message counters if IM floater doesn't have focus + LLIMModel::instance().getMessages( + mSessionID, messages, mLastMessageIndex + 1, hasFocus()); + + if (messages.size()) + { + std::ostringstream message; + std::list::const_reverse_iterator iter = messages.rbegin(); + std::list::const_reverse_iterator iter_end = messages.rend(); + for (; iter != iter_end; ++iter) + { + LLSD msg = *iter; + + std::string time = msg["time"].asString(); + LLUUID from_id = msg["from_id"].asUUID(); + std::string from = msg["from"].asString(); + std::string message = msg["message"].asString(); + bool is_history = msg["is_history"].asBoolean(); + + LLChat chat; + chat.mFromID = from_id; + chat.mSessionID = mSessionID; + chat.mFromName = from; + chat.mTimeStr = time; + chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle; + + // process offer notification + if (msg.has("notification_id")) + { + chat.mNotifId = msg["notification_id"].asUUID(); + // if notification exists - embed it + if (LLNotificationsUtil::find(chat.mNotifId) != NULL) + { + // remove embedded notification from channel + LLNotificationsUI::LLScreenChannel* channel = static_cast + (LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); + if (getVisible()) + { + // toast will be automatically closed since it is not storable toast + channel->hideToast(chat.mNotifId); + } + } + // if notification doesn't exist - try to use next message which should be log entry + else + { + continue; + } + } + //process text message + else + { + chat.mText = message; + } + + // Add the message to the chat log + appendMessage(chat); + mLastMessageIndex = msg["index"].asInteger(); + + // if it is a notification - next message is a notification history log, so skip it + if (chat.mNotifId.notNull() && LLNotificationsUtil::find(chat.mNotifId) != NULL) + { + if (++iter == iter_end) + { + break; + } + else + { + mLastMessageIndex++; + } + } + } + } +} + +void LLFloaterIMSession::reloadMessages() +{ + mChatHistory->clear(); + mLastMessageIndex = -1; + updateMessages(); + mInputEditor->setFont(LLViewerChat::getChatFont()); +} + +// static +void LLFloaterIMSession::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) +{ + LLFloaterIMSession* self= (LLFloaterIMSession*) userdata; + + // Allow enabling the LLFloaterIMSession input editor only if session can accept text + LLIMModel::LLIMSession* im_session = + LLIMModel::instance().findIMSession(self->mSessionID); + //TODO: While disabled lllineeditor can receive focus we need to check if it is enabled (EK) + if( im_session && im_session->mTextIMPossible && self->mInputEditor->getEnabled()) + { + //in disconnected state IM input editor should be disabled + self->mInputEditor->setEnabled(!gDisconnected); + } +} + +// static +void LLFloaterIMSession::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) +{ + LLFloaterIMSession* self = (LLFloaterIMSession*) userdata; + self->setTyping(false); +} + +// static +void LLFloaterIMSession::onInputEditorKeystroke(LLTextEditor* caller, void* userdata) +{ + LLFloaterIMSession* self = (LLFloaterIMSession*)userdata; + std::string text = self->mInputEditor->getText(); + + // Deleting all text counts as stopping typing. + self->setTyping(!text.empty()); +} + +void LLFloaterIMSession::setTyping(bool typing) +{ + if ( typing ) + { + // Started or proceeded typing, reset the typing timeout timer + mTypingTimeoutTimer.reset(); + } + + if ( mMeTyping != typing ) + { + // Typing state is changed + mMeTyping = typing; + // So, should send current state + mShouldSendTypingState = true; + // In case typing is started, send state after some delay + mTypingTimer.reset(); + } + + // 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 ) + { + // Still typing, send 'start typing' notification or + // send 'stop typing' notification immediately + if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f) + { + LLIMModel::instance().sendTypingState(mSessionID, + mOtherParticipantUUID, mMeTyping); + mShouldSendTypingState = false; + } + } + + if (!mIsNearbyChat) + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); + } + } +} + +void LLFloaterIMSession::processIMTyping(const LLIMInfo* im_info, BOOL typing) +{ + if ( typing ) + { + // other user started typing + addTypingIndicator(im_info); + } + else + { + // other user stopped typing + removeTypingIndicator(im_info); + } +} + +void LLFloaterIMSession::processAgentListUpdates(const LLSD& body) +{ + uuid_vec_t joined_uuids; + + if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) + { + LLSD::map_const_iterator update_it; + for(update_it = body["agent_updates"].beginMap(); + update_it != body["agent_updates"].endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + LLSD agent_data = update_it->second; + + if (agent_data.isMap()) + { + // store the new participants in joined_uuids + if (agent_data.has("transition") && agent_data["transition"].asString() == "ENTER") + { + joined_uuids.push_back(agent_id); + } + + // process the moderator mutes + if (agent_id == gAgentID && agent_data.has("info") && agent_data["info"].has("mutes")) + { + BOOL moderator_muted_text = agent_data["info"]["mutes"]["text"].asBoolean(); + mInputEditor->setEnabled(!moderator_muted_text); + std::string label; + if (moderator_muted_text) + label = LLTrans::getString("IM_muted_text_label"); + else + label = LLTrans::getString("IM_to_label") + " " + LLIMModel::instance().getName(mSessionID); + mInputEditor->setLabel(label); + + if (moderator_muted_text) + LLNotificationsUtil::add("TextChatIsMutedByModerator"); + } + } + } + } + + // the vectors need to be sorted for computing the intersection and difference + std::sort(mInvitedParticipants.begin(), mInvitedParticipants.end()); + std::sort(joined_uuids.begin(), joined_uuids.end()); + + uuid_vec_t intersection; // uuids of invited residents who have joined the conversation + std::set_intersection(mInvitedParticipants.begin(), mInvitedParticipants.end(), + joined_uuids.begin(), joined_uuids.end(), + std::back_inserter(intersection)); + + if (intersection.size() > 0) + { + sendParticipantsAddedNotification(intersection); + } + + // Remove all joined participants from invited array. + // The difference between the two vectors (the elements in mInvitedParticipants which are not in joined_uuids) + // is placed at the beginning of mInvitedParticipants, then all other elements are erased. + mInvitedParticipants.erase(std::set_difference(mInvitedParticipants.begin(), mInvitedParticipants.end(), + joined_uuids.begin(), joined_uuids.end(), + mInvitedParticipants.begin()), + mInvitedParticipants.end()); +} + +void LLFloaterIMSession::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") ) + { + BOOL voice_moderated = session_update["moderated_mode"]["voice"]; + const std::string session_label = LLIMModel::instance().getName(mSessionID); + + if (voice_moderated) + { + setTitle(session_label + std::string(" ") + + LLTrans::getString("IM_moderated_chat_label")); + } + else + { + setTitle(session_label); + } + + // *TODO : uncomment this when/if LLPanelActiveSpeakers panel will be added + //update the speakers dropdown too + //mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated); + } +} + +// virtual +BOOL LLFloaterIMSession::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + if (cargo_type == DAD_PERSON) + { + if (dropPerson(static_cast(cargo_data), drop)) + { + *accept = ACCEPT_YES_MULTI; + } + else + { + *accept = ACCEPT_NO; + } + } + else if (mDialog == IM_NOTHING_SPECIAL) + { + LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, + cargo_type, cargo_data, accept); + } + + return TRUE; +} + +bool LLFloaterIMSession::dropPerson(LLUUID* person_id, bool drop) +{ + bool res = person_id && person_id->notNull(); + if(res) + { + uuid_vec_t ids; + ids.push_back(*person_id); + + res = canAddSelectedToChat(ids); + if(res && drop) + { + addSessionParticipants(ids); + } + } + + return res; +} + +BOOL LLFloaterIMSession::isInviteAllowed() const +{ + return ( (IM_SESSION_CONFERENCE_START == mDialog) + || (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mIsP2PChat); +} + +class LLSessionInviteResponder : public LLHTTPClient::Responder +{ +public: + LLSessionInviteResponder(const LLUUID& session_id) + { + mSessionID = session_id; + } + + void error(U32 statusNum, const std::string& reason) + { + llinfos << "Error inviting all agents to session" << llendl; + //throw something back to the viewer here? + } + +private: + LLUUID mSessionID; +}; + +BOOL LLFloaterIMSession::inviteToSession(const uuid_vec_t& ids) +{ + LLViewerRegion* region = gAgent.getRegion(); + bool is_region_exist = region != NULL; + + if (is_region_exist) + { + S32 count = ids.size(); + + if( isInviteAllowed() && (count > 0) ) + { + llinfos << "LLFloaterIMSession::inviteToSession() - inviting participants" << llendl; + + std::string url = region->getCapability("ChatSessionRequest"); + + LLSD data; + data["params"] = LLSD::emptyArray(); + for (int i = 0; i < count; i++) + { + data["params"].append(ids[i]); + } + data["method"] = "invite"; + data["session-id"] = mSessionID; + LLHTTPClient::post(url, data,new LLSessionInviteResponder(mSessionID)); + } + else + { + llinfos << "LLFloaterIMSession::inviteToSession -" + << " no need to invite agents for " + << mDialog << llendl; + // successful add, because everyone that needed to get added + // was added. + } + } + + return is_region_exist; +} + +void LLFloaterIMSession::addTypingIndicator(const LLIMInfo* im_info) +{ + // We may have lost a "stop-typing" packet, don't add it twice + if ( im_info && !mOtherTyping ) + { + mOtherTyping = true; + + // Save and set new title + mSavedTitle = getTitle(); + setTitle (mTypingStart); + + // Update speaker + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if ( speaker_mgr ) + { + speaker_mgr->setSpeakerTyping(im_info->mFromID, TRUE); + } + } +} + +void LLFloaterIMSession::removeTypingIndicator(const LLIMInfo* im_info) +{ + if ( mOtherTyping ) + { + mOtherTyping = false; + + // Revert the title to saved one + setTitle(mSavedTitle); + + if ( im_info ) + { + // Update speaker + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if ( speaker_mgr ) + { + speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE); + } + } + } +} + +// static +void LLFloaterIMSession::closeHiddenIMToasts() +{ + class IMToastMatcher: public LLNotificationsUI::LLScreenChannel::Matcher + { + public: + bool matches(const LLNotificationPtr notification) const + { + // "notifytoast" type of notifications is reserved for IM notifications + return "notifytoast" == notification->getType(); + } + }; + + LLNotificationsUI::LLScreenChannel* channel = + LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); + if (channel != NULL) + { + channel->closeHiddenToasts(IMToastMatcher()); + } +} +// static +void LLFloaterIMSession::confirmLeaveCallCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + const LLSD& payload = notification["payload"]; + LLUUID session_id = payload["session_id"]; + + LLFloater* im_floater = findInstance(session_id); + if (option == 0 && im_floater != NULL) + { + im_floater->closeFloater(); + } + + return; +} + +// static +void LLFloaterIMSession::sRemoveTypingIndicator(const LLSD& data) +{ + LLUUID session_id = data["session_id"]; + if (session_id.isNull()) + return; + + LLUUID from_id = data["from_id"]; + if (gAgentID == from_id || LLUUID::null == from_id) + return; + + LLFloaterIMSession* floater = LLFloaterIMSession::findInstance(session_id); + if (!floater) + return; + + if (IM_NOTHING_SPECIAL != floater->mDialog) + return; + + floater->removeTypingIndicator(); +} + +// static +void LLFloaterIMSession::onIMChicletCreated( const LLUUID& session_id ) +{ + LLFloaterIMSession::addToHost(session_id); +} + +boost::signals2::connection LLFloaterIMSession::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb) +{ + return LLFloaterIMSession::sIMFloaterShowedSignal.connect(cb); +} diff --git a/indra/newview/llfloaterimsession.h b/indra/newview/llfloaterimsession.h new file mode 100644 index 0000000000..f4ec2d457d --- /dev/null +++ b/indra/newview/llfloaterimsession.h @@ -0,0 +1,196 @@ +/** + * @file llfloaterimsession.h + * @brief LLFloaterIMSession class definition + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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_FLOATERIMSESSION_H +#define LL_FLOATERIMSESSION_H + +#include "llimview.h" +#include "llfloaterimsessiontab.h" +#include "llinstantmessage.h" +#include "lllogchat.h" +#include "lltooldraganddrop.h" +#include "llvoicechannel.h" +#include "llvoiceclient.h" + +class LLAvatarName; +class LLButton; +class LLChatEntry; +class LLTextEditor; +class LLPanelChatControlPanel; +class LLChatHistory; +class LLInventoryItem; +class LLInventoryCategory; + +typedef boost::signals2::signal floater_showed_signal_t; + +/** + * Individual IM window that appears at the bottom of the screen, + * optionally "docked" to the bottom tray. + */ +class LLFloaterIMSession + : public LLVoiceClientStatusObserver + , public LLFloaterIMSessionTab +{ + LOG_CLASS(LLFloaterIMSession); +public: + LLFloaterIMSession(const LLUUID& session_id); + + virtual ~LLFloaterIMSession(); + + void initIMSession(const LLUUID& session_id); + void initIMFloater(); + + // LLView overrides + /*virtual*/ BOOL postBuild(); + /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ BOOL getVisible(); + // Check typing timeout timer. + + static LLFloaterIMSession* findInstance(const LLUUID& session_id); + static LLFloaterIMSession* getInstance(const LLUUID& session_id); + + // LLFloater overrides + /*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 LLFloaterIMSession* show(const LLUUID& session_id); + + // Toggle panel specified by session_id + // Returns true iff panel became visible + static bool toggle(const LLUUID& session_id); + + void sessionInitReplyReceived(const LLUUID& im_session_id); + + // get new messages from LLIMModel + /*virtual*/ void updateMessages(); + void reloadMessages(); + static void onSendMsg(LLUICtrl*, void*); + void sendMsgFromInputEditor(); + void sendMsg(const std::string& msg); + + // callback for LLIMModel on new messages + // route to specific floater if it is visible + static void newIMCallback(const LLSD& data); + + // called when docked floater's position has been set by chiclet + void setPositioned(bool b) { mPositioned = b; }; + + void onVisibilityChange(const LLSD& new_visibility); + + // Implements LLVoiceClientStatusObserver::onChange() to enable the call + // button when voice is available + 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); + + void processIMTyping(const LLIMInfo* im_info, BOOL typing); + void processAgentListUpdates(const LLSD& body); + void processSessionUpdate(const LLSD& session_update); + + /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + + + //used as a callback on receiving new IM message + static void sRemoveTypingIndicator(const LLSD& data); + static void onIMChicletCreated(const LLUUID& session_id); + + bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; } + const LLUUID& getOtherParticipantUUID() {return mOtherParticipantUUID;} + + static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb); + static floater_showed_signal_t sIMFloaterShowedSignal; + +private: + + /*virtual*/ void refresh(); + + /*virtual*/ void onClickCloseBtn(); + + // Update the window title and input field help text + /*virtual*/ void updateSessionName(const std::string& name); + + bool dropPerson(LLUUID* person_id, bool drop); + + BOOL isInviteAllowed() const; + BOOL inviteToSession(const uuid_vec_t& agent_ids); + static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); + static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); + static void onInputEditorKeystroke(LLTextEditor* caller, void* userdata); + void setTyping(bool typing); + void onAddButtonClicked(); + void addSessionParticipants(const uuid_vec_t& uuids); + void addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids); + void sendParticipantsAddedNotification(const uuid_vec_t& uuids); + bool canAddSelectedToChat(const uuid_vec_t& uuids); + + void onCallButtonClicked(); + + void boundVoiceChannel(); + + // Add the "User is typing..." indicator. + void addTypingIndicator(const LLIMInfo* im_info); + + // Remove the "User is typing..." indicator. + void removeTypingIndicator(const LLIMInfo* im_info = NULL); + + static void closeHiddenIMToasts(); + + static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); + + S32 mLastMessageIndex; + + EInstantMessage mDialog; + LLUUID mOtherParticipantUUID; + bool mPositioned; + + std::string mSavedTitle; + LLUIString mTypingStart; + bool mMeTyping; + bool mOtherTyping; + bool mShouldSendTypingState; + LLFrameTimer mTypingTimer; + LLFrameTimer mTypingTimeoutTimer; + + bool mSessionInitialized; + LLSD mQueuedMsgsForInit; + + bool mStartConferenceInSameFloater; + + uuid_vec_t mInvitedParticipants; + + // connection to voice channel state change signal + boost::signals2::connection mVoiceChannelStateChangeConnection; +}; + +#endif // LL_FLOATERIMSESSION_H diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp new file mode 100644 index 0000000000..a47c9177a1 --- /dev/null +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -0,0 +1,743 @@ +/** + * @file llfloaterimsessiontab.cpp + * @brief LLFloaterIMSessionTab class implements the common behavior of LNearbyChatBar + * @brief and LLFloaterIMSession 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 "llviewerprecompiledheaders.h" + +#include "llfloaterimsessiontab.h" + +#include "llagent.h" +#include "llavataractions.h" +#include "llchatentry.h" +#include "llchathistory.h" +#include "llchiclet.h" +#include "llchicletbar.h" +#include "lldraghandle.h" +#include "llfloaterreg.h" +#include "llfloaterimsession.h" +#include "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container +#include "lllayoutstack.h" +#include "llfloaterimnearbychat.h" + +const F32 REFRESH_INTERVAL = 0.2f; + +LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id) + : LLTransientDockableFloater(NULL, true, session_id) + , mIsP2PChat(false) + , mExpandCollapseBtn(NULL) + , mTearOffBtn(NULL) + , mCloseBtn(NULL) + , mSessionID(session_id.asUUID()) + , mConversationsRoot(NULL) + , mChatHistory(NULL) + , mInputEditor(NULL) + , mInputEditorTopPad(0) + , mRefreshTimer(new LLTimer()) + , mIsHostAttached(false) +{ + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + + mCommitCallbackRegistrar.add("IMSession.Menu.Action", + boost::bind(&LLFloaterIMSessionTab::onIMSessionMenuItemClicked, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem", + boost::bind(&LLFloaterIMSessionTab::onIMCompactExpandedMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.CheckItem", + boost::bind(&LLFloaterIMSessionTab::onIMShowModesMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.Enable", + boost::bind(&LLFloaterIMSessionTab::onIMShowModesMenuItemEnable, this, _2)); + + // Zero expiry time is set only once to allow initial update. + mRefreshTimer->setTimerExpirySec(0); + mRefreshTimer->start(); +} + +LLFloaterIMSessionTab::~LLFloaterIMSessionTab() +{ + delete mRefreshTimer; +} + +//static +LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uuid) +{ + LLFloaterIMSessionTab* conv; + + if (uuid.isNull()) + { + conv = LLFloaterReg::findTypedInstance("nearby_chat"); + } + else + { + conv = LLFloaterReg::findTypedInstance("impanel", LLSD(uuid)); + } + + return conv; +}; + +//static +LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid) +{ + LLFloaterIMSessionTab* conv; + + if (uuid.isNull()) + { + conv = LLFloaterReg::getTypedInstance("nearby_chat"); + } + else + { + conv = LLFloaterReg::getTypedInstance("impanel", LLSD(uuid)); + } + + return conv; +}; + +void LLFloaterIMSessionTab::setVisible(BOOL visible) +{ + LLTransientDockableFloater::setVisible(visible); + + if(visible) + { + LLFloaterIMSessionTab::addToHost(mSessionID); + } + setFocus(visible); +} + +/*virtual*/ +void LLFloaterIMSessionTab::setFocus(BOOL focus) +{ + LLTransientDockableFloater::setFocus(focus); + + //Redirect focus to input editor + if (focus) + { + updateMessages(); + + if (mInputEditor) + { + mInputEditor->setFocus(TRUE); + } + } +} + + +void LLFloaterIMSessionTab::addToHost(const LLUUID& session_id) +{ + if ((session_id.notNull() && !gIMMgr->hasSession(session_id)) + || !LLFloaterIMSessionTab::isChatMultiTab()) + { + return; + } + + // Get the floater: this will create the instance if it didn't exist + LLFloaterIMSessionTab* conversp = LLFloaterIMSessionTab::getConversation(session_id); + if (conversp) + { + LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance(); + + // Do not add again existing floaters + if (floater_container && !conversp->isHostAttached()) + { + conversp->setHostAttached(true); + + if (!conversp->isNearbyChat() + || gSavedSettings.getBOOL("NearbyChatIsNotTornOff")) + { + floater_container->addFloater(conversp, TRUE, LLTabContainer::END); + } + else + { + // setting of the "potential" host for Nearby Chat: this sequence sets + // LLFloater::mHostHandle = NULL (a current host), but + // LLFloater::mLastHostHandle = floater_container (a "future" host) + conversp->setHost(floater_container); + conversp->setHost(NULL); + } + // Added floaters share some state (like sort order) with their host + conversp->setSortOrder(floater_container->getSortOrder()); + } + } +} + +BOOL LLFloaterIMSessionTab::postBuild() +{ + BOOL result; + + mCloseBtn = getChild("close_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + + mExpandCollapseBtn = getChild("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onSlide, this)); + + mTearOffBtn = getChild("tear_off_btn"); + mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this)); + + mParticipantListPanel = getChild("speakers_list_panel"); + + // Create a root view folder for all participants + LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel); + LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); + p.rect = LLRect(0, 0, getRect().getWidth(), 0); + p.parent_panel = mParticipantListPanel; + p.listener = base_item; + p.view_model = &mConversationViewModel; + p.root = NULL; + p.use_ellipses = true; + mConversationsRoot = LLUICtrlFactory::create(p); + mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); + + // Add a scroller for the folder (participant) view + LLRect scroller_view_rect = mParticipantListPanel->getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams()); + scroller_params.rect(scroller_view_rect); + LLScrollContainer* scroller = LLUICtrlFactory::create(scroller_params); + scroller->setFollowsAll(); + + // Insert that scroller into the panel widgets hierarchy and folder view + mParticipantListPanel->addChild(scroller); + scroller->addChild(mConversationsRoot); + mConversationsRoot->setScrollContainer(scroller); + mConversationsRoot->setFollowsAll(); + mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox); + + + mChatHistory = getChild("chat_history"); + + mInputEditor = getChild("chat_editor"); + mInputEditor->setTextExpandedCallback(boost::bind(&LLFloaterIMSessionTab::reshapeChatHistory, this)); + mInputEditor->setCommitOnFocusLost( FALSE ); + mInputEditor->setPassDelete(TRUE); + mInputEditor->setFont(LLViewerChat::getChatFont()); + + mInputEditorTopPad = mChatHistory->getRect().mBottom - mInputEditor->getRect().mTop; + + setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); + + buildConversationViewParticipant(); + + mSaveRect = isTornOff(); + initRectControl(); + + if (isChatMultiTab()) + { + result = LLFloater::postBuild(); + } + else + { + result = LLDockableFloater::postBuild(); + } + + refreshConversation(); + + return result; +} + +LLParticipantList* LLFloaterIMSessionTab::getParticipantList() +{ + return dynamic_cast(LLFloaterIMContainer::getInstance()->getSessionModel(mSessionID)); +} + +void LLFloaterIMSessionTab::draw() +{ + if (mRefreshTimer->hasExpired()) + { + if (getParticipantList()) + { + getParticipantList()->update(); + } + + refreshConversation(); + + // Restart the refresh timer + mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL); + } + + LLTransientDockableFloater::draw(); +} + +void LLFloaterIMSessionTab::enableDisableCallBtn() +{ + getChildView("voice_call_btn")->setEnabled( + mSessionID.notNull() + && mSession + && mSession->mSessionInitialized + && LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking() + && mSession->mCallBackEnabled); +} + +void LLFloaterIMSessionTab::onFocusReceived() +{ + setBackgroundOpaque(true); + + if (mSessionID.notNull() && isInVisibleChain()) + { + LLIMModel::instance().sendNoUnreadMessages(mSessionID); + } + + LLTransientDockableFloater::onFocusReceived(); + + LLFloaterIMContainer* container = LLFloaterReg::getTypedInstance("im_container"); + if (container) + { + container->selectConversationPair(mSessionID, true); + } +} + +void LLFloaterIMSessionTab::onFocusLost() +{ + setBackgroundOpaque(false); + LLTransientDockableFloater::onFocusLost(); +} + +std::string LLFloaterIMSessionTab::appendTime() +{ + time_t utc_time; + utc_time = time_corrected(); + std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" + +LLTrans::getString("TimeMin")+"]"; + + LLSD substitution; + + substitution["datetime"] = (S32) utc_time; + LLStringUtil::format (timeStr, substitution); + + return timeStr; +} + +void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD &args) +{ + // Update the participant activity time + LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance(); + if (im_box) + { + im_box->setTimeNow(mSessionID,chat.mFromID); + } + + + LLChat& tmp_chat = const_cast(chat); + + if(tmp_chat.mTimeStr.empty()) + tmp_chat.mTimeStr = appendTime(); + + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = 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"] = + !mIsP2PChat || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + + if (mChatHistory) + { + mChatHistory->appendMessage(chat, chat_args); + } + } +} + + +void LLFloaterIMSessionTab::buildConversationViewParticipant() +{ + // Clear the widget list since we are rebuilding afresh from the model + conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + while (widget_it != mConversationsWidgets.end()) + { + removeConversationViewParticipant(widget_it->first); + // Iterators are invalidated by erase so we need to pick begin again + widget_it = mConversationsWidgets.begin(); + } + + // Get the model list + LLParticipantList* item = getParticipantList(); + if (!item) + { + // Nothing to do if the model list is empty + return; + } + + // Create the participants widgets now + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItem* participant_model = dynamic_cast(*current_participant_model); + addConversationViewParticipant(participant_model); + current_participant_model++; + } +} + +void LLFloaterIMSessionTab::addConversationViewParticipant(LLConversationItem* participant_model) +{ + // Check if the model already has an associated view + LLUUID uuid = participant_model->getUUID(); + LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid); + + // If not already present, create the participant view and attach it to the root, otherwise, just refresh it + if (widget) + { + updateConversationViewParticipant(uuid); // overkill? + } + else + { + LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); + mConversationsWidgets[uuid] = participant_view; + participant_view->addToFolder(mConversationsRoot); + participant_view->setVisible(TRUE); + refreshConversation(); + } +} + +void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& participant_id) +{ + LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); + if (widget) + { + mConversationsRoot->extractItem(widget); + delete widget; + mConversationsWidgets.erase(participant_id); + refreshConversation(); + } +} + +void LLFloaterIMSessionTab::updateConversationViewParticipant(const LLUUID& participant_id) +{ + LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); + if (widget) + { + widget->refresh(); + } + refreshConversation(); +} + +void LLFloaterIMSessionTab::refreshConversation() +{ + // Note: We collect participants names to change the session name only in the case of ad-hoc conversations + bool is_ad_hoc = (mSession ? mSession->isAdHocSessionType() : false); + uuid_vec_t participants_uuids; // uuids vector for building the added participants name string + // For P2P chat, we still need to update the session name who may have changed (switch display name for instance) + if (mIsP2PChat && mSession) + { + participants_uuids.push_back(mSession->mOtherParticipantID); + } + + conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + while (widget_it != mConversationsWidgets.end()) + { + // Add the participant to the list except if it's the agent itself (redundant) + if (is_ad_hoc && (widget_it->first != gAgentID)) + { + participants_uuids.push_back(widget_it->first); + } + widget_it->second->refresh(); + widget_it->second->setVisible(TRUE); + ++widget_it; + } + if (is_ad_hoc || mIsP2PChat) + { + // Build the session name and update it + std::string session_name; + if (participants_uuids.size() != 0) + { + LLAvatarActions::buildResidentsString(participants_uuids, session_name); + } + else + { + session_name = LLIMModel::instance().getName(mSessionID); + } + updateSessionName(session_name); + } + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + mConversationsRoot->update(); + updateHeaderAndToolbar(); + refresh(); +} + +// Copied from LLFloaterIMContainer::createConversationViewParticipant(). Refactor opportunity! +LLConversationViewParticipant* LLFloaterIMSessionTab::createConversationViewParticipant(LLConversationItem* item) +{ + LLRect panel_rect = mParticipantListPanel->getRect(); + + LLConversationViewParticipant::Params params; + params.name = item->getDisplayName(); + params.root = mConversationsRoot; + params.listener = item; + params.rect = LLRect (0, 24, panel_rect.getWidth(), 0); // *TODO: use conversation_view_participant.xml itemHeight value in lieu of 24 + params.tool_tip = params.name; + params.participant_id = item->getUUID(); + + return LLUICtrlFactory::create(params); +} + +void LLFloaterIMSessionTab::setSortOrder(const LLConversationSort& order) +{ + mConversationViewModel.setSorter(order); + mConversationsRoot->arrangeAll(); + refreshConversation(); +} + +void LLFloaterIMSessionTab::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); + } + + LLFloaterIMSessionTab::processChatHistoryStyleUpdate(); +} + + +bool LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::hideOrShowTitle() +{ + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + LLView* floater_contents = getChild("contents_view"); + + LLRect floater_rect = getLocalRect(); + S32 top_border_of_contents = floater_rect.mTop - (isTornOff()? floater_header_size : 0); + LLRect handle_rect (0, floater_rect.mTop, floater_rect.mRight, top_border_of_contents); + LLRect contents_rect (0, top_border_of_contents, floater_rect.mRight, floater_rect.mBottom); + mDragHandle->setShape(handle_rect); + mDragHandle->setVisible(isTornOff()); + floater_contents->setShape(contents_rect); +} + +void LLFloaterIMSessionTab::updateSessionName(const std::string& name) +{ + mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + name); +} + +void LLFloaterIMSessionTab::hideAllStandardButtons() +{ + 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); + } + } +} + +void LLFloaterIMSessionTab::updateHeaderAndToolbar() +{ + // prevent start conversation before its container + LLFloaterIMContainer::getInstance(); + + bool is_torn_off = checkIfTornOff(); + if (!is_torn_off) + { + hideAllStandardButtons(); + } + + hideOrShowTitle(); + + // Participant list should be visible only in torn off floaters. + bool is_participant_list_visible = + is_torn_off + && gSavedSettings.getBOOL("IMShowControlPanel") + && !mIsP2PChat; + + mParticipantListPanel->setVisible(is_participant_list_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_torn_off || is_participant_list_visible; + mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); + + // toggle floater's drag handle and title visibility + if (mDragHandle) + { + mDragHandle->setTitleVisible(is_torn_off); + } + + // The button (>>) should be disabled for torn off P2P conversations. + mExpandCollapseBtn->setEnabled(!is_torn_off || !mIsP2PChat); + + mTearOffBtn->setImageOverlay(getString(is_torn_off? "return_icon" : "tear_off_icon")); + mTearOffBtn->setToolTip(getString(!is_torn_off? "tooltip_to_separate_window" : "tooltip_to_main_window")); + + mCloseBtn->setVisible(!is_torn_off && !mIsNearbyChat); + + enableDisableCallBtn(); + + showTranslationCheckbox(); +} + +void LLFloaterIMSessionTab::reshapeChatHistory() +{ + LLRect chat_rect = mChatHistory->getRect(); + LLRect input_rect = mInputEditor->getRect(); + + int delta_height = chat_rect.mBottom - (input_rect.mTop + mInputEditorTopPad); + + chat_rect.setLeftTopAndSize(chat_rect.mLeft, chat_rect.mTop, chat_rect.getWidth(), chat_rect.getHeight() + delta_height); + mChatHistory->setShape(chat_rect); +} + +void LLFloaterIMSessionTab::showTranslationCheckbox(BOOL show) +{ + getChild("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); +} + +// static +void LLFloaterIMSessionTab::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) + { + LLFloaterIMSession* floater = dynamic_cast(*iter); + if (floater) + { + floater->reloadMessages(); + } + } + + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (nearby_chat) + { + nearby_chat->reloadMessages(); + } +} + +void LLFloaterIMSessionTab::updateCallBtnState(bool callIsActive) +{ + getChild("voice_call_btn")->setImageOverlay( + callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + enableDisableCallBtn(); + +} + +void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self) +{ + LLFloaterIMContainer* host_floater = dynamic_cast(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->mIsP2PChat) + { + bool expand = !self->mParticipantListPanel->getVisible(); + + // Expand/collapse the IM control panel + self->mParticipantListPanel->setVisible(expand); + + gSavedSettings.setBOOL("IMShowControlPanel", expand); + + self->mExpandCollapseBtn->setImageOverlay(self->getString(expand ? "collapse_icon" : "expand_icon")); + } + } +} + +/*virtual*/ +void LLFloaterIMSessionTab::onOpen(const LLSD& key) +{ + if (!checkIfTornOff()) + { + LLFloaterIMContainer* host_floater = dynamic_cast(getHost()); + // Show the messages pane when opening a floater hosted in the Conversations + host_floater->collapseMessagesPane(false); + } +} + +// virtual +void LLFloaterIMSessionTab::onClose(bool app_quitting) +{ + // Always suppress the IM from the conversations list on close if present for any reason + if (LLFloaterIMSessionTab::isChatMultiTab()) + { + LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance(); + if (im_box) + { + im_box->removeConversationListItem(mKey); + } + } +} + +void LLFloaterIMSessionTab::onTearOffClicked() +{ + setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE); + mSaveRect = isTornOff(); + initRectControl(); + LLFloater::onClickTearOff(this); + refreshConversation(); +} + +// static +bool LLFloaterIMSessionTab::isChatMultiTab() +{ + // Restart is required in order to change chat window type. + return true; +} + +bool LLFloaterIMSessionTab::checkIfTornOff() +{ + bool isTorn = !getHost(); + + if (isTorn != isTornOff()) + { + setTornOff(isTorn); + refreshConversation(); + } + + return isTorn; +} diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h new file mode 100644 index 0000000000..94854ee9ee --- /dev/null +++ b/indra/newview/llfloaterimsessiontab.h @@ -0,0 +1,176 @@ +/** + * @file llfloaterimsessiontab.h + * @brief LLFloaterIMSessionTab class implements the common behavior of LNearbyChatBar + * @brief and LLFloaterIMSession 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_FLOATERIMSESSIONTAB_H +#define LL_FLOATERIMSESSIONTAB_H + +#include "lllayoutstack.h" +#include "llparticipantlist.h" +#include "lltransientdockablefloater.h" +#include "llviewercontrol.h" +#include "lleventtimer.h" +#include "llimview.h" +#include "llconversationmodel.h" +#include "llconversationview.h" +#include "lltexteditor.h" + +class LLPanelChatControlPanel; +class LLChatEntry; +class LLChatHistory; + +class LLFloaterIMSessionTab + : public LLTransientDockableFloater +{ + +public: + LOG_CLASS(LLFloaterIMSessionTab); + + LLFloaterIMSessionTab(const LLSD& session_id); + ~LLFloaterIMSessionTab(); + + // 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(); + + // add conversation to container + static void addToHost(const LLUUID& session_id); + + bool isHostAttached() {return mIsHostAttached;} + void setHostAttached(bool is_attached) {mIsHostAttached = is_attached;} + + static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid); + static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid); + + // show/hide the translation check box + void showTranslationCheckbox(const BOOL visible = FALSE); + + bool isNearbyChat() {return mIsNearbyChat;} + + // LLFloater overrides + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setFocus(BOOL focus); + + // Handle the left hand participant list widgets + void addConversationViewParticipant(LLConversationItem* item); + void removeConversationViewParticipant(const LLUUID& participant_id); + void updateConversationViewParticipant(const LLUUID& participant_id); + void refreshConversation(); + void buildConversationViewParticipant(); + + void setSortOrder(const LLConversationSort& order); + + virtual void updateMessages() {} + +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(LLFloaterIMSessionTab *self); + virtual void onTearOffClicked(); + + // refresh a visual state of the Call button + void updateCallBtnState(bool callIsActive); + + void hideOrShowTitle(); // toggle the floater's drag handle + void hideAllStandardButtons(); + + /// Update floater header and toolbar buttons when hosted/torn off state is toggled. + void updateHeaderAndToolbar(); + + // Update the input field help text and other places that need the session name + virtual void updateSessionName(const std::string& name); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + + // process focus events to set a currently active session + /* virtual */ void onFocusLost(); + /* virtual */ void onFocusReceived(); + + // prepare chat's params and out one message to chatHistory + void appendMessage(const LLChat& chat, const LLSD &args = 0); + + std::string appendTime(); + + bool mIsNearbyChat; + bool mIsP2PChat; + + LLIMModel::LLIMSession* mSession; + + // Participants list: model and view + LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item); + + LLUUID mSessionID; + LLLayoutPanel* mParticipantListPanel; // add the widgets to that see mConversationsListPanel + LLParticipantList* getParticipantList(); + conversations_widgets_map mConversationsWidgets; + LLConversationViewModel mConversationViewModel; + LLFolderView* mConversationsRoot; + + LLChatHistory* mChatHistory; + LLChatEntry* mInputEditor; + int mInputEditorTopPad; // padding between input field and chat history + + LLButton* mExpandCollapseBtn; + LLButton* mTearOffBtn; + LLButton* mCloseBtn; + +private: + /// Refreshes the floater at a constant rate. + virtual void refresh() = 0; + + /** + * Adjusts chat history height to fit vertically with input chat field + * and avoid overlapping, since input chat field can be vertically expanded. + * Implementation: chat history bottom "follows" top+top_pad of input chat field + */ + void reshapeChatHistory(); + + bool checkIfTornOff(); + bool mIsHostAttached; + + LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. +}; + + +#endif /* LL_FLOATERIMSESSIONTAB_H */ diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index b60af1a635..7c5e0776a7 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -51,11 +51,11 @@ #include "llfloaterabout.h" #include "llfloaterhardwaresettings.h" #include "llfloatersidepanelcontainer.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" #include "llkeyboard.h" #include "llmodaldialog.h" #include "llnavigationbar.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnotificationtemplate.h" @@ -425,7 +425,7 @@ void LLFloaterPreference::saveAvatarProperties( void ) BOOL LLFloaterPreference::postBuild() { - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); + gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged)); diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 29d7732a68..6a9236ce0c 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -29,7 +29,7 @@ #include "llfloatertranslationsettings.h" // Viewer includes -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "lltranslate.h" #include "llviewercontrol.h" // for gSavedSettings @@ -293,7 +293,7 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - (LLFloaterReg::getTypedInstance("nearby_chat"))-> + (LLFloaterReg::getTypedInstance("nearby_chat"))-> showTranslationCheckbox(LLTranslate::isTranslationConfigured()); closeFloater(false); } diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 0996af6125..f307505ff8 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -52,7 +52,7 @@ #include "llviewermessage.h" #include "llvoavatarself.h" #include "llviewerstats.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llappearancemgr.h" #include "llgesturelistener.h" @@ -998,7 +998,7 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - (LLFloaterReg::getTypedInstance("nearby_chat"))-> + (LLFloaterReg::getTypedInstance("nearby_chat"))-> sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); gesture->mCurrentStep++; diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 15eca39bce..a0f2918bd7 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -36,7 +36,7 @@ #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llgroupmgr.h" -#include "llimfloatercontainer.h" +#include "llfloaterimcontainer.h" #include "llimview.h" // for gIMMgr #include "llnotificationsutil.h" #include "llstatusbar.h" // can_afford_transaction() @@ -335,7 +335,7 @@ LLUUID LLGroupActions::startIM(const LLUUID& group_id) group_id); if (session_id != LLUUID::null) { - LLIMFloaterContainer::getInstance()->showConversation(session_id); + LLFloaterIMContainer::getInstance()->showConversation(session_id); } make_ui_sound("UISndStartIM"); return session_id; diff --git a/indra/newview/llgroupiconctrl.cpp b/indra/newview/llgroupiconctrl.cpp index 2f9810775b..188c4bcf25 100644 --- a/indra/newview/llgroupiconctrl.cpp +++ b/indra/newview/llgroupiconctrl.cpp @@ -38,7 +38,7 @@ #include "llcachename.h" #include "llagentdata.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" */ static LLDefaultChildRegistry::Register g_i("group_icon"); diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp deleted file mode 100644 index a321b3545a..0000000000 --- a/indra/newview/llimconversation.cpp +++ /dev/null @@ -1,743 +0,0 @@ -/** - * @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 "llviewerprecompiledheaders.h" - -#include "llimconversation.h" - -#include "llagent.h" -#include "llavataractions.h" -#include "llchatentry.h" -#include "llchathistory.h" -#include "llchiclet.h" -#include "llchicletbar.h" -#include "lldraghandle.h" -#include "llfloaterreg.h" -#include "llimfloater.h" -#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container -#include "lllayoutstack.h" -#include "llnearbychat.h" - -const F32 REFRESH_INTERVAL = 0.2f; - -LLIMConversation::LLIMConversation(const LLSD& session_id) - : LLTransientDockableFloater(NULL, true, session_id) - , mIsP2PChat(false) - , mExpandCollapseBtn(NULL) - , mTearOffBtn(NULL) - , mCloseBtn(NULL) - , mSessionID(session_id.asUUID()) - , mConversationsRoot(NULL) - , mChatHistory(NULL) - , mInputEditor(NULL) - , mInputEditorTopPad(0) - , mRefreshTimer(new LLTimer()) - , mIsHostAttached(false) -{ - mSession = LLIMModel::getInstance()->findIMSession(mSessionID); - - mCommitCallbackRegistrar.add("IMSession.Menu.Action", - boost::bind(&LLIMConversation::onIMSessionMenuItemClicked, this, _2)); - 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)); - - // Zero expiry time is set only once to allow initial update. - mRefreshTimer->setTimerExpirySec(0); - mRefreshTimer->start(); -} - -LLIMConversation::~LLIMConversation() -{ - delete mRefreshTimer; -} - -//static -LLIMConversation* LLIMConversation::findConversation(const LLUUID& uuid) -{ - LLIMConversation* conv; - - if (uuid.isNull()) - { - conv = LLFloaterReg::findTypedInstance("nearby_chat"); - } - else - { - conv = LLFloaterReg::findTypedInstance("impanel", LLSD(uuid)); - } - - return conv; -}; - -//static -LLIMConversation* LLIMConversation::getConversation(const LLUUID& uuid) -{ - LLIMConversation* conv; - - if (uuid.isNull()) - { - conv = LLFloaterReg::getTypedInstance("nearby_chat"); - } - else - { - conv = LLFloaterReg::getTypedInstance("impanel", LLSD(uuid)); - } - - return conv; -}; - -void LLIMConversation::setVisible(BOOL visible) -{ - LLTransientDockableFloater::setVisible(visible); - - if(visible) - { - LLIMConversation::addToHost(mSessionID); - } - setFocus(visible); -} - -/*virtual*/ -void LLIMConversation::setFocus(BOOL focus) -{ - LLTransientDockableFloater::setFocus(focus); - - //Redirect focus to input editor - if (focus) - { - updateMessages(); - - if (mInputEditor) - { - mInputEditor->setFocus(TRUE); - } - } -} - - -void LLIMConversation::addToHost(const LLUUID& session_id) -{ - if ((session_id.notNull() && !gIMMgr->hasSession(session_id)) - || !LLIMConversation::isChatMultiTab()) - { - return; - } - - // Get the floater: this will create the instance if it didn't exist - LLIMConversation* conversp = LLIMConversation::getConversation(session_id); - if (conversp) - { - LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); - - // Do not add again existing floaters - if (floater_container && !conversp->isHostAttached()) - { - conversp->setHostAttached(true); - - if (!conversp->isNearbyChat() - || gSavedSettings.getBOOL("NearbyChatIsNotTornOff")) - { - floater_container->addFloater(conversp, TRUE, LLTabContainer::END); - } - else - { - // setting of the "potential" host for Nearby Chat: this sequence sets - // LLFloater::mHostHandle = NULL (a current host), but - // LLFloater::mLastHostHandle = floater_container (a "future" host) - conversp->setHost(floater_container); - conversp->setHost(NULL); - } - // Added floaters share some state (like sort order) with their host - conversp->setSortOrder(floater_container->getSortOrder()); - } - } -} - -BOOL LLIMConversation::postBuild() -{ - BOOL result; - - mCloseBtn = getChild("close_btn"); - mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); - - mExpandCollapseBtn = getChild("expand_collapse_btn"); - mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMConversation::onSlide, this)); - - mTearOffBtn = getChild("tear_off_btn"); - mTearOffBtn->setCommitCallback(boost::bind(&LLIMConversation::onTearOffClicked, this)); - - mParticipantListPanel = getChild("speakers_list_panel"); - - // Create a root view folder for all participants - LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel); - LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); - p.rect = LLRect(0, 0, getRect().getWidth(), 0); - p.parent_panel = mParticipantListPanel; - p.listener = base_item; - p.view_model = &mConversationViewModel; - p.root = NULL; - p.use_ellipses = true; - mConversationsRoot = LLUICtrlFactory::create(p); - mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); - - // Add a scroller for the folder (participant) view - LLRect scroller_view_rect = mParticipantListPanel->getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams()); - scroller_params.rect(scroller_view_rect); - LLScrollContainer* scroller = LLUICtrlFactory::create(scroller_params); - scroller->setFollowsAll(); - - // Insert that scroller into the panel widgets hierarchy and folder view - mParticipantListPanel->addChild(scroller); - scroller->addChild(mConversationsRoot); - mConversationsRoot->setScrollContainer(scroller); - mConversationsRoot->setFollowsAll(); - mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox); - - - mChatHistory = getChild("chat_history"); - - mInputEditor = getChild("chat_editor"); - mInputEditor->setTextExpandedCallback(boost::bind(&LLIMConversation::reshapeChatHistory, this)); - mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setPassDelete(TRUE); - mInputEditor->setFont(LLViewerChat::getChatFont()); - - mInputEditorTopPad = mChatHistory->getRect().mBottom - mInputEditor->getRect().mTop; - - setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); - - buildConversationViewParticipant(); - - mSaveRect = isTornOff(); - initRectControl(); - - if (isChatMultiTab()) - { - result = LLFloater::postBuild(); - } - else - { - result = LLDockableFloater::postBuild(); - } - - refreshConversation(); - - return result; -} - -LLParticipantList* LLIMConversation::getParticipantList() -{ - return dynamic_cast(LLIMFloaterContainer::getInstance()->getSessionModel(mSessionID)); -} - -void LLIMConversation::draw() -{ - if (mRefreshTimer->hasExpired()) - { - if (getParticipantList()) - { - getParticipantList()->update(); - } - - refreshConversation(); - - // Restart the refresh timer - mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL); - } - - LLTransientDockableFloater::draw(); -} - -void LLIMConversation::enableDisableCallBtn() -{ - getChildView("voice_call_btn")->setEnabled( - mSessionID.notNull() - && mSession - && mSession->mSessionInitialized - && LLVoiceClient::getInstance()->voiceEnabled() - && LLVoiceClient::getInstance()->isVoiceWorking() - && mSession->mCallBackEnabled); -} - -void LLIMConversation::onFocusReceived() -{ - setBackgroundOpaque(true); - - if (mSessionID.notNull() && isInVisibleChain()) - { - LLIMModel::instance().sendNoUnreadMessages(mSessionID); - } - - LLTransientDockableFloater::onFocusReceived(); - - LLIMFloaterContainer* container = LLFloaterReg::getTypedInstance("im_container"); - if (container) - { - container->selectConversationPair(mSessionID, true); - } -} - -void LLIMConversation::onFocusLost() -{ - setBackgroundOpaque(false); - LLTransientDockableFloater::onFocusLost(); -} - -std::string LLIMConversation::appendTime() -{ - time_t utc_time; - utc_time = time_corrected(); - std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"]"; - - LLSD substitution; - - substitution["datetime"] = (S32) utc_time; - LLStringUtil::format (timeStr, substitution); - - return timeStr; -} - -void LLIMConversation::appendMessage(const LLChat& chat, const LLSD &args) -{ - // Update the participant activity time - LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); - if (im_box) - { - im_box->setTimeNow(mSessionID,chat.mFromID); - } - - - LLChat& tmp_chat = const_cast(chat); - - if(tmp_chat.mTimeStr.empty()) - tmp_chat.mTimeStr = appendTime(); - - if (!chat.mMuted) - { - tmp_chat.mFromName = chat.mFromName; - LLSD chat_args; - if (args) chat_args = 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"] = - !mIsP2PChat || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); - - if (mChatHistory) - { - mChatHistory->appendMessage(chat, chat_args); - } - } -} - - -void LLIMConversation::buildConversationViewParticipant() -{ - // Clear the widget list since we are rebuilding afresh from the model - conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - while (widget_it != mConversationsWidgets.end()) - { - removeConversationViewParticipant(widget_it->first); - // Iterators are invalidated by erase so we need to pick begin again - widget_it = mConversationsWidgets.begin(); - } - - // Get the model list - LLParticipantList* item = getParticipantList(); - if (!item) - { - // Nothing to do if the model list is empty - return; - } - - // Create the participants widgets now - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); - while (current_participant_model != end_participant_model) - { - LLConversationItem* participant_model = dynamic_cast(*current_participant_model); - addConversationViewParticipant(participant_model); - current_participant_model++; - } -} - -void LLIMConversation::addConversationViewParticipant(LLConversationItem* participant_model) -{ - // Check if the model already has an associated view - LLUUID uuid = participant_model->getUUID(); - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid); - - // If not already present, create the participant view and attach it to the root, otherwise, just refresh it - if (widget) - { - updateConversationViewParticipant(uuid); // overkill? - } - else - { - LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); - mConversationsWidgets[uuid] = participant_view; - participant_view->addToFolder(mConversationsRoot); - participant_view->setVisible(TRUE); - refreshConversation(); - } -} - -void LLIMConversation::removeConversationViewParticipant(const LLUUID& participant_id) -{ - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); - if (widget) - { - mConversationsRoot->extractItem(widget); - delete widget; - mConversationsWidgets.erase(participant_id); - refreshConversation(); - } -} - -void LLIMConversation::updateConversationViewParticipant(const LLUUID& participant_id) -{ - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); - if (widget) - { - widget->refresh(); - } - refreshConversation(); -} - -void LLIMConversation::refreshConversation() -{ - // Note: We collect participants names to change the session name only in the case of ad-hoc conversations - bool is_ad_hoc = (mSession ? mSession->isAdHocSessionType() : false); - uuid_vec_t participants_uuids; // uuids vector for building the added participants name string - // For P2P chat, we still need to update the session name who may have changed (switch display name for instance) - if (mIsP2PChat && mSession) - { - participants_uuids.push_back(mSession->mOtherParticipantID); - } - - conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - while (widget_it != mConversationsWidgets.end()) - { - // Add the participant to the list except if it's the agent itself (redundant) - if (is_ad_hoc && (widget_it->first != gAgentID)) - { - participants_uuids.push_back(widget_it->first); - } - widget_it->second->refresh(); - widget_it->second->setVisible(TRUE); - ++widget_it; - } - if (is_ad_hoc || mIsP2PChat) - { - // Build the session name and update it - std::string session_name; - if (participants_uuids.size() != 0) - { - LLAvatarActions::buildResidentsString(participants_uuids, session_name); - } - else - { - session_name = LLIMModel::instance().getName(mSessionID); - } - updateSessionName(session_name); - } - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); - mConversationsRoot->update(); - updateHeaderAndToolbar(); - refresh(); -} - -// Copied from LLIMFloaterContainer::createConversationViewParticipant(). Refactor opportunity! -LLConversationViewParticipant* LLIMConversation::createConversationViewParticipant(LLConversationItem* item) -{ - LLRect panel_rect = mParticipantListPanel->getRect(); - - LLConversationViewParticipant::Params params; - params.name = item->getDisplayName(); - params.root = mConversationsRoot; - params.listener = item; - params.rect = LLRect (0, 24, panel_rect.getWidth(), 0); // *TODO: use conversation_view_participant.xml itemHeight value in lieu of 24 - params.tool_tip = params.name; - params.participant_id = item->getUUID(); - - return LLUICtrlFactory::create(params); -} - -void LLIMConversation::setSortOrder(const LLConversationSort& order) -{ - mConversationViewModel.setSorter(order); - mConversationsRoot->arrangeAll(); - refreshConversation(); -} - -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::hideOrShowTitle() -{ - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - LLView* floater_contents = getChild("contents_view"); - - LLRect floater_rect = getLocalRect(); - S32 top_border_of_contents = floater_rect.mTop - (isTornOff()? floater_header_size : 0); - LLRect handle_rect (0, floater_rect.mTop, floater_rect.mRight, top_border_of_contents); - LLRect contents_rect (0, top_border_of_contents, floater_rect.mRight, floater_rect.mBottom); - mDragHandle->setShape(handle_rect); - mDragHandle->setVisible(isTornOff()); - floater_contents->setShape(contents_rect); -} - -void LLIMConversation::updateSessionName(const std::string& name) -{ - mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + name); -} - -void LLIMConversation::hideAllStandardButtons() -{ - 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); - } - } -} - -void LLIMConversation::updateHeaderAndToolbar() -{ - // prevent start conversation before its container - LLIMFloaterContainer::getInstance(); - - bool is_torn_off = checkIfTornOff(); - if (!is_torn_off) - { - hideAllStandardButtons(); - } - - hideOrShowTitle(); - - // Participant list should be visible only in torn off floaters. - bool is_participant_list_visible = - is_torn_off - && gSavedSettings.getBOOL("IMShowControlPanel") - && !mIsP2PChat; - - mParticipantListPanel->setVisible(is_participant_list_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_torn_off || is_participant_list_visible; - mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); - - // toggle floater's drag handle and title visibility - if (mDragHandle) - { - mDragHandle->setTitleVisible(is_torn_off); - } - - // The button (>>) should be disabled for torn off P2P conversations. - mExpandCollapseBtn->setEnabled(!is_torn_off || !mIsP2PChat); - - mTearOffBtn->setImageOverlay(getString(is_torn_off? "return_icon" : "tear_off_icon")); - mTearOffBtn->setToolTip(getString(!is_torn_off? "tooltip_to_separate_window" : "tooltip_to_main_window")); - - mCloseBtn->setVisible(!is_torn_off && !mIsNearbyChat); - - enableDisableCallBtn(); - - showTranslationCheckbox(); -} - -void LLIMConversation::reshapeChatHistory() -{ - LLRect chat_rect = mChatHistory->getRect(); - LLRect input_rect = mInputEditor->getRect(); - - int delta_height = chat_rect.mBottom - (input_rect.mTop + mInputEditorTopPad); - - chat_rect.setLeftTopAndSize(chat_rect.mLeft, chat_rect.mTop, chat_rect.getWidth(), chat_rect.getHeight() + delta_height); - mChatHistory->setShape(chat_rect); -} - -void LLIMConversation::showTranslationCheckbox(BOOL show) -{ - getChild("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); -} - -// 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(*iter); - if (floater) - { - floater->reloadMessages(); - } - } - - LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); - if (nearby_chat) - { - nearby_chat->reloadMessages(); - } -} - -void LLIMConversation::updateCallBtnState(bool callIsActive) -{ - getChild("voice_call_btn")->setImageOverlay( - callIsActive? getString("call_btn_stop") : getString("call_btn_start")); - enableDisableCallBtn(); - -} - -void LLIMConversation::onSlide(LLIMConversation* self) -{ - LLIMFloaterContainer* host_floater = dynamic_cast(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->mIsP2PChat) - { - bool expand = !self->mParticipantListPanel->getVisible(); - - // Expand/collapse the IM control panel - self->mParticipantListPanel->setVisible(expand); - - gSavedSettings.setBOOL("IMShowControlPanel", expand); - - self->mExpandCollapseBtn->setImageOverlay(self->getString(expand ? "collapse_icon" : "expand_icon")); - } - } -} - -/*virtual*/ -void LLIMConversation::onOpen(const LLSD& key) -{ - if (!checkIfTornOff()) - { - LLIMFloaterContainer* host_floater = dynamic_cast(getHost()); - // Show the messages pane when opening a floater hosted in the Conversations - host_floater->collapseMessagesPane(false); - } -} - -// virtual -void LLIMConversation::onClose(bool app_quitting) -{ - // Always suppress the IM from the conversations list on close if present for any reason - if (LLIMConversation::isChatMultiTab()) - { - LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); - if (im_box) - { - im_box->removeConversationListItem(mKey); - } - } -} - -void LLIMConversation::onTearOffClicked() -{ - setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE); - mSaveRect = isTornOff(); - initRectControl(); - LLFloater::onClickTearOff(this); - refreshConversation(); -} - -// static -bool LLIMConversation::isChatMultiTab() -{ - // Restart is required in order to change chat window type. - return true; -} - -bool LLIMConversation::checkIfTornOff() -{ - bool isTorn = !getHost(); - - if (isTorn != isTornOff()) - { - setTornOff(isTorn); - refreshConversation(); - } - - return isTorn; -} diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h deleted file mode 100644 index 93a1ab847e..0000000000 --- a/indra/newview/llimconversation.h +++ /dev/null @@ -1,176 +0,0 @@ -/** - * @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 "lllayoutstack.h" -#include "llparticipantlist.h" -#include "lltransientdockablefloater.h" -#include "llviewercontrol.h" -#include "lleventtimer.h" -#include "llimview.h" -#include "llconversationmodel.h" -#include "llconversationview.h" -#include "lltexteditor.h" - -class LLPanelChatControlPanel; -class LLChatEntry; -class LLChatHistory; - -class LLIMConversation - : public LLTransientDockableFloater -{ - -public: - LOG_CLASS(LLIMConversation); - - LLIMConversation(const LLSD& session_id); - ~LLIMConversation(); - - // 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(); - - // add conversation to container - static void addToHost(const LLUUID& session_id); - - bool isHostAttached() {return mIsHostAttached;} - void setHostAttached(bool is_attached) {mIsHostAttached = is_attached;} - - static LLIMConversation* findConversation(const LLUUID& uuid); - static LLIMConversation* getConversation(const LLUUID& uuid); - - // show/hide the translation check box - void showTranslationCheckbox(const BOOL visible = FALSE); - - bool isNearbyChat() {return mIsNearbyChat;} - - // LLFloater overrides - /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void onClose(bool app_quitting); - /*virtual*/ BOOL postBuild(); - /*virtual*/ void draw(); - /*virtual*/ void setVisible(BOOL visible); - /*virtual*/ void setFocus(BOOL focus); - - // Handle the left hand participant list widgets - void addConversationViewParticipant(LLConversationItem* item); - void removeConversationViewParticipant(const LLUUID& participant_id); - void updateConversationViewParticipant(const LLUUID& participant_id); - void refreshConversation(); - void buildConversationViewParticipant(); - - void setSortOrder(const LLConversationSort& order); - - virtual void updateMessages() {} - -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); - virtual void onTearOffClicked(); - - // refresh a visual state of the Call button - void updateCallBtnState(bool callIsActive); - - void hideOrShowTitle(); // toggle the floater's drag handle - void hideAllStandardButtons(); - - /// Update floater header and toolbar buttons when hosted/torn off state is toggled. - void updateHeaderAndToolbar(); - - // Update the input field help text and other places that need the session name - virtual void updateSessionName(const std::string& name); - - // set the enable/disable state for the Call button - virtual void enableDisableCallBtn(); - - // process focus events to set a currently active session - /* virtual */ void onFocusLost(); - /* virtual */ void onFocusReceived(); - - // prepare chat's params and out one message to chatHistory - void appendMessage(const LLChat& chat, const LLSD &args = 0); - - std::string appendTime(); - - bool mIsNearbyChat; - bool mIsP2PChat; - - LLIMModel::LLIMSession* mSession; - - // Participants list: model and view - LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item); - - LLUUID mSessionID; - LLLayoutPanel* mParticipantListPanel; // add the widgets to that see mConversationsListPanel - LLParticipantList* getParticipantList(); - conversations_widgets_map mConversationsWidgets; - LLConversationViewModel mConversationViewModel; - LLFolderView* mConversationsRoot; - - LLChatHistory* mChatHistory; - LLChatEntry* mInputEditor; - int mInputEditorTopPad; // padding between input field and chat history - - LLButton* mExpandCollapseBtn; - LLButton* mTearOffBtn; - LLButton* mCloseBtn; - -private: - /// Refreshes the floater at a constant rate. - virtual void refresh() = 0; - - /** - * Adjusts chat history height to fit vertically with input chat field - * and avoid overlapping, since input chat field can be vertically expanded. - * Implementation: chat history bottom "follows" top+top_pad of input chat field - */ - void reshapeChatHistory(); - - bool checkIfTornOff(); - bool mIsHostAttached; - - LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. -}; - - -#endif // LL_IMCONVERSATION_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp deleted file mode 100644 index 73c7be37eb..0000000000 --- a/indra/newview/llimfloater.cpp +++ /dev/null @@ -1,1202 +0,0 @@ -/** - * @file llimfloater.cpp - * @brief LLIMFloater class definition - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" - -#include "llimfloater.h" - -#include "lldraghandle.h" -#include "llnotificationsutil.h" - -#include "llagent.h" -#include "llappviewer.h" -#include "llavataractions.h" -#include "llavatarnamecache.h" -#include "llbutton.h" -#include "llchannelmanager.h" -#include "llchiclet.h" -#include "llchicletbar.h" -#include "llfloaterreg.h" -#include "llfloateravatarpicker.h" -#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container -#include "llinventoryfunctions.h" -//#include "lllayoutstack.h" -#include "llchatentry.h" -#include "lllogchat.h" -#include "llscreenchannel.h" -#include "llsyswellwindow.h" -#include "lltrans.h" -#include "llchathistory.h" -#include "llnotifications.h" -#include "llviewerwindow.h" -#include "lltransientfloatermgr.h" -#include "llinventorymodel.h" -#include "llrootview.h" -#include "llspeakers.h" -#include "llviewerchat.h" -#include "llnotificationmanager.h" -#include "llautoreplace.h" - -floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal; - -LLIMFloater::LLIMFloater(const LLUUID& session_id) - : LLIMConversation(session_id), - mLastMessageIndex(-1), - mDialog(IM_NOTHING_SPECIAL), - mSavedTitle(), - mTypingStart(), - mShouldSendTypingState(false), - mMeTyping(false), - mOtherTyping(false), - mTypingTimer(), - mTypingTimeoutTimer(), - mPositioned(false), - mSessionInitialized(false), - mStartConferenceInSameFloater(false) -{ - mIsNearbyChat = false; - - initIMSession(session_id); - - setOverlapsScreenChannel(true); - - LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); - - setDocked(true); -} - - -// virtual -void LLIMFloater::refresh() -{ - if (mMeTyping) -{ - // Time out if user hasn't typed for a while. - if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS) - { - setTyping(false); - } - } -} - -// virtual -void LLIMFloater::onClickCloseBtn() -{ - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); - - if (session != NULL) - { - bool is_call_with_chat = session->isGroupSessionType() - || session->isAdHocSessionType() || session->isP2PSessionType(); - - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - - if (is_call_with_chat && voice_channel != NULL - && voice_channel->isActive()) - { - LLSD payload; - payload["session_id"] = mSessionID; - LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); - return; - } - } - else - { - llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; - return; - } - - LLIMConversation::onClickCloseBtn(); -} - -/* static */ -void LLIMFloater::newIMCallback(const LLSD& data) -{ - if (data["num_unread"].asInteger() > 0 || data["from_id"].asUUID().isNull()) - { - LLUUID session_id = data["session_id"].asUUID(); - - LLIMFloater* floater = LLFloaterReg::findTypedInstance("impanel", session_id); - - // update if visible, otherwise will be updated when opened - if (floater && floater->getVisible()) - { - floater->updateMessages(); - } - } -} - -void LLIMFloater::onVisibilityChange(const LLSD& new_visibility) -{ - bool visible = new_visibility.asBoolean(); - - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - - if (visible && voice_channel && - voice_channel->getState() == LLVoiceChannel::STATE_CONNECTED) - { - LLFloaterReg::showInstance("voice_call", mSessionID); - } - else - { - LLFloaterReg::hideInstance("voice_call", mSessionID); - } -} - -void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) -{ - LLIMFloater* self = (LLIMFloater*) userdata; - self->sendMsgFromInputEditor(); - self->setTyping(false); -} - -void LLIMFloater::sendMsgFromInputEditor() -{ - if (gAgent.isGodlike() - || (mDialog != IM_NOTHING_SPECIAL) - || !mOtherParticipantUUID.isNull()) - { - if (mInputEditor) - { - LLWString text = mInputEditor->getWText(); - LLWStringUtil::trim(text); - LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. - if(!text.empty()) - { - // Truncate and convert to UTF8 for transport - std::string utf8_text = wstring_to_utf8str(text); - - sendMsg(utf8_text); - - mInputEditor->setText(LLStringUtil::null); - } - } - } - else - { - llinfos << "Cannot send IM to everyone unless you're a god." << llendl; - } -} - -void LLIMFloater::sendMsg(const std::string& msg) -{ - const std::string utf8_text = utf8str_truncate(msg, MAX_MSG_BUF_SIZE - 1); - - 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); - } - - updateMessages(); -} - -LLIMFloater::~LLIMFloater() -{ - mVoiceChannelStateChangeConnection.disconnect(); - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver(this); - } - - LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); -} - - -void LLIMFloater::initIMSession(const LLUUID& session_id) -{ - // Change the floater key to bind it to a new session. - setKey(session_id); - - mSessionID = session_id; - mSession = LLIMModel::getInstance()->findIMSession(mSessionID); - - if (mSession) - { - mIsP2PChat = mSession->isP2PSessionType(); - mSessionInitialized = mSession->mSessionInitialized; - mDialog = mSession->mType; - } -} - -void LLIMFloater::initIMFloater() -{ - const LLUUID& other_party_id = - LLIMModel::getInstance()->getOtherParticipantID(mSessionID); - if (other_party_id.notNull()) - { - mOtherParticipantUUID = other_party_id; - } - - boundVoiceChannel(); - - mTypingStart = LLTrans::getString("IM_typing_start_string"); - - // Show control panel in torn off floaters only. - mParticipantListPanel->setVisible(!getHost() && gSavedSettings.getBOOL("IMShowControlPanel")); - - // Disable input editor if session cannot accept text - if ( mSession && !mSession->mTextIMPossible ) - { - mInputEditor->setEnabled(FALSE); - mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); - } - - if (!mIsP2PChat) - { - std::string session_name(LLIMModel::instance().getName(mSessionID)); - updateSessionName(session_name); - } -} - -//virtual -BOOL LLIMFloater::postBuild() -{ - BOOL result = LLIMConversation::postBuild(); - - mInputEditor->setMaxTextLength(1023); - // enable line history support for instant message bar - // XXX stinson TODO : resolve merge by adding autoreplace to text editors -#if 0 - // *TODO Establish LineEditor with autoreplace callback - mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); -#endif - - mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); - mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); - mInputEditor->setKeystrokeCallback( boost::bind(onInputEditorKeystroke, _1, this) ); - mInputEditor->setCommitCallback(boost::bind(onSendMsg, _1, this)); - - setDocked(true); - - LLButton* add_btn = getChild("add_btn"); - - // Allow to add chat participants depending on the session type - add_btn->setEnabled(isInviteAllowed()); - add_btn->setClickedCallback(boost::bind(&LLIMFloater::onAddButtonClicked, this)); - - childSetAction("voice_call_btn", boost::bind(&LLIMFloater::onCallButtonClicked, this)); - - LLVoiceClient::getInstance()->addObserver(this); - - //*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) - - initIMFloater(); - - return result; -} - -void LLIMFloater::onAddButtonClicked() -{ - LLView * button = findChild("toolbar_panel")->findChild("add_btn"); - LLFloater* root_floater = gFloaterView->getParentFloater(this); - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloater::addSessionParticipants, this, _1), TRUE, TRUE, FALSE, root_floater->getName(), button); - if (!picker) - { - return; - } - - // Need to disable 'ok' button when selected users are already in conversation. - picker->setOkBtnEnableCb(boost::bind(&LLIMFloater::canAddSelectedToChat, this, _1)); - - if (root_floater) - { - root_floater->addDependentFloater(picker); - } -} - -bool LLIMFloater::canAddSelectedToChat(const uuid_vec_t& uuids) -{ - if (!mSession - || mDialog == IM_SESSION_GROUP_START - || mDialog == IM_SESSION_INVITE && gAgent.isInGroup(mSessionID)) - { - return false; - } - - if (mIsP2PChat) - { - // For a P2P session just check if we are not adding the other participant. - - for (uuid_vec_t::const_iterator id = uuids.begin(); - id != uuids.end(); ++id) - { - if (*id == mOtherParticipantUUID) - { - return false; - } - } - } - else - { - // For a conference session we need to check against the list from LLSpeakerMgr, - // because this list may change when participants join or leave the session. - - LLSpeakerMgr::speaker_list_t speaker_list; - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - speaker_mgr->getSpeakerList(&speaker_list, true); - } - - for (uuid_vec_t::const_iterator id = uuids.begin(); - id != uuids.end(); ++id) - { - for (LLSpeakerMgr::speaker_list_t::const_iterator it = speaker_list.begin(); - it != speaker_list.end(); ++it) - { - const LLPointer& speaker = *it; - if (*id == speaker->mID) - { - return false; - } - } - } - } - - return true; -} - -void LLIMFloater::addSessionParticipants(const uuid_vec_t& uuids) -{ - if (mIsP2PChat) - { - LLSD payload; - LLSD args; - - LLNotificationsUtil::add("ConfirmAddingChatParticipants", args, payload, - boost::bind(&LLIMFloater::addP2PSessionParticipants, this, _1, _2, uuids)); - } - else - { - // remember whom we have invited, to notify others later, when the invited ones actually join - mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); - - inviteToSession(uuids); - } -} - -void LLIMFloater::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) - { - return; - } - - mStartConferenceInSameFloater = true; - - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - - // first check whether this is a voice session - bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); - - uuid_vec_t temp_ids; - - // Add the initial participant of a P2P session - temp_ids.push_back(mOtherParticipantUUID); - temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); - - // then we can close the current session - onClose(false); - - // we start a new session so reset the initialization flag - mSessionInitialized = false; - - // remember whom we have invited, to notify others later, when the invited ones actually join - mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); - - // Start a new ad hoc voice call if we invite new participants to a P2P call, - // or start a text chat otherwise. - if (is_voice_call) - { - LLAvatarActions::startAdhocCall(temp_ids, mSessionID); - } - else - { - LLAvatarActions::startConference(temp_ids, mSessionID); - } -} - -void LLIMFloater::sendParticipantsAddedNotification(const uuid_vec_t& uuids) -{ - std::string names_string; - LLAvatarActions::buildResidentsString(uuids, names_string); - LLStringUtil::format_map_t args; - args["[NAME]"] = names_string; - - sendMsg(getString(uuids.size() > 1 ? "multiple_participants_added" : "participant_added", args)); -} - -void LLIMFloater::boundVoiceChannel() -{ - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - if(voice_channel) - { - mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback( - boost::bind(&LLIMFloater::onVoiceChannelStateChanged, this, _1, _2)); - - //call (either p2p, group or ad-hoc) can be already in started state - bool callIsActive = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; - updateCallBtnState(callIsActive); - } -} - -void LLIMFloater::onCallButtonClicked() -{ - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - if (voice_channel) - { - bool is_call_active = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; - if (is_call_active) - { - gIMMgr->endCall(mSessionID); - } - else - { - gIMMgr->startCall(mSessionID); - } - } -} - -void LLIMFloater::onChange(EStatusType status, const std::string &channelURI, bool proximal) -{ - if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL) - { - enableDisableCallBtn(); - } -} - -void LLIMFloater::onVoiceChannelStateChanged( - const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) -{ - bool callIsActive = new_state >= LLVoiceChannel::STATE_CALL_STARTED; - updateCallBtnState(callIsActive); -} - -void LLIMFloater::updateSessionName(const std::string& name) -{ - LLIMConversation::updateSessionName(name); - setTitle(name); - mTypingStart.setArg("[NAME]", name); -} - -//static -LLIMFloater* LLIMFloater::show(const LLUUID& session_id) -{ - closeHiddenIMToasts(); - - if (!gIMMgr->hasSession(session_id)) - return NULL; - - // Test the existence of the floater before we try to create it - bool exist = findInstance(session_id); - - // Get the floater: this will create the instance if it didn't exist - LLIMFloater* floater = getInstance(session_id); - if (!floater) - return NULL; - - LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); - - // Do not add again existing floaters - if (!exist) - { - // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; - // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists - LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; - if (floater_container) - { - floater_container->addFloater(floater, TRUE, i_pt); - } - } - - floater->openFloater(floater->getKey()); - - floater->setVisible(TRUE); - - return floater; -} -//static -LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) -{ - LLIMFloater* conversation = - LLFloaterReg::findTypedInstance("impanel", session_id); - - return conversation; -} - -LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) -{ - LLIMFloater* conversation = - LLFloaterReg::getTypedInstance("impanel", session_id); - - return conversation; -} - -void LLIMFloater::onClose(bool app_quitting) -{ - setTyping(false); - - // The source of much argument and design thrashing - // Should the window hide or the session close when the X is clicked? - // - // Last change: - // EXT-3516 X Button should end IM session, _ button should hide - gIMMgr->leaveSession(mSessionID); - - // Clean up the conversation *after* the session has been ended - LLIMConversation::onClose(app_quitting); -} - -void LLIMFloater::setDocked(bool docked, bool pop_on_undock) -{ - // update notification channel state - LLNotificationsUI::LLScreenChannel* channel = static_cast - (LLNotificationsUI::LLChannelManager::getInstance()-> - findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); - - if(!isChatMultiTab()) - { - LLTransientDockableFloater::setDocked(docked, pop_on_undock); - } - - // update notification channel state - if(channel) - { - channel->updateShowToastsState(); - channel->redrawToasts(); - } -} - -void LLIMFloater::setVisible(BOOL visible) -{ - LLNotificationsUI::LLScreenChannel* channel = static_cast - (LLNotificationsUI::LLChannelManager::getInstance()-> - findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); - - LLIMConversation::setVisible(visible); - - // update notification channel state - if(channel) - { - channel->updateShowToastsState(); - channel->redrawToasts(); - } - - if(!visible) - { - LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet(mSessionID); - if(chiclet) - { - chiclet->setToggleState(false); - } - } - - if (visible && isInVisibleChain()) - { - sIMFloaterShowedSignal(mSessionID); - - } - - setFocus(visible); -} - -BOOL LLIMFloater::getVisible() -{ - bool visible; - - if(isChatMultiTab()) - { - LLIMFloaterContainer* im_container = - LLIMFloaterContainer::getInstance(); - - // Treat inactive floater as invisible. - bool is_active = im_container->getActiveFloater() == this; - - //torn off floater is always inactive - if (!is_active && getHost() != im_container) - { - visible = LLTransientDockableFloater::getVisible(); - } - else - { - // getVisible() returns TRUE when Tabbed IM window is minimized. - visible = is_active && !im_container->isMinimized() - && im_container->getVisible(); - } - } - else - { - visible = LLTransientDockableFloater::getVisible(); - } - - return visible; -} - -//static -bool LLIMFloater::toggle(const LLUUID& session_id) -{ - if(!isChatMultiTab()) - { - LLIMFloater* floater = LLFloaterReg::findTypedInstance( - "impanel", session_id); - if (floater && floater->getVisible() && floater->hasFocus()) - { - // clicking on chiclet to close floater just hides it to maintain existing - // scroll/text entry state - floater->setVisible(false); - return false; - } - else if(floater && (!floater->isDocked() || floater->getVisible() && !floater->hasFocus())) - { - floater->setVisible(TRUE); - floater->setFocus(TRUE); - return true; - } - } - - // ensure the list of messages is updated when floater is made visible - show(session_id); - return true; -} - -void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) -{ - mSessionInitialized = true; - - //will be different only for an ad-hoc im session - if (mSessionID != im_session_id) - { - initIMSession(im_session_id); - buildConversationViewParticipant(); - } - - initIMFloater(); - - //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) - - //need to send delayed messages collected while waiting for session initialization - if (mQueuedMsgsForInit.size()) - { - LLSD::array_iterator iter; - for ( iter = mQueuedMsgsForInit.beginArray(); - iter != mQueuedMsgsForInit.endArray(); ++iter) - { - LLIMModel::sendMessage(iter->asString(), mSessionID, - mOtherParticipantUUID, mDialog); - } - - mQueuedMsgsForInit.clear(); - } -} - -void LLIMFloater::updateMessages() -{ - std::list messages; - - // we shouldn't reset unread message counters if IM floater doesn't have focus - LLIMModel::instance().getMessages( - mSessionID, messages, mLastMessageIndex + 1, hasFocus()); - - if (messages.size()) - { - std::ostringstream message; - std::list::const_reverse_iterator iter = messages.rbegin(); - std::list::const_reverse_iterator iter_end = messages.rend(); - for (; iter != iter_end; ++iter) - { - LLSD msg = *iter; - - std::string time = msg["time"].asString(); - LLUUID from_id = msg["from_id"].asUUID(); - std::string from = msg["from"].asString(); - std::string message = msg["message"].asString(); - bool is_history = msg["is_history"].asBoolean(); - - LLChat chat; - chat.mFromID = from_id; - chat.mSessionID = mSessionID; - chat.mFromName = from; - chat.mTimeStr = time; - chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle; - - // process offer notification - if (msg.has("notification_id")) - { - chat.mNotifId = msg["notification_id"].asUUID(); - // if notification exists - embed it - if (LLNotificationsUtil::find(chat.mNotifId) != NULL) - { - // remove embedded notification from channel - LLNotificationsUI::LLScreenChannel* channel = static_cast - (LLNotificationsUI::LLChannelManager::getInstance()-> - findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); - if (getVisible()) - { - // toast will be automatically closed since it is not storable toast - channel->hideToast(chat.mNotifId); - } - } - // if notification doesn't exist - try to use next message which should be log entry - else - { - continue; - } - } - //process text message - else - { - chat.mText = message; - } - - // Add the message to the chat log - appendMessage(chat); - mLastMessageIndex = msg["index"].asInteger(); - - // if it is a notification - next message is a notification history log, so skip it - if (chat.mNotifId.notNull() && LLNotificationsUtil::find(chat.mNotifId) != NULL) - { - if (++iter == iter_end) - { - break; - } - else - { - mLastMessageIndex++; - } - } - } - } -} - -void LLIMFloater::reloadMessages() -{ - mChatHistory->clear(); - mLastMessageIndex = -1; - updateMessages(); - mInputEditor->setFont(LLViewerChat::getChatFont()); -} - -// static -void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) -{ - LLIMFloater* self= (LLIMFloater*) userdata; - - // Allow enabling the LLIMFloater input editor only if session can accept text - LLIMModel::LLIMSession* im_session = - LLIMModel::instance().findIMSession(self->mSessionID); - //TODO: While disabled lllineeditor can receive focus we need to check if it is enabled (EK) - if( im_session && im_session->mTextIMPossible && self->mInputEditor->getEnabled()) - { - //in disconnected state IM input editor should be disabled - self->mInputEditor->setEnabled(!gDisconnected); - } -} - -// static -void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) -{ - LLIMFloater* self = (LLIMFloater*) userdata; - self->setTyping(false); -} - -// static -void LLIMFloater::onInputEditorKeystroke(LLTextEditor* caller, void* userdata) -{ - LLIMFloater* self = (LLIMFloater*)userdata; - std::string text = self->mInputEditor->getText(); - - // Deleting all text counts as stopping typing. - self->setTyping(!text.empty()); -} - -void LLIMFloater::setTyping(bool typing) -{ - if ( typing ) - { - // Started or proceeded typing, reset the typing timeout timer - mTypingTimeoutTimer.reset(); - } - - if ( mMeTyping != typing ) - { - // Typing state is changed - mMeTyping = typing; - // So, should send current state - mShouldSendTypingState = true; - // In case typing is started, send state after some delay - mTypingTimer.reset(); - } - - // 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 ) - { - // Still typing, send 'start typing' notification or - // send 'stop typing' notification immediately - if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f) - { - LLIMModel::instance().sendTypingState(mSessionID, - mOtherParticipantUUID, mMeTyping); - mShouldSendTypingState = false; - } - } - - if (!mIsNearbyChat) - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); - } - } -} - -void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) -{ - if ( typing ) - { - // other user started typing - addTypingIndicator(im_info); - } - else - { - // other user stopped typing - removeTypingIndicator(im_info); - } -} - -void LLIMFloater::processAgentListUpdates(const LLSD& body) -{ - uuid_vec_t joined_uuids; - - if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) - { - LLSD::map_const_iterator update_it; - for(update_it = body["agent_updates"].beginMap(); - update_it != body["agent_updates"].endMap(); - ++update_it) - { - LLUUID agent_id(update_it->first); - LLSD agent_data = update_it->second; - - if (agent_data.isMap()) - { - // store the new participants in joined_uuids - if (agent_data.has("transition") && agent_data["transition"].asString() == "ENTER") - { - joined_uuids.push_back(agent_id); - } - - // process the moderator mutes - if (agent_id == gAgentID && agent_data.has("info") && agent_data["info"].has("mutes")) - { - BOOL moderator_muted_text = agent_data["info"]["mutes"]["text"].asBoolean(); - mInputEditor->setEnabled(!moderator_muted_text); - std::string label; - if (moderator_muted_text) - label = LLTrans::getString("IM_muted_text_label"); - else - label = LLTrans::getString("IM_to_label") + " " + LLIMModel::instance().getName(mSessionID); - mInputEditor->setLabel(label); - - if (moderator_muted_text) - LLNotificationsUtil::add("TextChatIsMutedByModerator"); - } - } - } - } - - // the vectors need to be sorted for computing the intersection and difference - std::sort(mInvitedParticipants.begin(), mInvitedParticipants.end()); - std::sort(joined_uuids.begin(), joined_uuids.end()); - - uuid_vec_t intersection; // uuids of invited residents who have joined the conversation - std::set_intersection(mInvitedParticipants.begin(), mInvitedParticipants.end(), - joined_uuids.begin(), joined_uuids.end(), - std::back_inserter(intersection)); - - if (intersection.size() > 0) - { - sendParticipantsAddedNotification(intersection); - } - - // Remove all joined participants from invited array. - // The difference between the two vectors (the elements in mInvitedParticipants which are not in joined_uuids) - // is placed at the beginning of mInvitedParticipants, then all other elements are erased. - mInvitedParticipants.erase(std::set_difference(mInvitedParticipants.begin(), mInvitedParticipants.end(), - joined_uuids.begin(), joined_uuids.end(), - mInvitedParticipants.begin()), - mInvitedParticipants.end()); -} - -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") ) - { - BOOL voice_moderated = session_update["moderated_mode"]["voice"]; - const std::string session_label = LLIMModel::instance().getName(mSessionID); - - if (voice_moderated) - { - setTitle(session_label + std::string(" ") - + LLTrans::getString("IM_moderated_chat_label")); - } - else - { - setTitle(session_label); - } - - // *TODO : uncomment this when/if LLPanelActiveSpeakers panel will be added - //update the speakers dropdown too - //mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated); - } -} - -// virtual -BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - if (cargo_type == DAD_PERSON) - { - if (dropPerson(static_cast(cargo_data), drop)) - { - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - } - else if (mDialog == IM_NOTHING_SPECIAL) - { - LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, - cargo_type, cargo_data, accept); - } - - return TRUE; -} - -bool LLIMFloater::dropPerson(LLUUID* person_id, bool drop) -{ - bool res = person_id && person_id->notNull(); - if(res) - { - uuid_vec_t ids; - ids.push_back(*person_id); - - res = canAddSelectedToChat(ids); - if(res && drop) - { - addSessionParticipants(ids); - } - } - - return res; -} - -BOOL LLIMFloater::isInviteAllowed() const -{ - return ( (IM_SESSION_CONFERENCE_START == mDialog) - || (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) - || mIsP2PChat); -} - -class LLSessionInviteResponder : public LLHTTPClient::Responder -{ -public: - LLSessionInviteResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - - void error(U32 statusNum, const std::string& reason) - { - llinfos << "Error inviting all agents to session" << llendl; - //throw something back to the viewer here? - } - -private: - LLUUID mSessionID; -}; - -BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) -{ - LLViewerRegion* region = gAgent.getRegion(); - bool is_region_exist = region != NULL; - - if (is_region_exist) - { - S32 count = ids.size(); - - if( isInviteAllowed() && (count > 0) ) - { - llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl; - - std::string url = region->getCapability("ChatSessionRequest"); - - LLSD data; - data["params"] = LLSD::emptyArray(); - for (int i = 0; i < count; i++) - { - data["params"].append(ids[i]); - } - 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 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 ) - { - mOtherTyping = true; - - // Save and set new title - mSavedTitle = getTitle(); - setTitle (mTypingStart); - - // Update speaker - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if ( speaker_mgr ) - { - speaker_mgr->setSpeakerTyping(im_info->mFromID, TRUE); - } - } -} - -void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) -{ - if ( mOtherTyping ) - { - mOtherTyping = false; - - // Revert the title to saved one - setTitle(mSavedTitle); - - if ( im_info ) - { - // Update speaker - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if ( speaker_mgr ) - { - speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE); - } - } - } -} - -// static -void LLIMFloater::closeHiddenIMToasts() -{ - class IMToastMatcher: public LLNotificationsUI::LLScreenChannel::Matcher - { - public: - bool matches(const LLNotificationPtr notification) const - { - // "notifytoast" type of notifications is reserved for IM notifications - return "notifytoast" == notification->getType(); - } - }; - - LLNotificationsUI::LLScreenChannel* channel = - LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); - if (channel != NULL) - { - channel->closeHiddenToasts(IMToastMatcher()); - } -} -// static -void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - const LLSD& payload = notification["payload"]; - LLUUID session_id = payload["session_id"]; - - LLFloater* im_floater = findInstance(session_id); - if (option == 0 && im_floater != NULL) - { - im_floater->closeFloater(); - } - - return; -} - -// static -void LLIMFloater::sRemoveTypingIndicator(const LLSD& data) -{ - LLUUID session_id = data["session_id"]; - if (session_id.isNull()) - return; - - LLUUID from_id = data["from_id"]; - if (gAgentID == from_id || LLUUID::null == from_id) - return; - - LLIMFloater* floater = LLIMFloater::findInstance(session_id); - if (!floater) - return; - - if (IM_NOTHING_SPECIAL != floater->mDialog) - return; - - floater->removeTypingIndicator(); -} - -// static -void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) -{ - LLIMFloater::addToHost(session_id); -} - -boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb) -{ - return LLIMFloater::sIMFloaterShowedSignal.connect(cb); -} diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h deleted file mode 100644 index 6ba31657dc..0000000000 --- a/indra/newview/llimfloater.h +++ /dev/null @@ -1,196 +0,0 @@ -/** - * @file llimfloater.h - * @brief LLIMFloater class definition - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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_IMFLOATER_H -#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" - -class LLAvatarName; -class LLButton; -class LLChatEntry; -class LLTextEditor; -class LLPanelChatControlPanel; -class LLChatHistory; -class LLInventoryItem; -class LLInventoryCategory; - -typedef boost::signals2::signal floater_showed_signal_t; - -/** - * Individual IM window that appears at the bottom of the screen, - * optionally "docked" to the bottom tray. - */ -class LLIMFloater - : public LLVoiceClientStatusObserver - , public LLIMConversation -{ - LOG_CLASS(LLIMFloater); -public: - LLIMFloater(const LLUUID& session_id); - - virtual ~LLIMFloater(); - - void initIMSession(const LLUUID& session_id); - void initIMFloater(); - - // LLView overrides - /*virtual*/ BOOL postBuild(); - /*virtual*/ void setVisible(BOOL visible); - /*virtual*/ BOOL getVisible(); - // Check typing timeout timer. - - static LLIMFloater* findInstance(const LLUUID& session_id); - static LLIMFloater* getInstance(const LLUUID& session_id); - - // LLFloater overrides - /*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); - - // Toggle panel specified by session_id - // Returns true iff panel became visible - static bool toggle(const LLUUID& session_id); - - void sessionInitReplyReceived(const LLUUID& im_session_id); - - // get new messages from LLIMModel - /*virtual*/ void updateMessages(); - void reloadMessages(); - static void onSendMsg(LLUICtrl*, void*); - void sendMsgFromInputEditor(); - void sendMsg(const std::string& msg); - - // callback for LLIMModel on new messages - // route to specific floater if it is visible - static void newIMCallback(const LLSD& data); - - // called when docked floater's position has been set by chiclet - void setPositioned(bool b) { mPositioned = b; }; - - void onVisibilityChange(const LLSD& new_visibility); - - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - 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); - - void processIMTyping(const LLIMInfo* im_info, BOOL typing); - void processAgentListUpdates(const LLSD& body); - void processSessionUpdate(const LLSD& session_update); - - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - - - //used as a callback on receiving new IM message - static void sRemoveTypingIndicator(const LLSD& data); - static void onIMChicletCreated(const LLUUID& session_id); - - bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; } - const LLUUID& getOtherParticipantUUID() {return mOtherParticipantUUID;} - - static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb); - static floater_showed_signal_t sIMFloaterShowedSignal; - -private: - - /*virtual*/ void refresh(); - - /*virtual*/ void onClickCloseBtn(); - - // Update the window title and input field help text - /*virtual*/ void updateSessionName(const std::string& name); - - bool dropPerson(LLUUID* person_id, bool drop); - - BOOL isInviteAllowed() const; - BOOL inviteToSession(const uuid_vec_t& agent_ids); - static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); - static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); - static void onInputEditorKeystroke(LLTextEditor* caller, void* userdata); - void setTyping(bool typing); - void onAddButtonClicked(); - void addSessionParticipants(const uuid_vec_t& uuids); - void addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids); - void sendParticipantsAddedNotification(const uuid_vec_t& uuids); - bool canAddSelectedToChat(const uuid_vec_t& uuids); - - void onCallButtonClicked(); - - void boundVoiceChannel(); - - // Add the "User is typing..." indicator. - void addTypingIndicator(const LLIMInfo* im_info); - - // Remove the "User is typing..." indicator. - void removeTypingIndicator(const LLIMInfo* im_info = NULL); - - static void closeHiddenIMToasts(); - - static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); - - S32 mLastMessageIndex; - - EInstantMessage mDialog; - LLUUID mOtherParticipantUUID; - bool mPositioned; - - std::string mSavedTitle; - LLUIString mTypingStart; - bool mMeTyping; - bool mOtherTyping; - bool mShouldSendTypingState; - LLFrameTimer mTypingTimer; - LLFrameTimer mTypingTimeoutTimer; - - bool mSessionInitialized; - LLSD mQueuedMsgsForInit; - - bool mStartConferenceInSameFloater; - - uuid_vec_t mInvitedParticipants; - - // connection to voice channel state change signal - boost::signals2::connection mVoiceChannelStateChangeConnection; -}; - -#endif // LL_IMFLOATER_H diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp deleted file mode 100644 index 9c1f5d7593..0000000000 --- a/indra/newview/llimfloatercontainer.cpp +++ /dev/null @@ -1,1557 +0,0 @@ -/** - * @file llimfloatercontainer.cpp - * @brief Multifloater containing active IM sessions in separate tab container tabs - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" - -#include "llimfloater.h" -#include "llimfloatercontainer.h" - -#include "llfloaterreg.h" -#include "lllayoutstack.h" -#include "llnearbychat.h" - -#include "llagent.h" -#include "llavataractions.h" -#include "llavatariconctrl.h" -#include "llavatarnamecache.h" -#include "llcallbacklist.h" -#include "llgroupactions.h" -#include "llgroupiconctrl.h" -#include "llfloateravatarpicker.h" -#include "llfloaterpreference.h" -#include "llimview.h" -#include "llnotificationsutil.h" -#include "lltransientfloatermgr.h" -#include "llviewercontrol.h" -#include "llconversationview.h" -#include "llcallbacklist.h" -#include "llworld.h" - -#include "llsdserialize.h" -// -// LLIMFloaterContainer -// -LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) -: LLMultiFloater(seed), - mExpandCollapseBtn(NULL), - mConversationsRoot(NULL), - mConversationsEventStream("ConversationsEvents"), - mInitialized(false) -{ - mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLIMFloaterContainer::isActionChecked, this, _2)); - mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLIMFloaterContainer::onCustomAction, this, _2)); - - mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLIMFloaterContainer::checkContextMenuItem, this, _2)); - mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLIMFloaterContainer::enableContextMenuItem, this, _2)); - mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLIMFloaterContainer::doToSelected, this, _2)); - - mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&LLIMFloaterContainer::doToSelectedGroup, this, _2)); - - // Firstly add our self to IMSession observers, so we catch session events - LLIMMgr::getInstance()->addSessionObserver(this); - - mAutoResize = FALSE; - LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); -} - -LLIMFloaterContainer::~LLIMFloaterContainer() -{ - mConversationsEventStream.stopListening("ConversationsRefresh"); - - gIdleCallbacks.deleteFunction(idle, this); - - mNewMessageConnection.disconnect(); - LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); - - gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed()); - gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed()); - - if (!LLSingleton::destroyed()) - { - LLIMMgr::getInstance()->removeSessionObserver(this); - } -} - -void LLIMFloaterContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) -{ - addConversationListItem(session_id); - LLIMConversation::addToHost(session_id); -} - -void LLIMFloaterContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) -{ - selectConversation(session_id); -} - -void LLIMFloaterContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) -{ - addConversationListItem(session_id); - LLIMConversation::addToHost(session_id); -} - -void LLIMFloaterContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) -{ - // *TODO: We should do this *without* delete and recreate - addConversationListItem(new_session_id, removeConversationListItem(old_session_id)); -} - -void LLIMFloaterContainer::sessionRemoved(const LLUUID& session_id) -{ - removeConversationListItem(session_id); -} - -// static -void LLIMFloaterContainer::onCurrentChannelChanged(const LLUUID& session_id) -{ - if (session_id != LLUUID::null) - { - LLIMFloaterContainer::getInstance()->showConversation(session_id); - } -} - - -BOOL LLIMFloaterContainer::postBuild() -{ - mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLIMFloaterContainer::onNewMessageReceived, this, _1)); - // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button - // mTabContainer will be initialized in LLMultiFloater::addChild() - - setTabContainer(getChild("im_box_tab_container")); - - mConversationsStack = getChild("conversations_stack"); - mConversationsPane = getChild("conversations_layout_panel"); - mMessagesPane = getChild("messages_layout_panel"); - - mConversationsListPanel = getChild("conversations_list_panel"); - - // Open IM session with selected participant on double click event - mConversationsListPanel->setDoubleClickCallback(boost::bind(&LLIMFloaterContainer::doToSelected, this, LLSD("im"))); - - // Create the root model and view for all conversation sessions - LLConversationItem* base_item = new LLConversationItem(getRootViewModel()); - - LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); - p.name = getName(); - p.title = getLabel(); - p.rect = LLRect(0, 0, getRect().getWidth(), 0); - p.parent_panel = mConversationsListPanel; - p.tool_tip = p.name; - p.listener = base_item; - p.view_model = &mConversationViewModel; - p.root = NULL; - p.use_ellipses = true; - p.options_menu = "menu_conversation.xml"; - mConversationsRoot = LLUICtrlFactory::create(p); - mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); - - // Add listener to conversation model events - mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLIMFloaterContainer::onConversationModelEvent, this, _1)); - - // a scroller for folder view - LLRect scroller_view_rect = mConversationsListPanel->getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams()); - scroller_params.rect(scroller_view_rect); - - LLScrollContainer* scroller = LLUICtrlFactory::create(scroller_params); - scroller->setFollowsAll(); - mConversationsListPanel->addChild(scroller); - scroller->addChild(mConversationsRoot); - mConversationsRoot->setScrollContainer(scroller); - mConversationsRoot->setFollowsAll(); - mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox); - - addConversationListItem(LLUUID()); // manually add nearby chat - - mExpandCollapseBtn = getChild("expand_collapse_btn"); - mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMFloaterContainer::onExpandCollapseButtonClicked, this)); - - childSetAction("add_btn", boost::bind(&LLIMFloaterContainer::onAddButtonClicked, this)); - - collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); - collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed")); - LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); - - if (! mMessagesPane->isCollapsed()) - { - S32 list_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth"); - LLRect list_size = mConversationsPane->getRect(); - S32 left_pad = mConversationsListPanel->getRect().mLeft; - list_size.mRight = list_size.mLeft + list_width - left_pad; - - mConversationsPane->handleReshape(list_size, TRUE); - } - - // Init the sort order now that the root had been created - setSortOrder(LLConversationSort(gSavedSettings.getU32("ConversationSortOrder"))); - - mInitialized = true; - - // Add callbacks: - // We'll take care of view updates on idle - gIdleCallbacks.addFunction(idle, this); - // When display name option change, we need to reload all participant names - LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLIMFloaterContainer::processParticipantsStyleUpdate, this)); - - return TRUE; -} - -void LLIMFloaterContainer::onOpen(const LLSD& key) -{ - LLMultiFloater::onOpen(key); - openNearbyChat(); -} - -// virtual -void LLIMFloaterContainer::addFloater(LLFloater* floaterp, - BOOL select_added_floater, - LLTabContainer::eInsertionPoint insertion_point) -{ - if(!floaterp) return; - - // already here - if (floaterp->getHost() == this) - { - openFloater(floaterp->getKey()); - return; - } - - // Make sure the message panel is open when adding a floater or it stays mysteriously hidden - collapseMessagesPane(false); - - // Add the floater - LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); - - LLUUID session_id = floaterp->getKey(); - - LLIconCtrl* icon = 0; - - if(gAgent.isInGroup(session_id, TRUE)) - { - LLGroupIconCtrl::Params icon_params; - icon_params.group_id = session_id; - icon = LLUICtrlFactory::instance().create(icon_params); - - mSessions[session_id] = floaterp; - floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id)); - } - else - { LLUUID avatar_id = session_id.notNull()? - LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID(); - - LLAvatarIconCtrl::Params icon_params; - icon_params.avatar_id = avatar_id; - icon = LLUICtrlFactory::instance().create(icon_params); - - mSessions[session_id] = floaterp; - floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id)); - } - - // forced resize of the floater - LLRect wrapper_rect = this->mTabContainer->getLocalRect(); - floaterp->setRect(wrapper_rect); - - mTabContainer->setTabImage(floaterp, icon); -} - - -void LLIMFloaterContainer::onCloseFloater(LLUUID& id) -{ - mSessions.erase(id); - setFocus(TRUE); -} - -// virtual -void LLIMFloaterContainer::computeResizeLimits(S32& new_min_width, S32& new_min_height) -{ - // possibly increase floater's minimum height according to children's minimums - for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) - { - LLFloater* floaterp = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); - if (floaterp) - { - new_min_height = llmax(new_min_height, floaterp->getMinHeight()); - } - } - - S32 conversations_pane_min_dim = mConversationsPane->getRelevantMinDim(); - S32 messages_pane_min_dim = mMessagesPane->getRelevantMinDim(); - - // set floater's minimum width according to relevant minimal children's dimensionals - new_min_width = conversations_pane_min_dim + messages_pane_min_dim + LLPANEL_BORDER_WIDTH*2; -} - -void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data) -{ - LLUUID session_id = data["session_id"].asUUID(); - LLFloater* floaterp = get_ptr_in_map(mSessions, session_id); - LLFloater* current_floater = LLMultiFloater::getActiveFloater(); - - if(floaterp && current_floater && floaterp != current_floater) - { - if(LLMultiFloater::isFloaterFlashing(floaterp)) - LLMultiFloater::setFloaterFlashing(floaterp, FALSE); - LLMultiFloater::setFloaterFlashing(floaterp, TRUE); - } -} - -void LLIMFloaterContainer::onExpandCollapseButtonClicked() -{ - if (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed() - && gSavedPerAccountSettings.getBOOL("ConversationsExpandMessagePaneFirst")) - { - // Expand the messages pane from ultra minimized state - // if it was collapsed last in order. - collapseMessagesPane(false); - } - else - { - collapseConversationsPane(!mConversationsPane->isCollapsed()); - } - selectConversation(mSelectedSession); -} - -LLIMFloaterContainer* LLIMFloaterContainer::findInstance() -{ - return LLFloaterReg::findTypedInstance("im_container"); -} - -LLIMFloaterContainer* LLIMFloaterContainer::getInstance() -{ - return LLFloaterReg::getTypedInstance("im_container"); -} - -// Update all participants in the conversation lists -void LLIMFloaterContainer::processParticipantsStyleUpdate() -{ - // On each session in mConversationsItems - for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) - { - // Get the current session descriptors - LLConversationItem* session_model = it_session->second; - // Iterate through each model participant child - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = session_model->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); - while (current_participant_model != end_participant_model) - { - LLConversationItemParticipant* participant_model = dynamic_cast(*current_participant_model); - // Get the avatar name for this participant id from the cache and update the model - participant_model->fetchAvatarName(); - // Next participant - current_participant_model++; - } - } -} - -// static -void LLIMFloaterContainer::idle(void* user_data) -{ - LLIMFloaterContainer* self = static_cast(user_data); - - // Update the distance to agent in the nearby chat session if required - // Note: it makes no sense of course to update the distance in other session - if (self->mConversationViewModel.getSorter().getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE) - { - self->setNearbyDistances(); - } - self->mConversationsRoot->update(); -} - -bool LLIMFloaterContainer::onConversationModelEvent(const LLSD& event) -{ - // For debug only - //std::ostringstream llsd_value; - //llsd_value << LLSDOStreamer(event) << std::endl; - //llinfos << "LLIMFloaterContainer::onConversationModelEvent, event = " << llsd_value.str() << llendl; - // end debug - - // Note: In conversations, the model is not responsible for creating the view, which is a good thing. This means that - // the model could change substantially and the view could echo only a portion of this model (though currently the - // conversation view does echo the conversation model 1 to 1). - // Consequently, the participant views need to be created either by the session view or by the container panel. - // For the moment, we create them here, at the container level, to conform to the pattern implemented in llinventorypanel.cpp - // (see LLInventoryPanel::buildNewViews()). - - std::string type = event.get("type").asString(); - LLUUID session_id = event.get("session_uuid").asUUID(); - LLUUID participant_id = event.get("participant_uuid").asUUID(); - - LLConversationViewSession* session_view = dynamic_cast(get_ptr_in_map(mConversationsWidgets,session_id)); - if (!session_view) - { - // We skip events that are not associated with a session - return false; - } - LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id); - LLIMConversation *conversation_floater = (session_id.isNull() ? (LLIMConversation*)(LLFloaterReg::findTypedInstance("nearby_chat")) : (LLIMConversation*)(LLIMFloater::findInstance(session_id))); - - if (type == "remove_participant") - { - // Remove a participant view from the hierarchical conversation list - if (participant_view) - { - session_view->extractItem(participant_view); - delete participant_view; - session_view->refresh(); - mConversationsRoot->arrangeAll(); - } - // Remove a participant view from the conversation floater - if (conversation_floater) - { - conversation_floater->removeConversationViewParticipant(participant_id); - } - } - else if (type == "add_participant") - { - LLConversationItemSession* session_model = dynamic_cast(mConversationsItems[session_id]); - LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL); - if (!participant_view && session_model && participant_model) - { - LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id); - if (session_id.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType())) - { - participant_view = createConversationViewParticipant(participant_model); - participant_view->addToFolder(session_view); - participant_view->setVisible(TRUE); - } - } - // Add a participant view to the conversation floater - if (conversation_floater && participant_model) - { - conversation_floater->addConversationViewParticipant(participant_model); - } - } - else if (type == "update_participant") - { - // Update the participant view in the hierarchical conversation list - if (participant_view) - { - participant_view->refresh(); - } - // Update the participant view in the conversation floater - if (conversation_floater) - { - conversation_floater->updateConversationViewParticipant(participant_id); - } - } - else if (type == "update_session") - { - session_view->refresh(); - if (conversation_floater) - { - conversation_floater->refreshConversation(); - } - } - - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); - - return false; -} - -void LLIMFloaterContainer::draw() -{ - if (mTabContainer->getTabCount() == 0) - { - // Do not close the container when every conversation is torn off because the user - // still needs the conversation list. Simply collapse the message pane in that case. - collapseMessagesPane(true); - } - LLFloater::draw(); -} - -void LLIMFloaterContainer::tabClose() -{ - if (mTabContainer->getTabCount() == 0) - { - // Do not close the container when every conversation is torn off because the user - // still needs the conversation list. Simply collapse the message pane in that case. - collapseMessagesPane(true); - } -} - -void LLIMFloaterContainer::setVisible(BOOL visible) -{ LLNearbyChat* nearby_chat; - if (visible) - { - // Make sure we have the Nearby Chat present when showing the conversation container - nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); - if (nearby_chat == NULL) - { - // If not found, force the creation of the nearby chat conversation panel - // *TODO: find a way to move this to XML as a default panel or something like that - LLSD name("nearby_chat"); - LLFloaterReg::toggleInstanceOrBringToFront(name); - } - openNearbyChat(); - } - - nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); - if (nearby_chat) - { - LLIMConversation::addToHost(LLUUID()); - } - - // We need to show/hide all the associated conversations that have been torn off - // (and therefore, are not longer managed by the multifloater), - // so that they show/hide with the conversations manager. - conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - for (;widget_it != mConversationsWidgets.end(); ++widget_it) - { - LLConversationViewSession* widget = dynamic_cast(widget_it->second); - if (widget) - { - widget->setVisibleIfDetached(visible); - } - } - - // Now, do the normal multifloater show/hide - LLMultiFloater::setVisible(visible); - -} - -void LLIMFloaterContainer::collapseMessagesPane(bool collapse) -{ - if (mMessagesPane->isCollapsed() == collapse) - { - return; - } - - if (collapse) - { - // Save the messages pane width before collapsing it. - gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", mMessagesPane->getRect().getWidth()); - - // Save the order in which the panels are closed to reverse user's last action. - gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed()); - } - - // Save left pane rectangle before collapsing/expanding right pane. - LLRect prevRect = mConversationsPane->getRect(); - - // Show/hide the messages pane. - mConversationsStack->collapsePanel(mMessagesPane, collapse); - - if (!collapse) - { - // Make sure layout is updated before resizing conversation pane. - mConversationsStack->updateLayout(); - } - - updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth")); - if (!collapse) - { - // Restore conversation's pane previous width after expanding messages pane. - mConversationsPane->setTargetDim(prevRect.getWidth()); - } -} -void LLIMFloaterContainer::collapseConversationsPane(bool collapse) -{ - if (mConversationsPane->isCollapsed() == collapse) - { - return; - } - - LLView* button_panel = getChild("conversations_pane_buttons_expanded"); - button_panel->setVisible(!collapse); - mExpandCollapseBtn->setImageOverlay(getString(collapse ? "expand_icon" : "collapse_icon")); - - if (collapse) - { - // Save the conversations pane width before collapsing it. - gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", mConversationsPane->getRect().getWidth()); - - // Save the order in which the panels are closed to reverse user's last action. - gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed()); - } - - mConversationsStack->collapsePanel(mConversationsPane, collapse); - - S32 collapsed_width = mConversationsPane->getMinDim(); - updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - collapsed_width); - - for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - widget_it != mConversationsWidgets.end(); ++widget_it) - { - LLConversationViewSession* widget = dynamic_cast(widget_it->second); - if (widget) - { - widget->toggleMinimizedMode(collapse); - - // force closing all open conversations when collapsing to minimized state - if (collapse) - { - widget->setOpen(false); - } -} - } -} - -void LLIMFloaterContainer::updateState(bool collapse, S32 delta_width) -{ - LLRect floater_rect = getRect(); - floater_rect.mRight += ((collapse ? -1 : 1) * delta_width); - - // Set by_user = true so that reshaped rect is saved in user_settings. - setShape(floater_rect, true); - - updateResizeLimits(); - - bool is_left_pane_expanded = !mConversationsPane->isCollapsed(); - bool is_right_pane_expanded = !mMessagesPane->isCollapsed(); - - setCanResize(is_left_pane_expanded || is_right_pane_expanded); - setCanMinimize(is_left_pane_expanded || is_right_pane_expanded); - - // force set correct size for the title after show/hide minimize button - LLRect cur_rect = getRect(); - LLRect force_rect = cur_rect; - force_rect.mRight = cur_rect.mRight + 1; - setRect(force_rect); - setRect(cur_rect); - - // restore floater's resize limits (prevent collapse when left panel is expanded) - if (is_left_pane_expanded && !is_right_pane_expanded) - { - S32 expanded_min_size = mConversationsPane->getExpandedMinDim(); - setResizeLimits(expanded_min_size, expanded_min_size); - } - -} - -void LLIMFloaterContainer::onAddButtonClicked() -{ - LLView * button = findChild("conversations_pane_buttons_expanded")->findChild("add_btn"); - LLFloater* root_floater = gFloaterView->getParentFloater(this); - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloaterContainer::onAvatarPicked, this, _1), TRUE, TRUE, TRUE, root_floater->getName(), button); - - if (picker && root_floater) - { - root_floater->addDependentFloater(picker); - } -} - -void LLIMFloaterContainer::onAvatarPicked(const uuid_vec_t& ids) -{ - if (ids.size() == 1) - { - LLAvatarActions::startIM(ids.back()); - } - else - { - LLAvatarActions::startConference(ids); - } -} - -void LLIMFloaterContainer::onCustomAction(const LLSD& userdata) -{ - std::string command = userdata.asString(); - - if ("sort_sessions_by_type" == command) - { - setSortOrderSessions(LLConversationFilter::SO_SESSION_TYPE); - } - if ("sort_sessions_by_name" == command) - { - setSortOrderSessions(LLConversationFilter::SO_NAME); - } - if ("sort_sessions_by_recent" == command) - { - setSortOrderSessions(LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_name" == command) - { - setSortOrderParticipants(LLConversationFilter::SO_NAME); - } - if ("sort_participants_by_recent" == command) - { - setSortOrderParticipants(LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_distance" == command) - { - setSortOrderParticipants(LLConversationFilter::SO_DISTANCE); - } - if ("chat_preferences" == command) - { - LLFloaterPreference* floater_prefs = LLFloaterReg::showTypedInstance("preferences"); - if (floater_prefs) - { - LLTabContainer* tab_container = floater_prefs->getChild("pref core"); - LLPanel* chat_panel = tab_container->getPanelByName("chat"); - if (tab_container && chat_panel) - { - tab_container->selectTabPanel(chat_panel); - } - } - } -} - -BOOL LLIMFloaterContainer::isActionChecked(const LLSD& userdata) -{ - LLConversationSort order = mConversationViewModel.getSorter(); - std::string command = userdata.asString(); - if ("sort_sessions_by_type" == command) - { - return (order.getSortOrderSessions() == LLConversationFilter::SO_SESSION_TYPE); - } - if ("sort_sessions_by_name" == command) - { - return (order.getSortOrderSessions() == LLConversationFilter::SO_NAME); - } - if ("sort_sessions_by_recent" == command) - { - return (order.getSortOrderSessions() == LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_name" == command) - { - return (order.getSortOrderParticipants() == LLConversationFilter::SO_NAME); - } - if ("sort_participants_by_recent" == command) - { - return (order.getSortOrderParticipants() == LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_distance" == command) - { - return (order.getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE); - } - - return FALSE; -} - -void LLIMFloaterContainer::setSortOrderSessions(const LLConversationFilter::ESortOrderType order) -{ - LLConversationSort old_order = mConversationViewModel.getSorter(); - if (order != old_order.getSortOrderSessions()) - { - old_order.setSortOrderSessions(order); - setSortOrder(old_order); - } -} - -void LLIMFloaterContainer::setSortOrderParticipants(const LLConversationFilter::ESortOrderType order) -{ - LLConversationSort old_order = mConversationViewModel.getSorter(); - if (order != old_order.getSortOrderParticipants()) - { - old_order.setSortOrderParticipants(order); - setSortOrder(old_order); - } -} - -void LLIMFloaterContainer::setSortOrder(const LLConversationSort& order) -{ - mConversationViewModel.setSorter(order); - mConversationsRoot->arrangeAll(); - // try to keep selection onscreen, even if it wasn't to start with - mConversationsRoot->scrollToShowSelection(); - - // Notify all conversation (torn off or not) of the change to the sort order - // Note: For the moment, the sort order is *unique* across all conversations. That might change in the future. - for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) - { - LLUUID session_id = it_session->first; - LLIMConversation *conversation_floater = (session_id.isNull() ? (LLIMConversation*)(LLFloaterReg::findTypedInstance("nearby_chat")) : (LLIMConversation*)(LLIMFloater::findInstance(session_id))); - if (conversation_floater) - { - conversation_floater->setSortOrder(order); - } - } - - gSavedSettings.setU32("ConversationSortOrder", (U32)order); -} - -void LLIMFloaterContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids) -{ - const std::set selectedItems = mConversationsRoot->getSelectionList(); - - std::set::const_iterator it = selectedItems.begin(); - const std::set::const_iterator it_end = selectedItems.end(); - LLConversationItem * conversationItem; - - for (; it != it_end; ++it) - { - conversationItem = static_cast((*it)->getViewModelItem()); - selected_uuids.push_back(conversationItem->getUUID()); - } -} - -const LLConversationItem * LLIMFloaterContainer::getCurSelectedViewModelItem() -{ - LLConversationItem * conversationItem = NULL; - - if(mConversationsRoot && - mConversationsRoot->getCurSelectedItem() && - mConversationsRoot->getCurSelectedItem()->getViewModelItem()) - { - conversationItem = static_cast(mConversationsRoot->getCurSelectedItem()->getViewModelItem()); - } - - return conversationItem; -} - -void LLIMFloaterContainer::getParticipantUUIDs(uuid_vec_t& selected_uuids) -{ - //Find the conversation floater associated with the selected id - const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); - - if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) - { - getSelectedUUIDs(selected_uuids); - } - //When a one-on-one conversation exists, retrieve the participant id from the conversation floater - else if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1) - { - LLIMFloater *conversationFloater = LLIMFloater::findInstance(conversationItem->getUUID()); - LLUUID participantID = conversationFloater->getOtherParticipantUUID(); - selected_uuids.push_back(participantID); - } -} - -void LLIMFloaterContainer::doToParticipants(const std::string& command, uuid_vec_t& selectedIDS) -{ - if(selectedIDS.size() > 0) - { - const LLUUID& userID = selectedIDS.front(); - if(gAgent.getID() != userID) - { - if ("view_profile" == command) - { - LLAvatarActions::showProfile(userID); - } - else if("im" == command) - { - LLAvatarActions::startIM(userID); - } - else if("offer_teleport" == command) - { - LLAvatarActions::offerTeleport(selectedIDS); - } - else if("voice_call" == command) - { - LLAvatarActions::startCall(userID); - } - else if("chat_history" == command) - { - LLAvatarActions::viewChatHistory(userID); - } - else if("add_friend" == command) - { - LLAvatarActions::requestFriendshipDialog(userID); - } - else if("remove_friend" == command) - { - LLAvatarActions::removeFriendDialog(userID); - } - else if("invite_to_group" == command) - { - LLAvatarActions::inviteToGroup(userID); - } - else if("map" == command) - { - LLAvatarActions::showOnMap(userID); - } - else if("share" == command) - { - LLAvatarActions::share(userID); - } - else if("pay" == command) - { - LLAvatarActions::pay(userID); - } - else if("block_unblock" == command) - { - LLAvatarActions::toggleBlock(userID); - } - else if("selected" == command || "mute_all" == command || "unmute_all" == command) - { - moderateVoice(command, userID); - } - else if ("toggle_allow_text_chat" == command) - { - toggleAllowTextChat(userID); - } - } - } -} - -void LLIMFloaterContainer::doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS) -{ - //Find the conversation floater associated with the selected id - const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); - LLIMFloater *conversationFloater = LLIMFloater::findInstance(conversationItem->getUUID()); - - if(conversationFloater) - { - //Close the selected conversation - if("close_conversation" == command) - { - LLFloater::onClickClose(conversationFloater); - } - else if("open_voice_conversation" == command) - { - gIMMgr->startCall(conversationItem->getUUID()); - } - else if("disconnect_from_voice" == command) - { - gIMMgr->endCall(conversationItem->getUUID()); - } - else if("chat_history" == command) - { - const LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(conversationItem->getUUID()); - - if (NULL != session) - { - const LLUUID session_id = session->isOutgoingAdHoc() ? session->generateOutgouigAdHocHash() : session->mSessionID; - LLFloaterReg::showInstance("preview_conversation", session_id, true); - } - } - else - { - doToParticipants(command, selectedIDS); - } - } -} - -void LLIMFloaterContainer::doToSelected(const LLSD& userdata) -{ - std::string command = userdata.asString(); - const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); - uuid_vec_t selected_uuids; - - if(conversationItem != NULL) - { - getParticipantUUIDs(selected_uuids); - - if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) - { - doToParticipants(command, selected_uuids); - } - else - { - doToSelectedConversation(command, selected_uuids); - } - } -} - -void LLIMFloaterContainer::doToSelectedGroup(const LLSD& userdata) -{ - std::string action = userdata.asString(); - LLUUID selected_group = getCurSelectedViewModelItem()->getUUID(); - - if (action == "group_profile") - { - LLGroupActions::show(selected_group); - } - else if (action == "activate_group") - { - LLGroupActions::activate(selected_group); - } - else if (action == "leave_group") - { - LLGroupActions::leave(selected_group); - } -} - -bool LLIMFloaterContainer::enableContextMenuItem(const LLSD& userdata) -{ - std::string item = userdata.asString(); - uuid_vec_t uuids; - getParticipantUUIDs(uuids); - - if(item == std::string("can_activate_group")) - { - LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID(); - return gAgent.getGroupID() != selected_group_id; - } - - if(uuids.size() <= 0) - { - return false; - } - - // Note: can_block and can_delete is used only for one person selected menu - // so we don't need to go over all uuids. - - if (item == std::string("can_block")) - { - const LLUUID& id = uuids.front(); - return LLAvatarActions::canBlock(id); - } - else if (item == std::string("can_add")) - { - // We can add friends if: - // - there are selected people - // - and there are no friends among selection yet. - - //EXT-7389 - disable for more than 1 - if(uuids.size() > 1) - { - return false; - } - - bool result = true; - - uuid_vec_t::const_iterator - id = uuids.begin(), - uuids_end = uuids.end(); - - for (;id != uuids_end; ++id) - { - if ( LLAvatarActions::isFriend(*id) ) - { - result = false; - break; - } - } - - return result; - } - else if (item == std::string("can_delete")) - { - // We can remove friends if: - // - there are selected people - // - and there are only friends among selection. - - bool result = (uuids.size() > 0); - - uuid_vec_t::const_iterator - id = uuids.begin(), - uuids_end = uuids.end(); - - for (;id != uuids_end; ++id) - { - if ( !LLAvatarActions::isFriend(*id) ) - { - result = false; - break; - } - } - - return result; - } - else if (item == std::string("can_call")) - { - return LLAvatarActions::canCall(); - } - else if (item == std::string("can_show_on_map")) - { - const LLUUID& id = uuids.front(); - - return (LLAvatarTracker::instance().isBuddyOnline(id) && is_agent_mappable(id)) - || gAgent.isGodlike(); - } - else if(item == std::string("can_offer_teleport")) - { - return LLAvatarActions::canOfferTeleport(uuids); - } - else if("can_moderate_voice" == item || "can_allow_text_chat" == item || "can_mute" == item || "can_unmute" == item) - { - return enableModerateContextMenuItem(item); - } - - return false; -} - -bool LLIMFloaterContainer::checkContextMenuItem(const LLSD& userdata) -{ - std::string item = userdata.asString(); - uuid_vec_t mUUIDs; - getParticipantUUIDs(mUUIDs); - - if(mUUIDs.size() > 0 ) - { - if ("is_blocked" == item) - { - return LLAvatarActions::isBlocked(mUUIDs.front()); - } - else if ("is_allowed_text_chat" == item) - { - const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); - - if (NULL != speakerp) - { - return !speakerp->mModeratorMutedText; - } - } - } - - return false; -} - -void LLIMFloaterContainer::showConversation(const LLUUID& session_id) -{ - setVisibleAndFrontmost(false); - selectConversation(session_id); -} - -// Will select only the conversation item -void LLIMFloaterContainer::selectConversation(const LLUUID& session_id) -{ - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,session_id); - if (widget) - { - (widget->getRoot())->setSelection(widget, FALSE, FALSE); - } -} - -// Synchronous select the conversation item and the conversation floater -BOOL LLIMFloaterContainer::selectConversationPair(const LLUUID& session_id, bool select_widget) -{ - BOOL handled = TRUE; - - /* widget processing */ - if (select_widget) - { - LLFolderViewItem* widget = mConversationsWidgets[session_id]; - if (widget && widget->getParentFolder()) - { - widget->getParentFolder()->setSelection(widget, FALSE, FALSE); - } - } - - /* floater processing */ - - if (session_id != getSelectedSession()) - { - // Store the active session - setSelectedSession(session_id); - - LLIMConversation* session_floater = LLIMConversation::getConversation(session_id); - - if (session_floater->getHost()) - { - // Always expand the message pane if the panel is hosted by the container - collapseMessagesPane(false); - // Switch to the conversation floater that is being selected - selectFloater(session_floater); - } - - // Set the focus on the selected floater - if (!session_floater->hasFocus()) - { - session_floater->setFocus(TRUE); - } - } - - return handled; -} - -void LLIMFloaterContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) -{ - LLConversationItemSession* item = dynamic_cast(get_ptr_in_map(mConversationsItems,session_id)); - if (item) - { - item->setTimeNow(participant_id); - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); - } -} - -void LLIMFloaterContainer::setNearbyDistances() -{ - // Get the nearby chat session: that's the one with uuid nul - LLConversationItemSession* item = dynamic_cast(get_ptr_in_map(mConversationsItems,LLUUID())); - if (item) - { - // Get the positions of the nearby avatars and their ids - std::vector positions; - uuid_vec_t avatar_ids; - LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); - // Get the position of the agent - const LLVector3d& me_pos = gAgent.getPositionGlobal(); - // For each nearby avatar, compute and update the distance - int avatar_count = positions.size(); - for (int i = 0; i < avatar_count; i++) - { - F64 dist = dist_vec_squared(positions[i], me_pos); - item->setDistance(avatar_ids[i],dist); - } - // Also does it for the agent itself - item->setDistance(gAgent.getID(),0.0f); - // Request resort - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); - } -} - -LLConversationItem* LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid, bool isWidgetSelected /*= false*/) -{ - bool is_nearby_chat = uuid.isNull(); - - // Stores the display name for the conversation line item - std::string display_name = is_nearby_chat ? LLTrans::getString("NearbyChatLabel") : LLIMModel::instance().getName(uuid); - - // Check if the item is not already in the list, exit (nothing to do) - // Note: this happens often, when reattaching a torn off conversation for instance - conversations_items_map::iterator item_it = mConversationsItems.find(uuid); - if (item_it != mConversationsItems.end()) - { - return item_it->second; - } - - // Remove the conversation item that might exist already: it'll be recreated anew further down anyway - // and nothing wrong will happen removing it if it doesn't exist - removeConversationListItem(uuid,false); - - // Create a conversation session model - LLConversationItemSession* item = NULL; - LLSpeakerMgr* speaker_manager = (is_nearby_chat ? (LLSpeakerMgr*)(LLLocalSpeakerMgr::getInstance()) : LLIMModel::getInstance()->getSpeakerManager(uuid)); - if (speaker_manager) - { - item = new LLParticipantList(speaker_manager, getRootViewModel()); - } - if (!item) - { - llwarns << "Couldn't create conversation session item : " << display_name << llendl; - return NULL; - } - item->renameItem(display_name); - item->updateParticipantName(NULL); - - mConversationsItems[uuid] = item; - - // Create a widget from it - LLConversationViewSession* widget = createConversationItemWidget(item); - mConversationsWidgets[uuid] = widget; - - // Add a new conversation widget to the root folder of the folder view - widget->addToFolder(mConversationsRoot); - widget->requestArrange(); - - LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(uuid); - - // Create the participants widgets now - // Note: usually, we do not get an updated avatar list at that point - if (uuid.isNull() || im_sessionp && !im_sessionp->isP2PSessionType()) - { - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); - while (current_participant_model != end_participant_model) - { - LLConversationItem* participant_model = dynamic_cast(*current_participant_model); - LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); - participant_view->addToFolder(widget); - current_participant_model++; - } - } - // Do that too for the conversation dialog - LLIMConversation *conversation_floater = (uuid.isNull() ? (LLIMConversation*)(LLFloaterReg::findTypedInstance("nearby_chat")) : (LLIMConversation*)(LLIMFloater::findInstance(uuid))); - if (conversation_floater) - { - conversation_floater->buildConversationViewParticipant(); - } - - // set the widget to minimized mode if conversations pane is collapsed - widget->toggleMinimizedMode(mConversationsPane->isCollapsed()); - - if (isWidgetSelected) - { - selectConversation(uuid); - // scroll to newly added item - mConversationsRoot->scrollToShowSelection(); - } - - return item; -} - -bool LLIMFloaterContainer::removeConversationListItem(const LLUUID& uuid, bool change_focus) -{ - // Delete the widget and the associated conversation item - // Note : since the mConversationsItems is also the listener to the widget, deleting - // the widget will also delete its listener - bool isWidgetSelected = false; - LLFolderViewItem* new_selection = NULL; - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid); - if (widget) - { - isWidgetSelected = widget->isSelected(); - new_selection = mConversationsRoot->getNextFromChild(widget); - if(new_selection == NULL) - { - new_selection = mConversationsRoot->getPreviousFromChild(widget); - } - widget->destroyView(); - } - - // Suppress the conversation items and widgets from their respective maps - mConversationsItems.erase(uuid); - mConversationsWidgets.erase(uuid); - - // Don't let the focus fall IW, select and refocus on the first conversation in the list - if (change_focus) - { - setFocus(TRUE); - if(new_selection != NULL) - { - LLConversationItem* vmi = dynamic_cast(new_selection->getViewModelItem()); - if(vmi != NULL) - { - selectConversation(vmi->getUUID()); - } - } - } - return isWidgetSelected; -} - -LLConversationViewSession* LLIMFloaterContainer::createConversationItemWidget(LLConversationItem* item) -{ - LLConversationViewSession::Params params; - - params.name = item->getDisplayName(); - params.root = mConversationsRoot; - params.listener = item; - params.tool_tip = params.name; - params.container = this; - - return LLUICtrlFactory::create(params); -} - -LLConversationViewParticipant* LLIMFloaterContainer::createConversationViewParticipant(LLConversationItem* item) -{ - LLConversationViewParticipant::Params params; - LLRect panel_rect = mConversationsListPanel->getRect(); - - params.name = item->getDisplayName(); - params.root = mConversationsRoot; - params.listener = item; - - //24 is the the current hight of an item (itemHeight) loaded from conversation_view_participant.xml. - params.rect = LLRect (0, 24, panel_rect.getWidth(), 0); - params.tool_tip = params.name; - params.participant_id = item->getUUID(); - params.folder_indentation = 42; - - return LLUICtrlFactory::create(params); -} - -bool LLIMFloaterContainer::enableModerateContextMenuItem(const std::string& userdata) -{ - // only group moderators can perform actions related to this "enable callback" - if (!isGroupModerator()) - { - return false; - } - - LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); - if (NULL == speakerp) - { - return false; - } - - bool voice_channel = speakerp->isInVoiceChannel(); - - if ("can_moderate_voice" == userdata) - { - return voice_channel; - } - else if ("can_mute" == userdata) - { - return voice_channel && !isMuted(getCurSelectedViewModelItem()->getUUID()); - } - else if ("can_unmute" == userdata) - { - return voice_channel && isMuted(getCurSelectedViewModelItem()->getUUID()); - } - - // The last invoke is used to check whether the "can_allow_text_chat" will enabled - return LLVoiceClient::getInstance()->isParticipantAvatar(getCurSelectedViewModelItem()->getUUID()); -} - -bool LLIMFloaterContainer::isGroupModerator() -{ - LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant(); - if (NULL == speaker_manager) - { - llwarns << "Speaker manager is missing" << llendl; - return false; - } - - // Is session a group call/chat? - if(gAgent.isInGroup(speaker_manager->getSessionID())) - { - LLSpeaker * speaker = speaker_manager->findSpeaker(gAgentID).get(); - - // Is agent a moderator? - return speaker && speaker->mIsModerator; - } - - return false; -} - -void LLIMFloaterContainer::moderateVoice(const std::string& command, const LLUUID& userID) -{ - if (!gAgent.getRegion()) return; - - if (command.compare("selected")) - { - moderateVoiceAllParticipants(command.compare("mute_all")); - } - else - { - moderateVoiceParticipant(userID, isMuted(userID)); - } -} - -bool LLIMFloaterContainer::isMuted(const LLUUID& avatar_id) -{ - const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); - return NULL == speakerp ? true : speakerp->mStatus == LLSpeaker::STATUS_MUTED; -} - -void LLIMFloaterContainer::moderateVoiceAllParticipants(bool unmute) -{ - LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); - - if (NULL != speaker_managerp) - { - if (!unmute) - { - LLSD payload; - payload["session_id"] = speaker_managerp->getSessionID(); - LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback); - return; - } - - speaker_managerp->moderateVoiceAllParticipants(unmute); - } -} - -// static -void LLIMFloaterContainer::confirmMuteAllCallback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - // if Cancel pressed - if (option == 1) - { - return; - } - - const LLSD& payload = notification["payload"]; - const LLUUID& session_id = payload["session_id"]; - - LLIMSpeakerMgr * speaker_manager = dynamic_cast ( - LLIMModel::getInstance()->getSpeakerManager(session_id)); - if (speaker_manager) - { - speaker_manager->moderateVoiceAllParticipants(false); - } - - return; -} - -void LLIMFloaterContainer::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) -{ - LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); - - if (NULL != speaker_managerp) - { - speaker_managerp->moderateVoiceParticipant(avatar_id, unmute); - } -} - -LLSpeakerMgr * LLIMFloaterContainer::getSpeakerMgrForSelectedParticipant() -{ - LLFolderViewItem * selected_folder_itemp = mConversationsRoot->getCurSelectedItem(); - if (NULL == selected_folder_itemp) - { - llwarns << "Current selected item is null" << llendl; - return NULL; - } - - LLFolderViewFolder * conversation_itemp = selected_folder_itemp->getParentFolder(); - - conversations_widgets_map::const_iterator iter = mConversationsWidgets.begin(); - conversations_widgets_map::const_iterator end = mConversationsWidgets.end(); - const LLUUID * conversation_uuidp = NULL; - while(iter != end) - { - if (iter->second == conversation_itemp) - { - conversation_uuidp = &iter->first; - break; - } - ++iter; - } - if (NULL == conversation_uuidp) - { - llwarns << "Cannot find conversation item widget" << llendl; - return NULL; - } - - return conversation_uuidp->isNull() ? (LLSpeakerMgr *)LLLocalSpeakerMgr::getInstance() - : LLIMModel::getInstance()->getSpeakerManager(*conversation_uuidp); -} - -LLSpeaker * LLIMFloaterContainer::getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp) -{ - if (NULL == speaker_managerp) - { - llwarns << "Speaker manager is missing" << llendl; - return NULL; - } - - const LLConversationItem * participant_itemp = getCurSelectedViewModelItem(); - if (NULL == participant_itemp) - { - llwarns << "Cannot evaluate current selected view model item" << llendl; - return NULL; - } - - return speaker_managerp->findSpeaker(participant_itemp->getUUID()); -} - -void LLIMFloaterContainer::toggleAllowTextChat(const LLUUID& participant_uuid) -{ - LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); - if (NULL != speaker_managerp) - { - speaker_managerp->toggleAllowTextChat(participant_uuid); - } -} - -void LLIMFloaterContainer::openNearbyChat() -{ - // If there's only one conversation in the container and that conversation is the nearby chat - //(which it should be...), open it so to make the list of participants visible. This happens to be the most common case when opening the Chat floater. - if(mConversationsItems.size() == 1) - { - LLConversationViewSession* nearby_chat = dynamic_cast(mConversationsWidgets[LLUUID()]); - if (nearby_chat) - { - nearby_chat->setOpen(TRUE); - } - } -} - -void LLIMFloaterContainer::onNearbyChatClosed() -{ - // If nearby chat is the only remaining conversation and it is closed, close whole conversation floater as well - if (mConversationsItems.size() == 1) - closeFloater(); -} - -// EOF diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h deleted file mode 100644 index e42ed053cb..0000000000 --- a/indra/newview/llimfloatercontainer.h +++ /dev/null @@ -1,178 +0,0 @@ -/** - * @file llimfloatercontainer.h - * @brief Multifloater containing active IM sessions in separate tab container tabs - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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_LLIMFLOATERCONTAINER_H -#define LL_LLIMFLOATERCONTAINER_H - -#include -#include - -#include "llimview.h" -#include "llevents.h" -#include "llfloater.h" -#include "llmultifloater.h" -#include "llavatarpropertiesprocessor.h" -#include "llgroupmgr.h" -#include "lltrans.h" -#include "llconversationmodel.h" -#include "llconversationview.h" - -class LLButton; -class LLLayoutPanel; -class LLLayoutStack; -class LLTabContainer; -class LLIMFloaterContainer; -class LLSpeaker; -class LLSpeakerMgr; - -class LLIMFloaterContainer - : public LLMultiFloater - , public LLIMSessionObserver -{ -public: - LLIMFloaterContainer(const LLSD& seed); - virtual ~LLIMFloaterContainer(); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void draw(); - /*virtual*/ void setVisible(BOOL visible); - void onCloseFloater(LLUUID& id); - - /*virtual*/ void addFloater(LLFloater* floaterp, - BOOL select_added_floater, - LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); - - void showConversation(const LLUUID& session_id); - void selectConversation(const LLUUID& session_id); - BOOL selectConversationPair(const LLUUID& session_id, bool select_widget); - - /*virtual*/ void tabClose(); - - static LLFloater* getCurrentVoiceFloater(); - static LLIMFloaterContainer* findInstance(); - static LLIMFloaterContainer* getInstance(); - - static void onCurrentChannelChanged(const LLUUID& session_id); - - void collapseMessagesPane(bool collapse); - - // Callbacks - static void idle(void* user_data); - - // LLIMSessionObserver observe triggers - /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); - /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); - /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id); - /*virtual*/ void sessionRemoved(const LLUUID& session_id); - /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); - - LLConversationViewModel& getRootViewModel() { return mConversationViewModel; } - LLUUID getSelectedSession() { return mSelectedSession; } - void setSelectedSession(LLUUID sessionID) { mSelectedSession = sessionID; } - LLConversationItem* getSessionModel(const LLUUID& session_id) { return get_ptr_in_map(mConversationsItems,session_id); } - LLConversationSort& getSortOrder() { return mConversationViewModel.getSorter(); } - - void onNearbyChatClosed(); - -private: - typedef std::map avatarID_panel_map_t; - avatarID_panel_map_t mSessions; - boost::signals2::connection mNewMessageConnection; - - /*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height); - - void onNewMessageReceived(const LLSD& data); - - void onExpandCollapseButtonClicked(); - void processParticipantsStyleUpdate(); - - void collapseConversationsPane(bool collapse); - - void updateState(bool collapse, S32 delta_width); - - void onAddButtonClicked(); - void onAvatarPicked(const uuid_vec_t& ids); - - BOOL isActionChecked(const LLSD& userdata); - void onCustomAction (const LLSD& userdata); - void setSortOrderSessions(const LLConversationFilter::ESortOrderType order); - void setSortOrderParticipants(const LLConversationFilter::ESortOrderType order); - void setSortOrder(const LLConversationSort& order); - - void getSelectedUUIDs(uuid_vec_t& selected_uuids); - const LLConversationItem * getCurSelectedViewModelItem(); - void getParticipantUUIDs(uuid_vec_t& selected_uuids); - void doToSelected(const LLSD& userdata); - void doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS); - void doToParticipants(const std::string& item, uuid_vec_t& selectedIDS); - void doToSelectedGroup(const LLSD& userdata); - bool checkContextMenuItem(const LLSD& userdata); - bool enableContextMenuItem(const LLSD& userdata); - - static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); - bool enableModerateContextMenuItem(const std::string& userdata); - LLSpeaker * getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp); - LLSpeakerMgr * getSpeakerMgrForSelectedParticipant(); - bool isGroupModerator(); - bool isMuted(const LLUUID& avatar_id); - void moderateVoice(const std::string& command, const LLUUID& userID); - void moderateVoiceAllParticipants(bool unmute); - void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute); - void toggleAllowTextChat(const LLUUID& participant_uuid); - void openNearbyChat(); - - LLButton* mExpandCollapseBtn; - LLLayoutPanel* mMessagesPane; - LLLayoutPanel* mConversationsPane; - LLLayoutStack* mConversationsStack; - - bool mInitialized; - - LLUUID mSelectedSession; - - // Conversation list implementation -public: - bool removeConversationListItem(const LLUUID& uuid, bool change_focus = true); - LLConversationItem* addConversationListItem(const LLUUID& uuid, bool isWidgetSelected = false); - void setTimeNow(const LLUUID& session_id, const LLUUID& participant_id); - void setNearbyDistances(); - -private: - LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); - LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item); - bool onConversationModelEvent(const LLSD& event); - - // Conversation list data - LLPanel* mConversationsListPanel; // This is the main widget we add conversation widget to - conversations_items_map mConversationsItems; - conversations_widgets_map mConversationsWidgets; - LLConversationViewModel mConversationViewModel; - LLFolderView* mConversationsRoot; - LLEventStream mConversationsEventStream; -}; - -#endif // LL_LLIMFLOATERCONTAINER_H diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 0250af6a0e..c64ecdc47a 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -171,7 +171,7 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& session_label, // enable line history support for instant message bar mInputEditor->setEnableLineHistory(TRUE); - //*TODO we probably need the same "awaiting message" thing in LLIMFloater + //*TODO we probably need the same "awaiting message" thing in LLFloaterIMSession LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionUUID); if (!im_session) { diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 9f24a5372f..6712127750 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -41,15 +41,15 @@ #include "lltextutil.h" #include "lltrans.h" #include "lluictrlfactory.h" -#include "llimconversation.h" +#include "llfloaterimsessiontab.h" #include "llagent.h" #include "llagentui.h" #include "llappviewer.h" #include "llavatariconctrl.h" #include "llcallingcard.h" #include "llchat.h" -#include "llimfloater.h" -#include "llimfloatercontainer.h" +#include "llfloaterimsession.h" +#include "llfloaterimcontainer.h" #include "llgroupiconctrl.h" #include "llmd5.h" #include "llmutelist.h" @@ -58,7 +58,7 @@ #include "llviewerwindow.h" #include "llnotifications.h" #include "llnotificationsutil.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llspeakers.h" //for LLIMSpeakerMgr #include "lltextbox.h" #include "lltoolbarview.h" @@ -109,7 +109,7 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id, args["FROM"] = av_name.getCompleteName(); args["FROM_ID"] = msg["from_id"]; args["SESSION_ID"] = msg["session_id"]; - LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloaterContainer::showConversation, LLIMFloaterContainer::getInstance(), msg["session_id"].asUUID())); + LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLFloaterIMContainer::showConversation, LLFloaterIMContainer::getInstance(), msg["session_id"].asUUID())); } void toast_callback(const LLSD& msg){ @@ -120,7 +120,7 @@ void toast_callback(const LLSD& msg){ } // Skip toasting if we have open window of IM with this session id - LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]); + LLFloaterIMSession* open_im_floater = LLFloaterIMSession::findInstance(msg["session_id"]); if ( open_im_floater && open_im_floater->isInVisibleChain() @@ -160,7 +160,7 @@ void toast_callback(const LLSD& msg){ LLIMModel::LLIMModel() { - addNewMsgCallback(boost::bind(&LLIMFloater::newIMCallback, _1)); + addNewMsgCallback(boost::bind(&LLFloaterIMSession::newIMCallback, _1)); addNewMsgCallback(boost::bind(&toast_callback, _1)); } @@ -638,7 +638,7 @@ void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, con mId2SessionMap[new_session_id] = session; } - LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(old_session_id); if (im_floater) { im_floater->sessionInitReplyReceived(new_session_id); @@ -1387,7 +1387,7 @@ public: && LLIMModel::getInstance()->findIMSession(mSessionID)) { // TODO remove in 2010, for voice calls we do not open an IM window - //LLIMFloater::show(mSessionID); + //LLFloaterIMSession::show(mSessionID); } gIMMgr->clearPendingAgentListUpdates(mSessionID); @@ -1531,7 +1531,7 @@ LLIMMgr::onConfirmForceCloseError( //only 1 option really LLUUID session_id = notification["payload"]["session_id"]; - LLFloater* floater = LLIMFloater::findInstance(session_id); + LLFloater* floater = LLFloaterIMSession::findInstance(session_id); if ( floater ) { floater->closeFloater(FALSE); @@ -2397,7 +2397,7 @@ LLIMMgr::LLIMMgr() mPendingInvitations = LLSD::emptyMap(); mPendingAgentListUpdates = LLSD::emptyMap(); - LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLIMFloater::sRemoveTypingIndicator, _1)); + LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLFloaterIMSession::sRemoveTypingIndicator, _1)); } // Add a message to a session. @@ -2492,7 +2492,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); if (nearby_chat) { nearby_chat->addMessage(chat); @@ -2618,12 +2618,12 @@ LLUUID LLIMMgr::addSession( if (floater_id.notNull()) { - LLIMFloater* im_floater = LLIMFloater::findInstance(floater_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(floater_id); if (im_floater && im_floater->getStartConferenceInSameFloater()) { // The IM floater should be initialized with a new session_id - // so that it is found by that id when creating a chiclet in LLIMFloater::onIMChicletCreated, + // so that it is found by that id when creating a chiclet in LLFloaterIMSession::onIMChicletCreated, // and a new floater is not created. im_floater->initIMSession(session_id); } @@ -2841,7 +2841,7 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) { - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); if ( im_floater ) { im_floater->processAgentListUpdates(body); @@ -3115,7 +3115,7 @@ void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info) void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing) { LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID); - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); if ( im_floater ) { im_floater->processIMTyping(im_info, typing); @@ -3160,7 +3160,7 @@ public: speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id)); } - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); if ( im_floater ) { if ( body.has("session_info") ) @@ -3254,7 +3254,7 @@ public: const LLSD& input) const { LLUUID session_id = input["body"]["session_id"].asUUID(); - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); if ( im_floater ) { im_floater->processSessionUpdate(input["body"]["info"]); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 8b04af71c7..ffac67557a 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -46,7 +46,7 @@ #include "llfriendcard.h" #include "llgesturemgr.h" #include "llgiveinventory.h" -#include "llimfloatercontainer.h" +#include "llfloaterimcontainer.h" #include "llimview.h" #include "llclipboard.h" #include "llinventorydefines.h" @@ -4683,7 +4683,7 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID()); if (session_id != LLUUID::null) { - LLIMFloaterContainer::getInstance()->showConversation(session_id); + LLFloaterIMContainer::getInstance()->showConversation(session_id); } } } diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index dafc71b59c..7c717af840 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -39,7 +39,7 @@ #include "llfloatersidepanelcontainer.h" #include "llfolderview.h" #include "llfolderviewitem.h" -#include "llimfloatercontainer.h" +#include "llfloaterimcontainer.h" #include "llimview.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -1087,7 +1087,7 @@ bool LLInventoryPanel::beginIMSession() LLUUID session_id = gIMMgr->addSession(name, type, members[0], members); if (session_id != LLUUID::null) { - LLIMFloaterContainer::getInstance()->showConversation(session_id); + LLFloaterIMContainer::getInstance()->showConversation(session_id); } return true; diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp deleted file mode 100644 index dbdf460785..0000000000 --- a/indra/newview/llnearbychat.cpp +++ /dev/null @@ -1,867 +0,0 @@ -/** - * @file LLNearbyChat.cpp - * @brief LLNearbyChat class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" - -#include "message.h" - -#include "lliconctrl.h" -#include "llappviewer.h" -#include "llchatentry.h" -#include "llfloaterreg.h" -#include "lltrans.h" -#include "llimfloatercontainer.h" -#include "llfloatersidepanelcontainer.h" -#include "llfocusmgr.h" -#include "lllogchat.h" -#include "llresizebar.h" -#include "llresizehandle.h" -#include "lldraghandle.h" -#include "llmenugl.h" -#include "llviewermenu.h" // for gMenuHolder -#include "llnearbychathandler.h" -#include "llchannelmanager.h" -#include "llchathistory.h" -#include "llstylemap.h" -#include "llavatarnamecache.h" -#include "llfloaterreg.h" -#include "lltrans.h" - -#include "llfirstuse.h" -#include "llnearbychat.h" -#include "llagent.h" // gAgent -#include "llgesturemgr.h" -#include "llmultigesture.h" -#include "llkeyboard.h" -#include "llanimationstates.h" -#include "llviewerstats.h" -#include "llcommandhandler.h" -#include "llviewercontrol.h" -#include "llnavigationbar.h" -#include "llwindow.h" -#include "llviewerwindow.h" -#include "llrootview.h" -#include "llviewerchat.h" -#include "lltranslate.h" - -S32 LLNearbyChat::sLastSpecialChatChannel = 0; - -const S32 EXPANDED_HEIGHT = 266; -const S32 COLLAPSED_HEIGHT = 60; -const S32 EXPANDED_MIN_HEIGHT = 150; - -// legacy callback glue -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); - -struct LLChatTypeTrigger { - std::string name; - EChatType type; -}; - -static LLChatTypeTrigger sChatTypeTriggers[] = { - { "/whisper" , CHAT_TYPE_WHISPER}, - { "/shout" , CHAT_TYPE_SHOUT} -}; - - -LLNearbyChat::LLNearbyChat(const LLSD& llsd) -: LLIMConversation(llsd), - //mOutputMonitor(NULL), - mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) -{ - mIsP2PChat = false; - mIsNearbyChat = true; - setIsChrome(TRUE); - mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); - mSessionID = LLUUID(); -} - -//static -LLNearbyChat* LLNearbyChat::buildFloater(const LLSD& key) -{ - LLFloaterReg::getInstance("im_container"); - return new LLNearbyChat(key); -} - -//virtual -BOOL LLNearbyChat::postBuild() -{ - setIsSingleInstance(TRUE); - BOOL result = LLIMConversation::postBuild(); - mInputEditor->setCommitCallback(boost::bind(&LLNearbyChat::onChatBoxCommit, this)); - mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); - mInputEditor->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); - mInputEditor->setFocusReceivedCallback(boost::bind(&LLNearbyChat::onChatBoxFocusReceived, this)); - mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle")); - -// mOutputMonitor = getChild("chat_zone_indicator"); -// mOutputMonitor->setVisible(FALSE); - - // Register for font change notifications - LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChat::onChatFontChange, this, _1)); - - // title must be defined BEFORE call addConversationListItem() because - // it is used for show the item's name in the conversations list - setTitle(LLTrans::getString("NearbyChatTitle")); - - //for menu - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); - registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); - - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(menu) - { - mPopupMenuHandle = menu->getHandle(); - } - - // obsolete, but may be needed for backward compatibility? - gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", true); - - if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) - { - loadHistory(); - } - - return result; -} - -// virtual -void LLNearbyChat::refresh() -{ - displaySpeakingIndicator(); - updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); - - // *HACK: Update transparency type depending on whether our children have focus. - // This is needed because this floater is chrome and thus cannot accept focus, so - // the transparency type setting code from LLFloater::setFocus() isn't reached. - if (getTransparencyType() != TT_DEFAULT) - { - setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); - } -} - -void LLNearbyChat::onNearbySpeakers() -{ - LLSD param; - param["people_panel_tab_name"] = "nearby_panel"; - LLFloaterSidePanelContainer::showPanel("people", "panel_people", param); -} - -void LLNearbyChat::onNearbyChatContextMenuItemClicked(const LLSD& userdata) -{ -} - -bool LLNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) -{ - std::string str = userdata.asString(); - if(str == "nearby_people") - onNearbySpeakers(); - return false; -} - - -BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) -{ - //fix for EXT-6625 - //highlight NearbyChat history whenever mouseclick happen in NearbyChat - //setting focus to eidtor will force onFocusLost() call that in its turn will change - //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. - - if(mChatHistory) - { - mChatHistory->setFocus(TRUE); - } - - BOOL handled = LLPanel::handleMouseDown(x, y, mask); - setFocus(handled); - return handled; -} - -void LLNearbyChat::reloadMessages() -{ - mChatHistory->clear(); - - LLSD do_not_log; - do_not_log["do_not_log"] = true; - for(std::vector::iterator it = mMessageArchive.begin();it!=mMessageArchive.end();++it) - { - // Update the messages without re-writing them to a log file. - addMessage(*it,false, do_not_log); - } -} - -void LLNearbyChat::loadHistory() -{ - LLSD do_not_log; - do_not_log["do_not_log"] = true; - - std::list history; - LLLogChat::loadChatHistory("chat", history); - - std::list::const_iterator it = history.begin(); - while (it != history.end()) - { - const LLSD& msg = *it; - - std::string from = msg[IM_FROM]; - LLUUID from_id; - if (msg[IM_FROM_ID].isDefined()) - { - from_id = msg[IM_FROM_ID].asUUID(); - } - else - { - std::string legacy_name = gCacheName->buildLegacyName(from); - gCacheName->getUUID(legacy_name, from_id); - } - - LLChat chat; - chat.mFromName = from; - chat.mFromID = from_id; - chat.mText = msg[IM_TEXT].asString(); - chat.mTimeStr = msg[IM_TIME].asString(); - chat.mChatStyle = CHAT_STYLE_HISTORY; - - chat.mSourceType = CHAT_SOURCE_AGENT; - if (from_id.isNull() && SYSTEM_FROM == from) - { - chat.mSourceType = CHAT_SOURCE_SYSTEM; - - } - else if (from_id.isNull()) - { - chat.mSourceType = isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; - } - - addMessage(chat, true, do_not_log); - - it++; - } -} - -void LLNearbyChat::removeScreenChat() -{ - LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); - if(chat_channel) - { - chat_channel->removeToastsFromChannel(); - } -} - - -void LLNearbyChat::setVisible(BOOL visible) -{ - LLIMConversation::setVisible(visible); - - if(visible) - { - removeScreenChat(); - } - setFocus(visible); -} - -// virtual -void LLNearbyChat::onTearOffClicked() -{ - LLIMConversation::onTearOffClicked(); - - // see CHUI-170: Save torn-off state of the nearby chat between sessions - BOOL in_the_multifloater = !isTornOff(); - gSavedSettings.setBOOL("NearbyChatIsNotTornOff", in_the_multifloater); -} - - -// virtual -void LLNearbyChat::onOpen(const LLSD& key) -{ - LLIMConversation::onOpen(key); - showTranslationCheckbox(LLTranslate::isTranslationConfigured()); -} - -// virtual -void LLNearbyChat::onClose(bool app_quitting) -{ - // Override LLIMConversation::onClose() so that Nearby Chat is not removed from the conversation floater -} - -// virtual -void LLNearbyChat::onClickCloseBtn() -{ - if (!isTornOff()) - return; - onTearOffClicked(); - - LLIMFloaterContainer *im_box = LLIMFloaterContainer::findInstance(); - if (im_box) - { - im_box->onNearbyChatClosed(); - } -} - -void LLNearbyChat::onChatFontChange(LLFontGL* fontp) -{ - // Update things with the new font whohoo - if (mInputEditor) - { - mInputEditor->setFont(fontp); - } -} - - -void LLNearbyChat::show() -{ - if (isChatMultiTab()) - { - openFloater(getKey()); - } -} - -bool LLNearbyChat::isChatVisible() const -{ - bool isVisible = false; - LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); - // Is the IM floater container ever null? - llassert(im_box != NULL); - if (im_box != NULL) - { - isVisible = - isChatMultiTab() && gSavedSettings.getBOOL("NearbyChatIsNotTornOff")? - im_box->getVisible() && !im_box->isMinimized() : - getVisible() && !isMinimized(); - } - - return isVisible; -} - -void LLNearbyChat::showHistory() -{ - openFloater(); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); -} - -std::string LLNearbyChat::getCurrentChat() -{ - return mInputEditor ? mInputEditor->getText() : LLStringUtil::null; -} - -// virtual -BOOL LLNearbyChat::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - if( KEY_RETURN == key && mask == MASK_CONTROL) - { - // shout - sendChat(CHAT_TYPE_SHOUT); - handled = TRUE; - } - - return handled; -} - -BOOL LLNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) -{ - U32 in_len = in_str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - bool string_was_found = false; - - for (S32 n = 0; n < cnt && !string_was_found; n++) - { - if (in_len <= sChatTypeTriggers[n].name.length()) - { - 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 string_was_found; -} - -void LLNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userdata) -{ - LLFirstUse::otherAvatarChatFirst(false); - - LLNearbyChat* self = (LLNearbyChat *)userdata; - - LLWString raw_text = self->mInputEditor->getWText(); - - // Can't trim the end, because that will cause autocompletion - // to eat trailing spaces that might be part of a gesture. - LLWStringUtil::trimHead(raw_text); - - S32 length = raw_text.length(); - - if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences - { - gAgent.startTyping(); - } - else - { - gAgent.stopTyping(); - } - - /* Doesn't work -- can't tell the difference between a backspace - that killed the selection vs. backspace at the end of line. - if (length > 1 - && text[0] == '/' - && key == KEY_BACKSPACE) - { - // the selection will already be deleted, but we need to trim - // off the character before - std::string new_text = raw_text.substr(0, length-1); - self->mInputEditor->setText( new_text ); - self->mInputEditor->setCursorToEnd(); - length = length - 1; - } - */ - - KEY key = gKeyboard->currentKey(); - - // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL) - { - // we're starting a gesture, attempt to autocomplete - - std::string utf8_trigger = wstring_to_utf8str(raw_text); - std::string utf8_out_str(utf8_trigger); - - if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part - - // Select to end of line, starting from the character - // after the last one the user typed. - self->mInputEditor->selectNext(rest_of_match, false); - } - else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part - self->mInputEditor->endOfDoc(); - } - - //llinfos << "GESTUREDEBUG " << trigger - // << " len " << length - // << " outlen " << out_str.getLength() - // << llendl; - } -} - -// static -void LLNearbyChat::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) -{ - // stop typing animation - gAgent.stopTyping(); -} - -void LLNearbyChat::onChatBoxFocusReceived() -{ - mInputEditor->setEnabled(!gDisconnected); -} - -EChatType LLNearbyChat::processChatTypeTriggers(EChatType type, std::string &str) -{ - U32 length = str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (length >= sChatTypeTriggers[n].name.length()) - { - std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); - - if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) - { - U32 trigger_length = sChatTypeTriggers[n].name.length(); - - // It's to remove space after trigger name - if (length > trigger_length && str[trigger_length] == ' ') - trigger_length++; - - str = str.substr(trigger_length, length); - - if (CHAT_TYPE_NORMAL == type) - return sChatTypeTriggers[n].type; - else - break; - } - } - } - - return type; -} - -void LLNearbyChat::sendChat( EChatType type ) -{ - if (mInputEditor) - { - LLWString text = mInputEditor->getWText(); - LLWStringUtil::trim(text); - LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. - if (!text.empty()) - { - // Check if this is destined for another channel - S32 channel = 0; - stripChannelNumber(text, &channel); - - std::string utf8text = wstring_to_utf8str(text); - // Try to trigger a gesture, if not chat to a script. - std::string utf8_revised_text; - if (0 == channel) - { - // discard returned "found" boolean - LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); - } - else - { - utf8_revised_text = utf8text; - } - - utf8_revised_text = utf8str_trim(utf8_revised_text); - - type = processChatTypeTriggers(type, utf8_revised_text); - - if (!utf8_revised_text.empty()) - { - // Chat with animation - sendChatFromViewer(utf8_revised_text, type, TRUE); - } - } - - mInputEditor->setText(LLStringExplicit("")); - } - - gAgent.stopTyping(); - - // If the user wants to stop chatting on hitting return, lose focus - // and go out of chat mode. - if (gSavedSettings.getBOOL("CloseChatOnReturn")) - { - stopChat(); - } -} - -void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) -{ - appendMessage(chat, args); - - if(archive) - { - mMessageArchive.push_back(chat); - if(mMessageArchive.size()>200) - mMessageArchive.erase(mMessageArchive.begin()); - } - - // logging - if (!args["do_not_log"].asBoolean() - && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) - { - std::string from_name = chat.mFromName; - - if (chat.mSourceType == CHAT_SOURCE_AGENT) - { - // if the chat is coming from an agent, log the complete name - LLAvatarName av_name; - LLAvatarNameCache::get(chat.mFromID, &av_name); - - if (!av_name.mIsDisplayNameDefault) - { - from_name = av_name.getCompleteName(); - } - } - - LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); - } -} - - -void LLNearbyChat::onChatBoxCommit() -{ - if (mInputEditor->getText().length() > 0) - { - sendChat(CHAT_TYPE_NORMAL); - } - - gAgent.stopTyping(); -} - -void LLNearbyChat::displaySpeakingIndicator() -{ - LLSpeakerMgr::speaker_list_t speaker_list; - LLUUID id; - - id.setNull(); - mSpeakerMgr->update(TRUE); - mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); - - for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) - { - LLPointer s = *i; - if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) - { - id = s->mID; - break; - } - } - - if (!id.isNull()) - { - //mOutputMonitor->setVisible(TRUE); - //mOutputMonitor->setSpeakerId(id); - } - else - { - //mOutputMonitor->setVisible(FALSE); - } -} - -void LLNearbyChat::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) -{ - sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); -} - -void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) -{ - // Look for "/20 foo" channel chats. - S32 channel = 0; - LLWString out_text = stripChannelNumber(wtext, &channel); - std::string utf8_out_text = wstring_to_utf8str(out_text); - std::string utf8_text = wstring_to_utf8str(wtext); - - utf8_text = utf8str_trim(utf8_text); - if (!utf8_text.empty()) - { - utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); - } - - // Don't animate for chats people can't hear (chat to scripts) - if (animate && (channel == 0)) - { - if (type == CHAT_TYPE_WHISPER) - { - lldebugs << "You whisper " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_NORMAL) - { - lldebugs << "You say " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_SHOUT) - { - lldebugs << "You shout " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); - } - else - { - llinfos << "send_chat_from_viewer() - invalid volume" << llendl; - return; - } - } - else - { - if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) - { - lldebugs << "Channel chat: " << utf8_text << llendl; - } - } - - send_chat_from_viewer(utf8_out_text, type, channel); -} - -// static -bool LLNearbyChat::isWordsName(const std::string& name) -{ - // checking to see if it's display name plus username in parentheses - S32 open_paren = name.find(" (", 0); - S32 close_paren = name.find(')', 0); - - if (open_paren != std::string::npos && - close_paren == name.length()-1) - { - return true; - } - else - { - //checking for a single space - S32 pos = name.find(' ', 0); - return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; - } -} - -// static -void LLNearbyChat::startChat(const char* line) -{ - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); - if (nearby_chat) - { - nearby_chat->show(); - nearby_chat->setVisible(TRUE); - nearby_chat->setFocus(TRUE); - nearby_chat->mInputEditor->setFocus(TRUE); - - if (line) - { - std::string line_string(line); - nearby_chat->mInputEditor->setText(line_string); - } - - nearby_chat->mInputEditor->endOfDoc(); - } -} - -// Exit "chat mode" and do the appropriate focus changes -// static -void LLNearbyChat::stopChat() -{ - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); - if (nearby_chat) - { - nearby_chat->mInputEditor->setFocus(FALSE); - gAgent.stopTyping(); - } -} - -// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. -// Otherwise returns input and channel 0. -LLWString LLNearbyChat::stripChannelNumber(const LLWString &mesg, S32* channel) -{ - if (mesg[0] == '/' - && mesg[1] == '/') - { - // This is a "repeat channel send" - *channel = sLastSpecialChatChannel; - return mesg.substr(2, mesg.length() - 2); - } - else if (mesg[0] == '/' - && mesg[1] - && LLStringOps::isDigit(mesg[1])) - { - // This a special "/20" speak on a channel - S32 pos = 0; - - // Copy the channel number into a string - LLWString channel_string; - llwchar c; - do - { - c = mesg[pos+1]; - channel_string.push_back(c); - pos++; - } - while(c && pos < 64 && LLStringOps::isDigit(c)); - - // Move the pointer forward to the first non-whitespace char - // Check isspace before looping, so we can handle "/33foo" - // as well as "/33 foo" - while(c && iswspace(c)) - { - c = mesg[pos+1]; - pos++; - } - - sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); - *channel = sLastSpecialChatChannel; - return mesg.substr(pos, mesg.length() - pos); - } - else - { - // This is normal chat. - *channel = 0; - return mesg; - } -} - -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ChatFromViewer); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ChatData); - msg->addStringFast(_PREHASH_Message, utf8_out_text); - msg->addU8Fast(_PREHASH_Type, type); - msg->addS32("Channel", channel); - - gAgent.sendReliableMessage(); - - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); -} - -class LLChatCommandHandler : public LLCommandHandler -{ -public: - // not allowed from outside the app - LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } - - // Your code here - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) - { - bool retval = false; - // Need at least 2 tokens to have a valid message. - if (tokens.size() < 2) - { - retval = false; - } - else - { - S32 channel = tokens[0].asInteger(); - // VWR-19499 Restrict function to chat channels greater than 0. - if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) - { - retval = true; - // Send unescaped message, see EXT-6353. - std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); - send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); - } - else - { - retval = false; - // Tell us this is an unsupported SLurl. - } - } - return retval; - } -}; - -// Creating the object registers with the dispatcher. -LLChatCommandHandler gChatHandler; diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h deleted file mode 100644 index c6a2637e8f..0000000000 --- a/indra/newview/llnearbychat.h +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file llnearbychat.h - * @brief LLNearbyChat class definition - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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_LLNEARBYCHAT_H -#define LL_LLNEARBYCHAT_H - -#include "llimconversation.h" -#include "llcombobox.h" -#include "llgesturemgr.h" -#include "llchat.h" -#include "llvoiceclient.h" -#include "lloutputmonitorctrl.h" -#include "llspeakers.h" -#include "llscrollbar.h" -#include "llviewerchat.h" -#include "llpanel.h" - -class LLResizeBar; - -class LLNearbyChat - : public LLIMConversation -{ -public: - // constructor for inline chat-bars (e.g. hosted in chat history window) - LLNearbyChat(const LLSD& key = LLSD(LLUUID())); - ~LLNearbyChat() {} - - static LLNearbyChat* buildFloater(const LLSD& key); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void onClose(bool app_quitting); - /*virtual*/ void setVisible(BOOL visible); - - void loadHistory(); - void reloadMessages(); - void removeScreenChat(); - - void addToHost(); - void show(); - bool isChatVisible() const; - - /** @param archive true - to save a message to the chat history log */ - void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); - void onNearbyChatContextMenuItemClicked(const LLSD& userdata); - bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); - - LLChatEntry* getChatBox() { return mInputEditor; } - - std::string getCurrentChat(); - - virtual BOOL handleKeyHere( KEY key, MASK mask ); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - - static void startChat(const char* line); - static void stopChat(); - - static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); - static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - - static bool isWordsName(const std::string& name); - - void showHistory(); - -protected: - static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); - static void onChatBoxKeystroke(LLTextEditor* caller, void* userdata); - static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); - void onChatBoxFocusReceived(); - - void sendChat( EChatType type ); - void onChatBoxCommit(); - void onChatFontChange(LLFontGL* fontp); - - /*virtual*/ void onTearOffClicked(); - /*virtual*/ void onClickCloseBtn(); - - static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); - EChatType processChatTypeTriggers(EChatType type, std::string &str); - - void displaySpeakingIndicator(); - - // Which non-zero channel did we last chat on? - static S32 sLastSpecialChatChannel; - - LLOutputMonitorCtrl* mOutputMonitor; - LLLocalSpeakerMgr* mSpeakerMgr; - - S32 mExpandedHeight; - -private: - - void onNearbySpeakers (); - - /*virtual*/ void refresh(); - - LLHandle mPopupMenuHandle; - std::vector mMessageArchive; - -}; - -#endif diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llnearbychatbarlistener.cpp deleted file mode 100644 index 61815d1864..0000000000 --- a/indra/newview/llnearbychatbarlistener.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @file llnearbychatbarlistener.cpp - * @author Dave Simmons - * @date 2011-03-15 - * @brief Implementation for LLNearbyChatBarListener. - * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, 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 "llviewerprecompiledheaders.h" - -#include "llnearbychatbarlistener.h" -#include "llnearbychat.h" - -#include "llagent.h" -#include "llchat.h" - - - -LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChat & chatbar) - : LLEventAPI("LLChatBar", - "LLChatBar listener to (e.g.) sendChat, etc."), - mChatbar(chatbar) -{ - add("sendChat", - "Send chat to the simulator:\n" - "[\"message\"] chat message text [required]\n" - "[\"channel\"] chat channel number [default = 0]\n" - "[\"type\"] chat type \"whisper\", \"normal\", \"shout\" [default = \"normal\"]", - &LLNearbyChatBarListener::sendChat); -} - - -// "sendChat" command -void LLNearbyChatBarListener::sendChat(LLSD const & chat_data) const -{ - // Extract the data - std::string chat_text = chat_data["message"].asString(); - - S32 channel = 0; - if (chat_data.has("channel")) - { - channel = chat_data["channel"].asInteger(); - if (channel < 0 || channel >= CHAT_CHANNEL_DEBUG) - { // Use 0 up to (but not including) CHAT_CHANNEL_DEBUG - channel = 0; - } - } - - EChatType type_o_chat = CHAT_TYPE_NORMAL; - if (chat_data.has("type")) - { - std::string type_string = chat_data["type"].asString(); - if (type_string == "whisper") - { - type_o_chat = CHAT_TYPE_WHISPER; - } - else if (type_string == "shout") - { - type_o_chat = CHAT_TYPE_SHOUT; - } - } - - // Have to prepend /42 style channel numbers - std::string chat_to_send; - if (channel == 0) - { - chat_to_send = chat_text; - } - else - { - chat_to_send += "/"; - chat_to_send += chat_data["channel"].asString(); - chat_to_send += " "; - chat_to_send += chat_text; - } - - // Send it as if it was typed in - mChatbar.sendChatFromViewer(chat_to_send, type_o_chat, (BOOL)(channel == 0)); -} - diff --git a/indra/newview/llnearbychatbarlistener.h b/indra/newview/llnearbychatbarlistener.h deleted file mode 100644 index 0537275424..0000000000 --- a/indra/newview/llnearbychatbarlistener.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file llnearbychatbarlistener.h - * @author Dave Simmons - * @date 2011-03-15 - * @brief Class definition for LLNearbyChatBarListener. - * - * $LicenseInfo:firstyear=2011&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2011, 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_LLNEARBYCHATBARLISTENER_H -#define LL_LLNEARBYCHATBARLISTENER_H - -#include "lleventapi.h" - -class LLSD; -class LLNearbyChat; - -class LLNearbyChatBarListener : public LLEventAPI -{ -public: - LLNearbyChatBarListener(LLNearbyChat & chatbar); - -private: - void sendChat(LLSD const & chat_data) const; - - LLNearbyChat & mChatbar; -}; - -#endif // LL_LLNEARBYCHATBARLISTENER_H - diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp deleted file mode 100644 index 1494d9d6ee..0000000000 --- a/indra/newview/llnearbychathandler.cpp +++ /dev/null @@ -1,630 +0,0 @@ -/** - * @file LLNearbyChatHandler.cpp - * @brief Nearby chat chat managment - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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 "llviewerprecompiledheaders.h" - -#include "llagentdata.h" // for gAgentID -#include "llnearbychathandler.h" - -#include "llchatitemscontainerctrl.h" -#include "llfirstuse.h" -#include "llfloaterscriptdebug.h" -#include "llhints.h" -#include "llnearbychat.h" -#include "llrecentpeople.h" - -#include "llviewercontrol.h" - -#include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance -#include "llviewerwindow.h"//for screen channel position -#include "llnearbychat.h" -#include "llrootview.h" -#include "lllayoutstack.h" - -//add LLNearbyChatHandler to LLNotificationsUI namespace -using namespace LLNotificationsUI; - -static LLNearbyChatToastPanel* createToastPanel() -{ - LLNearbyChatToastPanel* item = LLNearbyChatToastPanel::createInstance(); - return item; -} - - -//----------------------------------------------------------------------------------------------- -//LLNearbyChatScreenChannel -//----------------------------------------------------------------------------------------------- - -class LLNearbyChatScreenChannel: public LLScreenChannelBase -{ - LOG_CLASS(LLNearbyChatScreenChannel); -public: - typedef std::vector > toast_vec_t; - typedef std::list > toast_list_t; - - LLNearbyChatScreenChannel(const Params& p) - : LLScreenChannelBase(p) - { - mStopProcessing = false; - - LLControlVariable* ctrl = gSavedSettings.getControl("NearbyToastLifeTime").get(); - if (ctrl) - { - ctrl->getSignal()->connect(boost::bind(&LLNearbyChatScreenChannel::updateToastsLifetime, this)); - } - - ctrl = gSavedSettings.getControl("NearbyToastFadingTime").get(); - if (ctrl) - { - ctrl->getSignal()->connect(boost::bind(&LLNearbyChatScreenChannel::updateToastFadingTime, this)); - } - } - - void addChat (LLSD& chat); - void arrangeToasts (); - - typedef boost::function create_toast_panel_callback_t; - void setCreatePanelCallback(create_toast_panel_callback_t value) { m_create_toast_panel_callback_t = value;} - - void onToastDestroyed (LLToast* toast, bool app_quitting); - void onToastFade (LLToast* toast); - - void redrawToasts() - { - arrangeToasts(); - } - - // hide all toasts from screen, but not remove them from a channel - // removes all toasts from a channel - virtual void removeToastsFromChannel() - { - for(toast_vec_t::iterator it = m_active_toasts.begin(); it != m_active_toasts.end(); ++it) - { - addToToastPool(it->get()); - } - m_active_toasts.clear(); - }; - - virtual void deleteAllChildren() - { - LL_DEBUGS("NearbyChat") << "Clearing toast pool" << llendl; - m_toast_pool.clear(); - m_active_toasts.clear(); - LLScreenChannelBase::deleteAllChildren(); - } - -protected: - void deactivateToast(LLToast* toast); - void addToToastPool(LLToast* toast) - { - if (!toast) return; - LL_DEBUGS("NearbyChat") << "Pooling toast" << llendl; - toast->setVisible(FALSE); - toast->stopTimer(); - toast->setIsHidden(true); - - // Nearby chat toasts that are hidden, not destroyed. They are collected to the toast pool, so that - // they can be used next time, this is done for performance. But if the toast lifetime was changed - // (from preferences floater (STORY-36)) while it was shown (at this moment toast isn't in the pool yet) - // changes don't take affect. - // So toast's lifetime should be updated each time it's added to the pool. Otherwise viewer would have - // to be restarted so that changes take effect. - toast->setLifetime(gSavedSettings.getS32("NearbyToastLifeTime")); - toast->setFadingTime(gSavedSettings.getS32("NearbyToastFadingTime")); - m_toast_pool.push_back(toast->getHandle()); - } - - void createOverflowToast(S32 bottom, F32 timer); - - void updateToastsLifetime(); - - void updateToastFadingTime(); - - create_toast_panel_callback_t m_create_toast_panel_callback_t; - - bool createPoolToast(); - - toast_vec_t m_active_toasts; - toast_list_t m_toast_pool; - - bool mStopProcessing; - bool mChannelRect; -}; - - - -//----------------------------------------------------------------------------------------------- -// LLNearbyChatToast -//----------------------------------------------------------------------------------------------- - -// We're deriving from LLToast to be able to override onClose() -// in order to handle closing nearby chat toasts properly. -class LLNearbyChatToast : public LLToast -{ - LOG_CLASS(LLNearbyChatToast); -public: - LLNearbyChatToast(const LLToast::Params& p, LLNearbyChatScreenChannel* nc_channelp) - : LLToast(p), - mNearbyChatScreenChannelp(nc_channelp) - { - } - - /*virtual*/ void onClose(bool app_quitting); - -private: - LLNearbyChatScreenChannel* mNearbyChatScreenChannelp; -}; - -//----------------------------------------------------------------------------------------------- -// LLNearbyChatScreenChannel -//----------------------------------------------------------------------------------------------- - -void LLNearbyChatScreenChannel::deactivateToast(LLToast* toast) -{ - toast_vec_t::iterator pos = std::find(m_active_toasts.begin(), m_active_toasts.end(), toast->getHandle()); - - if (pos == m_active_toasts.end()) - { - llassert(pos == m_active_toasts.end()); - return; - } - - LL_DEBUGS("NearbyChat") << "Deactivating toast" << llendl; - m_active_toasts.erase(pos); -} - -void LLNearbyChatScreenChannel::createOverflowToast(S32 bottom, F32 timer) -{ - //we don't need overflow toast in nearby chat -} - -void LLNearbyChatScreenChannel::onToastDestroyed(LLToast* toast, bool app_quitting) -{ - LL_DEBUGS("NearbyChat") << "Toast destroyed (app_quitting=" << app_quitting << ")" << llendl; - - if (app_quitting) - { - // Viewer is quitting. - // Immediately stop processing chat messages (EXT-1419). - mStopProcessing = true; -} - else - { - // The toast is being closed by user (STORM-192). - // Remove it from the list of active toasts to prevent - // further references to the invalid pointer. - deactivateToast(toast); - } -} - -void LLNearbyChatScreenChannel::onToastFade(LLToast* toast) -{ - LL_DEBUGS("NearbyChat") << "Toast fading" << llendl; - - //fade mean we put toast to toast pool - if(!toast) - return; - - deactivateToast(toast); - - addToToastPool(toast); - - arrangeToasts(); -} - -void LLNearbyChatScreenChannel::updateToastsLifetime() -{ - S32 seconds = gSavedSettings.getS32("NearbyToastLifeTime"); - toast_list_t::iterator it; - - for(it = m_toast_pool.begin(); it != m_toast_pool.end(); ++it) - { - (*it).get()->setLifetime(seconds); - } -} - -void LLNearbyChatScreenChannel::updateToastFadingTime() -{ - S32 seconds = gSavedSettings.getS32("NearbyToastFadingTime"); - toast_list_t::iterator it; - - for(it = m_toast_pool.begin(); it != m_toast_pool.end(); ++it) - { - (*it).get()->setFadingTime(seconds); - } -} - -bool LLNearbyChatScreenChannel::createPoolToast() -{ - LLNearbyChatToastPanel* panel= m_create_toast_panel_callback_t(); - if(!panel) - return false; - - LLToast::Params p; - p.panel = panel; - p.lifetime_secs = gSavedSettings.getS32("NearbyToastLifeTime"); - p.fading_time_secs = gSavedSettings.getS32("NearbyToastFadingTime"); - - LLToast* toast = new LLNearbyChatToast(p, this); - - - toast->setOnFadeCallback(boost::bind(&LLNearbyChatScreenChannel::onToastFade, this, _1)); - - // If the toast gets somehow prematurely destroyed, deactivate it to prevent crash (STORM-1352). - toast->setOnToastDestroyedCallback(boost::bind(&LLNearbyChatScreenChannel::onToastDestroyed, this, _1, false)); - - LL_DEBUGS("NearbyChat") << "Creating and pooling toast" << llendl; - m_toast_pool.push_back(toast->getHandle()); - return true; -} - -void LLNearbyChatScreenChannel::addChat(LLSD& chat) -{ - //look in pool. if there is any message - if(mStopProcessing) - return; - - /* - find last toast and check ID - */ - - if(m_active_toasts.size()) - { - LLUUID fromID = chat["from_id"].asUUID(); // agent id or object id - std::string from = chat["from"].asString(); - LLToast* toast = m_active_toasts[0].get(); - if (toast) - { - LLNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); - - if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText()) - { - panel->addMessage(chat); - toast->reshapeToPanel(); - toast->startTimer(); - - arrangeToasts(); - return; - } - } - } - - - - if(m_toast_pool.empty()) - { - //"pool" is empty - create one more panel - LL_DEBUGS("NearbyChat") << "Empty pool" << llendl; - if(!createPoolToast())//created toast will go to pool. so next call will find it - return; - addChat(chat); - return; - } - - int chat_type = chat["chat_type"].asInteger(); - - if( ((EChatType)chat_type == CHAT_TYPE_DEBUG_MSG)) - { - if(gSavedSettings.getBOOL("ShowScriptErrors") == FALSE) - return; - if(gSavedSettings.getS32("ShowScriptErrorsLocation")== 1) - return; - } - - - //take 1st element from pool, (re)initialize it, put it in active toasts - - LL_DEBUGS("NearbyChat") << "Getting toast from pool" << llendl; - LLToast* toast = m_toast_pool.back().get(); - - m_toast_pool.pop_back(); - - - LLNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); - if(!panel) - return; - panel->init(chat); - - toast->reshapeToPanel(); - toast->startTimer(); - - m_active_toasts.push_back(toast->getHandle()); - - arrangeToasts(); -} - -static bool sort_toasts_predicate(LLHandle first, LLHandle second) -{ - if (!first.get() || !second.get()) return false; // STORM-1352 - - F32 v1 = first.get()->getTimeLeftToLive(); - F32 v2 = second.get()->getTimeLeftToLive(); - return v1 > v2; -} - -void LLNearbyChatScreenChannel::arrangeToasts() -{ - if(mStopProcessing || isHovering()) - return; - - if (mFloaterSnapRegion == NULL) - { - mFloaterSnapRegion = gViewerWindow->getRootView()->getChildView("floater_snap_region"); - } - - if (!getParent()) - { - // connect to floater snap region just to get resize events, we don't care about being a proper widget - mFloaterSnapRegion->addChild(this); - setFollows(FOLLOWS_ALL); - } - - LLRect toast_rect; - updateRect(); - - LLRect channel_rect; - mFloaterSnapRegion->localRectToOtherView(mFloaterSnapRegion->getLocalRect(), &channel_rect, gFloaterView); - channel_rect.mLeft += 10; - channel_rect.mRight = channel_rect.mLeft + 300; - - S32 channel_bottom = channel_rect.mBottom; - - S32 bottom = channel_bottom + 80; - S32 margin = gSavedSettings.getS32("ToastGap"); - - //sort active toasts - std::sort(m_active_toasts.begin(),m_active_toasts.end(),sort_toasts_predicate); - - //calc max visible item and hide other toasts. - - for(toast_vec_t::iterator it = m_active_toasts.begin(); it != m_active_toasts.end(); ++it) - { - LLToast* toast = it->get(); - if (!toast) - { - llwarns << "NULL found in the active chat toasts list!" << llendl; - continue; - } - - S32 toast_top = bottom + toast->getRect().getHeight() + margin; - - if(toast_top > channel_rect.getHeight()) - { - while(it!=m_active_toasts.end()) - { - addToToastPool(it->get()); - it=m_active_toasts.erase(it); - } - break; - } - - toast_rect = toast->getRect(); - toast_rect.setLeftTopAndSize(channel_rect.mLeft , bottom + toast_rect.getHeight(), toast_rect.getWidth() ,toast_rect.getHeight()); - - toast->setRect(toast_rect); - bottom += toast_rect.getHeight() - toast->getTopPad() + margin; - } - - // use reverse order to provide correct z-order and avoid toast blinking - - for(toast_vec_t::reverse_iterator it = m_active_toasts.rbegin(); it != m_active_toasts.rend(); ++it) - { - LLToast* toast = it->get(); - if (toast) - { - toast->setIsHidden(false); - toast->setVisible(TRUE); - } - } - -} - - - -//----------------------------------------------------------------------------------------------- -//LLNearbyChatHandler -//----------------------------------------------------------------------------------------------- -boost::scoped_ptr LLNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat")); - -LLNearbyChatHandler::LLNearbyChatHandler() -{ - // Getting a Channel for our notifications - LLNearbyChatScreenChannel::Params p; - p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); - LLNearbyChatScreenChannel* channel = new LLNearbyChatScreenChannel(p); - - LLNearbyChatScreenChannel::create_toast_panel_callback_t callback = createToastPanel; - - channel->setCreatePanelCallback(callback); - - LLChannelManager::getInstance()->addChannel(channel); - - mChannel = channel->getHandle(); -} - -LLNearbyChatHandler::~LLNearbyChatHandler() -{ -} - - -void LLNearbyChatHandler::initChannel() -{ - //LLRect snap_rect = gFloaterView->getSnapRect(); - //mChannel->init(snap_rect.mLeft, snap_rect.mLeft + 200); -} - - - -void LLNearbyChatHandler::processChat(const LLChat& chat_msg, - const LLSD &args) -{ - if(chat_msg.mMuted == TRUE) - return; - - if(chat_msg.mText.empty()) - return;//don't process empty messages - - LLFloaterReg::getInstance("im_container"); - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); - - // Build notification data - LLSD chat; - chat["message"] = chat_msg.mText; - chat["from"] = chat_msg.mFromName; - chat["from_id"] = chat_msg.mFromID; - chat["time"] = chat_msg.mTime; - chat["source"] = (S32)chat_msg.mSourceType; - chat["chat_type"] = (S32)chat_msg.mChatType; - chat["chat_style"] = (S32)chat_msg.mChatStyle; - // Pass sender info so that it can be rendered properly (STORM-1021). - chat["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); - - if (chat_msg.mChatType == CHAT_TYPE_DIRECT && - chat_msg.mText.length() > 0 && - chat_msg.mText[0] == '@') - { - // Send event on to LLEventStream and exit - sChatWatcher->post(chat); - return; - } - - // don't show toast and add message to chat history on receive debug message - // with disabled setting showing script errors or enabled setting to show script - // errors in separate window. - if (chat_msg.mChatType == CHAT_TYPE_DEBUG_MSG) - { - if(gSavedSettings.getBOOL("ShowScriptErrors") == FALSE) - return; - - // don't process debug messages from not owned objects, see EXT-7762 - if (gAgentID != chat_msg.mOwnerID) - { - return; - } - - if (gSavedSettings.getS32("ShowScriptErrorsLocation")== 1)// show error in window //("ScriptErrorsAsChat")) - { - - LLColor4 txt_color; - - LLViewerChat::getChatColor(chat_msg,txt_color); - - LLFloaterScriptDebug::addScriptLine(chat_msg.mText, - chat_msg.mFromName, - txt_color, - chat_msg.mFromID); - return; - } - } - - nearby_chat->addMessage(chat_msg, true, args); - - if(chat_msg.mSourceType == CHAT_SOURCE_AGENT - && chat_msg.mFromID.notNull() - && chat_msg.mFromID != gAgentID) - { - LLFirstUse::otherAvatarChatFirst(); - - // Add sender to the recent people list. - LLRecentPeople::instance().add(chat_msg.mFromID); - - } - - // Send event on to LLEventStream - sChatWatcher->post(chat); - - if( nearby_chat->isInVisibleChain() - || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT - && gSavedSettings.getBOOL("UseChatBubbles") ) - || mChannel.isDead() - || !mChannel.get()->getShowToasts() ) // to prevent toasts in Do Not Disturb mode - return;//no need in toast if chat is visible or if bubble chat is enabled - - // arrange a channel on a screen - if(!mChannel.get()->getVisible()) - { - initChannel(); - } - - /* - //comment all this due to EXT-4432 - ..may clean up after some time... - - //only messages from AGENTS - if(CHAT_SOURCE_OBJECT == chat_msg.mSourceType) - { - if(chat_msg.mChatType == CHAT_TYPE_DEBUG_MSG) - return;//ok for now we don't skip messeges from object, so skip only debug messages - } - */ - - LLNearbyChatScreenChannel* channel = dynamic_cast(mChannel.get()); - - if(channel) - { - // Handle IRC styled messages. - std::string toast_msg; - if (chat_msg.mChatStyle == CHAT_STYLE_IRC) - { - if (!chat_msg.mFromName.empty()) - { - toast_msg += chat_msg.mFromName; - } - toast_msg += chat_msg.mText.substr(3); - } - else - { - toast_msg = chat_msg.mText; - } - - // Add a nearby chat toast. - LLUUID id; - id.generate(); - chat["id"] = id; - std::string r_color_name = "White"; - F32 r_color_alpha = 1.0f; - LLViewerChat::getChatColor( chat_msg, r_color_name, r_color_alpha); - - chat["text_color"] = r_color_name; - chat["color_alpha"] = r_color_alpha; - chat["font_size"] = (S32)LLViewerChat::getChatFontSize() ; - chat["message"] = toast_msg; - channel->addChat(chat); - } -} - - -//----------------------------------------------------------------------------------------------- -// LLNearbyChatToast -//----------------------------------------------------------------------------------------------- - -// virtual -void LLNearbyChatToast::onClose(bool app_quitting) -{ - mNearbyChatScreenChannelp->onToastDestroyed(this, app_quitting); -} - -// EOF diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h deleted file mode 100644 index a5034ac1cb..0000000000 --- a/indra/newview/llnearbychathandler.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file llnearbychathandler.h - * @brief nearby chat notify - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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_LLNEARBYCHATHANDLER_H -#define LL_LLNEARBYCHATHANDLER_H - -#include "llnotificationhandler.h" - -class LLEventPump; - -//add LLNearbyChatHandler to LLNotificationsUI namespace -namespace LLNotificationsUI{ - -class LLNearbyChatHandler : public LLChatHandler -{ -public: - LLNearbyChatHandler(); - virtual ~LLNearbyChatHandler(); - - - virtual void processChat(const LLChat& chat_msg, const LLSD &args); - -protected: - virtual void initChannel(); - - static boost::scoped_ptr sChatWatcher; -}; - -} - -#endif /* LL_LLNEARBYCHATHANDLER_H */ diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 0899625242..4bded6ab30 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -36,7 +36,7 @@ #include "llinstantmessage.h" #include "llnotificationptr.h" -class LLIMFloater; +class LLFloaterIMSession; namespace LLNotificationsUI { diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index b4e8927879..7f1216ff40 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -34,9 +34,9 @@ #include "llurlaction.h" #include "llagent.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" #include "llimview.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llnotificationhandler.h" using namespace LLNotificationsUI; @@ -52,7 +52,7 @@ bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) LLUUID from_id = notification->getPayload()["from_id"]; LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - LLIMFloater* im_floater = LLFloaterReg::findTypedInstance("impanel", session_id); + LLFloaterIMSession* im_floater = LLFloaterReg::findTypedInstance("impanel", session_id); if (im_floater != NULL) { @@ -164,7 +164,7 @@ void LLHandlerUtil::logGroupNoticeToIMGroup( // static void LLHandlerUtil::logToNearbyChat(const LLNotificationPtr& notification, EChatSourceType type) { - LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); if (nearby_chat) { LLChat chat_msg(notification->getMessage()); @@ -244,7 +244,7 @@ void LLHandlerUtil::addNotifPanelToIM(const LLNotificationPtr& notification) // static void LLHandlerUtil::updateIMFLoaterMesages(const LLUUID& session_id) { - LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); if (im_floater != NULL && im_floater->getVisible()) { im_floater->updateMessages(); diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index 2862ad6962..56f13802e3 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -31,7 +31,7 @@ #include "llnotificationmanager.h" -#include "llnearbychathandler.h" +#include "llfloaterimnearbychathandler.h" #include "llnotifications.h" #include @@ -64,7 +64,7 @@ void LLNotificationManager::init() mChannels.push_back(new LLOutboxNotification()); mChannels.push_back(new LLIMHandler()); - mChatHandler = boost::shared_ptr(new LLNearbyChatHandler()); + mChatHandler = boost::shared_ptr(new LLFloaterIMNearbyChatHandler()); } //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h index c8afdf9e46..f37c6b833c 100644 --- a/indra/newview/llnotificationmanager.h +++ b/indra/newview/llnotificationmanager.h @@ -60,7 +60,7 @@ public: void onChat(const LLChat& msg, const LLSD &args); private: - boost::shared_ptr mChatHandler; + boost::shared_ptr mChatHandler; std::vector mChannels; }; diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index a293e6acb6..faa67b5ea4 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -28,8 +28,8 @@ #include "llviewerprecompiledheaders.h" // must be first include #include "llfloaterreg.h" -#include "llnearbychat.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llnotificationhandler.h" #include "llnotifications.h" #include "lltoastnotifypanel.h" @@ -85,7 +85,7 @@ bool LLTipHandler::processNotification(const LLNotificationPtr& notification) LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); // don't show toast if Nearby Chat is opened - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); if (nearby_chat->isChatVisible()) { return false; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 9a4d1166db..9f89b5f809 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -28,7 +28,7 @@ #include "llavatarnamecache.h" #include "llimview.h" -#include "llimfloatercontainer.h" +#include "llfloaterimcontainer.h" #include "llparticipantlist.h" #include "llspeakers.h" @@ -316,7 +316,7 @@ bool LLParticipantList::onSpeakerUpdateEvent(LLPointer eve if ( evt_data.has("id") ) { LLUUID participant_id = evt_data["id"]; - LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance(); if (im_box) { im_box->setTimeNow(mUUID,participant_id); @@ -345,7 +345,7 @@ bool LLParticipantList::onModeratorUpdateEvent(LLPointer e mModeratorList.erase(id); } } - // *TODO : do we have to fire an event so that LLIMConversation::refreshConversation() gets called + // *TODO : do we have to fire an event so that LLFloaterIMSessionTab::refreshConversation() gets called } } return true; diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index a4a0198305..3bcf36ffde 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -39,7 +39,7 @@ #include "lldockablefloater.h" #include "llsyswellwindow.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" #include "llscriptfloater.h" #include "llrootview.h" diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp index 6f98be1cb8..dc12192697 100644 --- a/indra/newview/llscriptfloater.cpp +++ b/indra/newview/llscriptfloater.cpp @@ -41,7 +41,7 @@ #include "lltoastscripttextbox.h" #include "lltrans.h" #include "llviewerwindow.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index c827b39d0e..8932d12e20 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -54,7 +54,7 @@ #include "llfloaterreg.h" #include "llfocusmgr.h" #include "llhttpsender.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" #include "lllocationhistory.h" #include "llimageworker.h" @@ -63,8 +63,8 @@ #include "llmemorystream.h" #include "llmessageconfig.h" #include "llmoveview.h" -#include "llimfloatercontainer.h" -#include "llnearbychat.h" +#include "llfloaterimcontainer.h" +#include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llteleporthistory.h" @@ -1384,7 +1384,7 @@ bool idle_startup() // create a container's instance for start a controlling conversation windows // by the voice's events - LLIMFloaterContainer::getInstance(); + LLFloaterIMContainer::getInstance(); // *Note: this is where gWorldMap used to be initialized. diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 4a49922656..65b4a3a44c 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -40,7 +40,7 @@ #include "lltrans.h" #include "llnotificationsutil.h" #include "llviewermessage.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" const S32 BOTTOM_PAD = VPAD * 3; const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index b99d04abae..c6b28b9e5e 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -71,7 +71,7 @@ #include "llfloatermediasettings.h" #include "llfloaterhud.h" #include "llfloaterimagepreview.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" #include "llfloaterinspect.h" #include "llfloaterinventory.h" #include "llfloaterjoystick.h" @@ -120,14 +120,14 @@ #include "llfloaterwhitelistentry.h" #include "llfloaterwindowsize.h" #include "llfloaterworldmap.h" -#include "llimfloatercontainer.h" +#include "llfloaterimcontainer.h" #include "llinspectavatar.h" #include "llinspectgroup.h" #include "llinspectobject.h" #include "llinspectremoteobject.h" #include "llinspecttoast.h" #include "llmoveview.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llpanelblockedlist.h" #include "llpanelclassified.h" #include "llpreviewanim.h" @@ -193,7 +193,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLNearbyChat::buildFloater); + LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -217,8 +217,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("hud", "floater_hud.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("im_container", "floater_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("im_container", "floater_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("im_well_window", "floater_sys_well.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("incoming_call", "floater_incoming_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index 71608b5280..3f35a5001d 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -41,7 +41,7 @@ #include "llviewermessage.h" // send_guid_sound_trigger #include "llviewernetwork.h" #include "llagent.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" // Globals LLViewerGestureList gGestureList; @@ -131,7 +131,7 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) { // Don't play nodding animation, since that might not blend // with the gesture animation. - (LLFloaterReg::getTypedInstance("nearby_chat"))-> + (LLFloaterReg::getTypedInstance("nearby_chat"))-> sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); } } diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index f8e988bc0c..4ecdc31e21 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -32,7 +32,7 @@ #include "llmath.h" #include "llagent.h" #include "llagentcamera.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llviewercontrol.h" #include "llfocusmgr.h" #include "llmorphview.h" @@ -535,7 +535,7 @@ void stop_moving( EKeystate s ) void start_chat( EKeystate s ) { // start chat - LLNearbyChat::startChat(NULL); + LLFloaterIMNearbyChat::startChat(NULL); } void start_gesture( EKeystate s ) @@ -544,15 +544,15 @@ void start_gesture( EKeystate s ) if (KEYSTATE_UP == s && ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) { - if ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) + if ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) { // No existing chat in chat editor, insert '/' - LLNearbyChat::startChat("/"); + LLFloaterIMNearbyChat::startChat("/"); } else { // Don't overwrite existing text in chat editor - LLNearbyChat::startChat(NULL); + LLFloaterIMNearbyChat::startChat(NULL); } } } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1ddfc51f27..47249fad70 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -68,7 +68,7 @@ #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "llinventorypanel.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llpanelgrouplandmoney.h" @@ -2295,7 +2295,7 @@ void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string m // Treat like a system message and put in chat history. chat.mText = av_name.getCompleteName() + ": " + message; - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); if (nearby_chat) { nearby_chat->addMessage(chat); @@ -2877,7 +2877,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // Note: lie to Nearby Chat, pretending that this is NOT an IM, because // IMs from obejcts don't open IM sessions. - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); if(!chat_from_system && nearby_chat) { chat.mOwnerID = from_id; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index ee838b19b7..afc3e3965c 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -188,7 +188,7 @@ #include "llviewerjoystick.h" #include "llviewernetwork.h" #include "llpostprocess.h" -#include "llnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llagentui.h" #include "llwearablelist.h" @@ -2496,7 +2496,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) return TRUE; } - LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); // Traverses up the hierarchy if( keyboard_focus ) @@ -2574,7 +2574,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) LLFloaterReg::toggleInstanceOrBringToFront(name); } - LLChatEntry* chat_editor = LLFloaterReg::findTypedInstance("nearby_chat")->getChatBox(); + LLChatEntry* chat_editor = LLFloaterReg::findTypedInstance("nearby_chat")->getChatBox(); if (chat_editor) { // passing NULL here, character will be added later when it is handled by character handler. -- cgit v1.3