diff options
-rw-r--r-- | indra/newview/llimfloater.cpp | 376 | ||||
-rw-r--r-- | indra/newview/llimfloater.h | 7 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_im_session.xml | 6 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 14 |
4 files changed, 256 insertions, 147 deletions
diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index cdd5ba6889..6f26f1daff 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -60,6 +60,10 @@ #include "llnotificationmanager.h" #include "llautoreplace.h" +/// Helper function to resolve resident names from given uuids +/// and form a string of names separated by "words_separator". +static void build_names_string(const uuid_vec_t& uuids, std::string& names_string); + floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal; LLIMFloater::LLIMFloater(const LLUUID& session_id) @@ -123,14 +127,13 @@ void LLIMFloater::refresh() // virtual void LLIMFloater::onClickCloseBtn() { - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( - mSessionID); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); if (session == NULL) { llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; return; -} + } bool is_call_with_chat = session->isGroupSessionType() || session->isAdHocSessionType() || session->isP2PSessionType(); @@ -186,49 +189,55 @@ void LLIMFloater::onVisibilityChange(const LLSD& new_visibility) void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) { LLIMFloater* self = (LLIMFloater*) userdata; - self->sendMsg(); + self->sendMsgFromInputEditor(); self->setTyping(false); } -void LLIMFloater::sendMsg() +void LLIMFloater::sendMsgFromInputEditor() { if (gAgent.isGodlike() || (mDialog != IM_NOTHING_SPECIAL) || !mOtherParticipantUUID.isNull()) { - if (mInputEditor) - { + 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); - utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); - - if (mSessionInitialized) - { - LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID, mDialog); - } - else + if(!text.empty()) { - //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); - mInputEditor->setText(LLStringUtil::null); + sendMsg(utf8_text); - updateMessages(); + 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() { mParticipantsListRefreshConnection.disconnect(); @@ -241,6 +250,7 @@ LLIMFloater::~LLIMFloater() LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); } + void LLIMFloater::initIMSession(const LLUUID& session_id) { // Change the floater key to bind it to a new session. @@ -250,7 +260,7 @@ void LLIMFloater::initIMSession(const LLUUID& session_id) mSession = LLIMModel::getInstance()->findIMSession(mSessionID); if (mSession) - { + { mIsP2PChat = mSession->isP2PSessionType(); mSessionInitialized = mSession->mSessionInitialized; @@ -348,19 +358,19 @@ BOOL LLIMFloater::postBuild() } void LLIMFloater::onAddButtonClicked() +{ + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloater::addSessionParticipants, this, _1), TRUE, TRUE); + if (!picker) { - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloater::addSessionParticipants, this, _1), TRUE, TRUE); - if (!picker) - { - return; + return; } - // Need to disable 'ok' button when selected users are already in conversation. - picker->setOkBtnEnableCb(boost::bind(&LLIMFloater::canAddSelectedToChat, this, _1)); - LLFloater* root_floater = gFloaterView->getParentFloater(this); - if (root_floater) + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&LLIMFloater::canAddSelectedToChat, this, _1)); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (root_floater) { - root_floater->addDependentFloater(picker); + root_floater->addDependentFloater(picker); } } @@ -396,7 +406,7 @@ bool LLIMFloater::canAddSelectedToChat(const uuid_vec_t& uuids) if (speaker_mgr) { speaker_mgr->getSpeakerList(&speaker_list, true); - } + } for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id) @@ -417,42 +427,76 @@ bool LLIMFloater::canAddSelectedToChat(const uuid_vec_t& uuids) } void LLIMFloater::addSessionParticipants(const uuid_vec_t& uuids) - { +{ if (mIsP2PChat) { - mStartConferenceInSameFloater = true; + LLSD payload; + LLSD args; - uuid_vec_t temp_ids; + 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); + } +} - // Add the initial participant of a P2P session - temp_ids.push_back(mOtherParticipantUUID); - temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); +void LLIMFloater::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) + { + return; + } - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + mStartConferenceInSameFloater = true; - // first check whether this is a voice session - bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - // then we can close the current session - onClose(false); + // first check whether this is a voice session + bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); - // 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 + 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::startConference(temp_ids, mSessionID); + LLAvatarActions::startAdhocCall(temp_ids, mSessionID); } -} else { - inviteToSession(uuids); + LLAvatarActions::startConference(temp_ids, mSessionID); } } +void LLIMFloater::sendParticipantsAddedNotification(const uuid_vec_t& uuids) +{ + std::string names_string; + build_names_string(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); @@ -547,13 +591,15 @@ void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) bool all_names_resolved = true; std::vector<LLSD> participants_uuids; + uuid_vec_t temp_uuids; // uuids vector for building the added participants' names string avatar_list->getValues(participants_uuids); // Check whether we have all participants names in LLAvatarNameCache for (std::vector<LLSD>::const_iterator it = participants_uuids.begin(); it != participants_uuids.end(); ++it) - { + { const LLUUID& id = it->asUUID(); + temp_uuids.push_back(id); LLAvatarName av_name; if (!LLAvatarNameCache::get(id, &av_name)) { @@ -565,32 +611,15 @@ void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) boost::bind(&LLIMFloater::onParticipantsListChanged, this, avatar_list)); break; } - } + } if (all_names_resolved) { - std::vector<LLAvatarName> avatar_names; - std::vector<LLSD>::const_iterator it = participants_uuids.begin(); - for (; it != participants_uuids.end(); ++it) - { - const LLUUID& id = it->asUUID(); - LLAvatarName av_name; - if (LLAvatarNameCache::get(id, &av_name)) - { - avatar_names.push_back(av_name); - } - } - - // We should check whether the vector is not empty to pass the assertion - // that avatar_names.size() > 0 in LLAvatarActions::buildResidentsString. - if (!avatar_names.empty()) - { - std::string ui_title; - LLAvatarActions::buildResidentsString(avatar_names, ui_title); - updateSessionName(ui_title, ui_title); - } + std::string ui_title; + build_names_string(temp_uuids, ui_title); + updateSessionName(ui_title, ui_title); + } } -} //static LLIMFloater* LLIMFloater::addToIMContainer(const LLUUID& session_id) @@ -840,18 +869,20 @@ 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 + //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); + LLSD::array_iterator iter; + for ( iter = mQueuedMsgsForInit.beginArray(); + iter != mQueuedMsgsForInit.endArray(); ++iter) + { + LLIMModel::sendMessage(iter->asString(), mSessionID, + mOtherParticipantUUID, mDialog); + } + + mQueuedMsgsForInit.clear(); } } -} void LLIMFloater::appendMessage(const LLChat& chat, const LLSD &args) { @@ -1019,19 +1050,18 @@ void LLIMFloater::setTyping(bool typing) { LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, mMeTyping); - mShouldSendTypingState = false; - - } + mShouldSendTypingState = false; } + } if (!mIsNearbyChat) + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - { - speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); + speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); } -} + } } void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) @@ -1050,29 +1080,66 @@ void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) void LLIMFloater::processAgentListUpdates(const LLSD& body) { + uuid_vec_t joined_uuids; + 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")) + LLSD::map_const_iterator update_it; + for(update_it = body["agent_updates"].beginMap(); + update_it != body["agent_updates"].endMap(); + ++update_it) { - LLSD agent_info = agent_data["info"]; + LLUUID agent_id(update_it->first); + LLSD agent_data = update_it->second; - if (agent_info.has("mutes")) + if (agent_data.isMap()) { - BOOL moderator_muted_text = agent_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); + // store the new participants in joined_uuids + if (agent_data.has("transition") && agent_data["transition"].asString() == "ENTER") + { + joined_uuids.push_back(agent_id); + } - if (moderator_muted_text) - LLNotificationsUtil::add("TextChatIsMutedByModerator"); + // 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) @@ -1110,19 +1177,19 @@ BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, if (cargo_type == DAD_PERSON) { if (dropPerson(static_cast<LLUUID*>(cargo_data), drop)) - { + { *accept = ACCEPT_YES_MULTI; - } + } else - { - *accept = ACCEPT_NO; - } + { + *accept = ACCEPT_NO; } + } else if (mDialog == IM_NOTHING_SPECIAL) - { + { LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, cargo_type, cargo_data, accept); - } + } return TRUE; } @@ -1132,18 +1199,18 @@ bool LLIMFloater::dropPerson(LLUUID* person_id, bool drop) bool res = person_id && person_id->notNull(); if(res) { - uuid_vec_t ids; + uuid_vec_t ids; ids.push_back(*person_id); res = canAddSelectedToChat(ids); if(res && drop) - { + { addSessionParticipants(ids); + } } -} return res; - } +} BOOL LLIMFloater::isInviteAllowed() const { @@ -1177,37 +1244,32 @@ BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) if (is_region_exist) { - S32 count = ids.size(); - - if( isInviteAllowed() && (count > 0) ) - { - llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl; + S32 count = ids.size(); - std::string url = region->getCapability("ChatSessionRequest"); + if( isInviteAllowed() && (count > 0) ) + { + llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl; - LLSD data; + std::string url = region->getCapability("ChatSessionRequest"); - data["params"] = LLSD::emptyArray(); - for (int i = 0; i < count; i++) + 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 { - 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 is_region_exist; @@ -1337,3 +1399,25 @@ boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floate { return LLIMFloater::sIMFloaterShowedSignal.connect(cb); } + +// static +void build_names_string(const uuid_vec_t& uuids, std::string& names_string) +{ + std::vector<LLAvatarName> avatar_names; + uuid_vec_t::const_iterator it = uuids.begin(); + for (; it != uuids.end(); ++it) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(*it, &av_name)) + { + avatar_names.push_back(av_name); + } + } + + // We should check whether the vector is not empty to pass the assertion + // that avatar_names.size() > 0 in LLAvatarActions::buildResidentsString. + if (!avatar_names.empty()) + { + LLAvatarActions::buildResidentsString(avatar_names, names_string); + } +} diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index d528c77e8d..24a8f17feb 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -90,7 +90,8 @@ public: void updateMessages(); void reloadMessages(); static void onSendMsg(LLUICtrl*, void*); - void sendMsg(); + void sendMsgFromInputEditor(); + void sendMsg(const std::string& msg); // callback for LLIMModel on new messages // route to specific floater if it is visible @@ -162,6 +163,8 @@ private: 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(); @@ -202,6 +205,8 @@ private: bool mStartConferenceInSameFloater; + uuid_vec_t mInvitedParticipants; + // connection to voice channel state change signal boost::signals2::connection mVoiceChannelStateChangeConnection; 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 37aa8bb787..d6d48130ab 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -31,6 +31,12 @@ <floater.string name="return_icon" value="Conv_toolbar_arrow_sw"/> + <floater.string + name="participant_added" + value="[NAME] was invited to the conversation."/> + <floater.string + name="multiple_participants_added" + value="[NAME] were invited to the conversation."/> <view follows="all" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 6df02a25af..64db7cd969 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4986,6 +4986,20 @@ Go to your [http://secondlife.com/account/ Dashboard] to see your account histor <notification icon="alertmodal.tga" + name="ConfirmAddingChatParticipants" + type="alertmodal"> + <unique/> +When you add a person to an existing conversation, a new conversation will be created. All participants will receive new conversation notifications. + <tag>confirm</tag> + <usetemplate + ignoretext="Confirm adding chat paticipants" + name="okcancelignore" + notext="Cancel" + yestext="Ok"/> + </notification> + + <notification + icon="alertmodal.tga" name="ConfirmQuit" type="alertmodal"> <unique/> |