diff options
Diffstat (limited to 'indra/newview/llimview.cpp')
-rw-r--r-- | indra/newview/llimview.cpp | 1874 |
1 files changed, 1313 insertions, 561 deletions
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index ab6f50aaf3..e0f155a6a9 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -36,61 +36,77 @@ #include "llfloaterreg.h" #include "llfontgl.h" +#include "llgl.h" #include "llrect.h" #include "llerror.h" #include "llbutton.h" #include "llhttpclient.h" -#include "llsdutil.h" +#include "llsdutil_math.h" #include "llstring.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llagent.h" +#include "llagentui.h" +#include "llappviewer.h" #include "llavatariconctrl.h" +#include "llbottomtray.h" #include "llcallingcard.h" #include "llchat.h" -#include "llresmgr.h" -#include "llfloaterchat.h" -#include "llfloaterchatterbox.h" -#include "llavataractions.h" -#include "llhttpnode.h" #include "llimfloater.h" -#include "llimpanel.h" -#include "llresizebar.h" -#include "lltabcontainer.h" -#include "llviewercontrol.h" -#include "llfloater.h" +#include "llgroupiconctrl.h" +#include "llmd5.h" #include "llmutelist.h" -#include "llresizehandle.h" -#include "llkeyboard.h" -#include "llui.h" -#include "llviewermenu.h" -#include "llcallingcard.h" -#include "lltoolbar.h" +#include "llrecentpeople.h" #include "llviewermessage.h" #include "llviewerwindow.h" -#include "llnotify.h" -#include "llviewerregion.h" -#include "lltrans.h" -#include "llrecentpeople.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llnearbychat.h" +#include "llspeakers.h" //for LLIMSpeakerMgr +#include "lltextbox.h" +#include "lltextutil.h" +#include "llviewercontrol.h" +#include "llviewerparcelmgr.h" -#include "llfirstuse.h" -#include "llagentui.h" +const static std::string IM_TIME("time"); +const static std::string IM_TEXT("message"); +const static std::string IM_FROM("from"); +const static std::string IM_FROM_ID("from_id"); + +const static std::string NO_SESSION("(IM Session Doesn't Exist)"); +const static std::string ADHOC_NAME_SUFFIX(" Conference"); + +const static std::string NEARBY_P2P_BY_OTHER("nearby_P2P_by_other"); +const static std::string NEARBY_P2P_BY_AGENT("nearby_P2P_by_agent"); + +/** Timeout of outgoing session initialization (in seconds) */ +const static U32 SESSION_INITIALIZATION_TIMEOUT = 30; + +std::string LLCallDialogManager::sPreviousSessionlName = ""; +LLIMModel::LLIMSession::SType LLCallDialogManager::sPreviousSessionType = LLIMModel::LLIMSession::P2P_SESSION; +std::string LLCallDialogManager::sCurrentSessionlName = ""; +LLIMModel::LLIMSession* LLCallDialogManager::sSession = NULL; +LLVoiceChannel::EState LLCallDialogManager::sOldState = LLVoiceChannel::STATE_READY; +const LLUUID LLOutgoingCallDialog::OCD_KEY = LLUUID("7CF78E11-0CFE-498D-ADB9-1417BF03DDB4"); // // Globals // LLIMMgr* gIMMgr = NULL; -// -// Statics -// -// *FIXME: make these all either UIStrings or Strings - -const static std::string IM_SEPARATOR(": "); - -std::map<LLUUID, LLIMModel::LLIMSession*> LLIMModel::sSessionsMap; +BOOL LLSessionTimeoutTimer::tick() +{ + if (mSessionId.isNull()) return TRUE; + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); + if (session && !session->mSessionInitialized) + { + gIMMgr->showSessionStartError("session_initialization_timed_out_error", mSessionId); + } + return TRUE; +} void toast_callback(const LLSD& msg){ // do not show toast in busy mode or it goes from agent @@ -100,11 +116,25 @@ void toast_callback(const LLSD& msg){ } // check whether incoming IM belongs to an active session or not - if (LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"]) + if (LLIMModel::getInstance()->getActiveSessionID().notNull() + && LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"]) { return; } - + + // Skip toasting for system messages + if (msg["from_id"].asUUID() == LLUUID::null) + { + return; + } + + // Skip toasting if we have open window of IM with this session id + LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]); + if (open_im_floater && open_im_floater->getVisible()) + { + return; + } + LLSD args; args["MESSAGE"] = msg["message"]; args["TIME"] = msg["time"]; @@ -112,7 +142,7 @@ void toast_callback(const LLSD& msg){ args["FROM_ID"] = msg["from_id"]; args["SESSION_ID"] = msg["session_id"]; - LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); + LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); } void LLIMModel::setActiveSessionID(const LLUUID& session_id) @@ -130,34 +160,67 @@ void LLIMModel::setActiveSessionID(const LLUUID& session_id) LLIMModel::LLIMModel() { addNewMsgCallback(LLIMFloater::newIMCallback); - addNoUnreadMsgsCallback(LLIMFloater::newIMCallback); addNewMsgCallback(toast_callback); } - -LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const std::vector<LLUUID>& ids) +LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) : mSessionID(session_id), mName(name), mType(type), + mParticipantUnreadMessageCount(0), mNumUnread(0), mOtherParticipantID(other_participant_id), mInitialTargetIDs(ids), mVoiceChannel(NULL), mSpeakers(NULL), - mSessionInitialized(false) + mSessionInitialized(false), + mCallBackEnabled(true), + mTextIMPossible(true), + mOtherParticipantIsAvatar(true), + mStartCallOnInitialize(false), + mStartedAsIMCall(voice) { + // set P2P type by default + mSessionType = P2P_SESSION; + if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type) { mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); + mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID); + + // check if it was AVALINE call + if (!mOtherParticipantIsAvatar) + { + mSessionType = AVALINE_SESSION; + } } else { mVoiceChannel = new LLVoiceChannelGroup(session_id, name); + + // determine whether it is group or conference session + if (gAgent.isInGroup(mSessionID)) + { + mSessionType = GROUP_SESSION; + } + else + { + mSessionType = ADHOC_SESSION; + } + } + + if(mVoiceChannel) + { + mVoiceChannelStateChangeConnection = mVoiceChannel->setStateChangedCallback(boost::bind(&LLIMSession::onVoiceChannelStateChanged, this, _1, _2, _3)); } + mSpeakers = new LLIMSpeakerMgr(mVoiceChannel); // All participants will be added to the list of people we've recently interacted with. - mSpeakers->addListener(&LLRecentPeople::instance(), "add"); + + // we need to add only _active_ speakers...so comment this. + // may delete this later on cleanup + //mSpeakers->addListener(&LLRecentPeople::instance(), "add"); //we need to wait for session initialization for outgoing ad-hoc and group chat session //correct session id for initiated ad-hoc chat will be received from the server @@ -168,6 +231,111 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& //so we're already initialized mSessionInitialized = true; } + else + { + //tick returns TRUE - timer will be deleted after the tick + new LLSessionTimeoutTimer(mSessionID, SESSION_INITIALIZATION_TIMEOUT); + } + + if (IM_NOTHING_SPECIAL == type) + { + mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID); + mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID); + } + + buildHistoryFileName(); + + if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) + { + std::list<LLSD> chat_history; + + //involves parsing of a chat history + LLLogChat::loadAllHistory(mHistoryFileName, chat_history); + addMessagesFromHistory(chat_history); + } +} + +void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction) +{ + std::string you = LLTrans::getString("You"); + std::string started_call = LLTrans::getString("started_call"); + std::string joined_call = LLTrans::getString("joined_call"); + std::string other_avatar_name = ""; + + std::string message; + + switch(mSessionType) + { + case AVALINE_SESSION: + // no text notifications + break; + case P2P_SESSION: + gCacheName->getFullName(mOtherParticipantID, other_avatar_name); + + if(direction == LLVoiceChannel::INCOMING_CALL) + { + switch(new_state) + { + case LLVoiceChannel::STATE_CALL_STARTED : + message = other_avatar_name + " " + started_call; + LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message); + + break; + case LLVoiceChannel::STATE_CONNECTED : + message = you + " " + joined_call; + LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message); + default: + break; + } + } + else // outgoing call + { + switch(new_state) + { + case LLVoiceChannel::STATE_CALL_STARTED : + message = you + " " + started_call; + LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message); + break; + case LLVoiceChannel::STATE_CONNECTED : + message = other_avatar_name + " " + joined_call; + LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message); + default: + break; + } + } + break; + + case GROUP_SESSION: + case ADHOC_SESSION: + if(direction == LLVoiceChannel::INCOMING_CALL) + { + switch(new_state) + { + case LLVoiceChannel::STATE_CONNECTED : + message = you + " " + joined_call; + LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message); + default: + break; + } + } + else // outgoing call + { + switch(new_state) + { + case LLVoiceChannel::STATE_CALL_STARTED : + message = you + " " + started_call; + LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message); + break; + default: + break; + } + } + } + // Update speakers list when connected + if (LLVoiceChannel::STATE_CONNECTED == new_state) + { + mSpeakers->update(true); + } } LLIMModel::LLIMSession::~LLIMSession() @@ -191,9 +359,11 @@ LLIMModel::LLIMSession::~LLIMSession() } } + mVoiceChannelStateChangeConnection.disconnect(); + // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point). mVoiceChannel->deactivate(); - + delete mVoiceChannel; mVoiceChannel = NULL; } @@ -209,14 +379,165 @@ void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_ } } +void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time, const bool is_history) +{ + LLSD message; + message["from"] = from; + message["from_id"] = from_id; + message["message"] = utf8_text; + message["time"] = time; + message["index"] = (LLSD::Integer)mMsgs.size(); + message["is_history"] = is_history; + + mMsgs.push_front(message); + + if (mSpeakers && from_id.notNull()) + { + mSpeakers->speakerChatted(from_id); + mSpeakers->setSpeakerTyping(from_id, FALSE); + } +} + +void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& history) +{ + std::list<LLSD>::const_iterator it = history.begin(); + while (it != history.end()) + { + const LLSD& msg = *it; + + std::string from = msg[IM_FROM]; + LLUUID from_id = LLUUID::null; + if (msg[IM_FROM_ID].isUndefined()) + { + gCacheName->getUUID(from, from_id); + } + + + std::string timestamp = msg[IM_TIME]; + std::string text = msg[IM_TEXT]; + + addMessage(from, from_id, text, timestamp, true); + + it++; + } +} + +void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata) +{ + if (!userdata) return; + + LLIMSession* self = (LLIMSession*) userdata; + + if (type == LLLogChat::LOG_LINE) + { + self->addMessage("", LLSD(), msg["message"].asString(), "", true); + } + else if (type == LLLogChat::LOG_LLSD) + { + self->addMessage(msg["from"].asString(), msg["from_id"].asUUID(), msg["message"].asString(), msg["time"].asString(), true); + } +} + LLIMModel::LLIMSession* LLIMModel::findIMSession(const LLUUID& session_id) const { - return get_if_there(LLIMModel::instance().sSessionsMap, session_id, + return get_if_there(mId2SessionMap, session_id, (LLIMModel::LLIMSession*) NULL); } -//*TODO change name to represent session initialization aspect (IB) -void LLIMModel::updateSessionID(const LLUUID& old_session_id, const LLUUID& new_session_id) +//*TODO consider switching to using std::set instead of std::list for holding LLUUIDs across the whole code +LLIMModel::LLIMSession* LLIMModel::findAdHocIMSession(const uuid_vec_t& ids) +{ + S32 num = ids.size(); + if (!num) return NULL; + + if (mId2SessionMap.empty()) return NULL; + + std::map<LLUUID, LLIMSession*>::const_iterator it = mId2SessionMap.begin(); + for (; it != mId2SessionMap.end(); ++it) + { + LLIMSession* session = (*it).second; + + if (!session->isAdHoc()) continue; + if (session->mInitialTargetIDs.size() != num) continue; + + std::list<LLUUID> tmp_list(session->mInitialTargetIDs.begin(), session->mInitialTargetIDs.end()); + + uuid_vec_t::const_iterator iter = ids.begin(); + while (iter != ids.end()) + { + tmp_list.remove(*iter); + ++iter; + + if (tmp_list.empty()) + { + break; + } + } + + if (tmp_list.empty() && iter == ids.end()) + { + return session; + } + } + + return NULL; +} + +bool LLIMModel::LLIMSession::isAdHoc() +{ + return IM_SESSION_CONFERENCE_START == mType || (IM_SESSION_INVITE == mType && !gAgent.isInGroup(mSessionID)); +} + +bool LLIMModel::LLIMSession::isP2P() +{ + return IM_NOTHING_SPECIAL == mType; +} + +bool LLIMModel::LLIMSession::isOtherParticipantAvaline() +{ + return !mOtherParticipantIsAvatar; +} + +void LLIMModel::LLIMSession::buildHistoryFileName() +{ + mHistoryFileName = mName; + + //ad-hoc requires sophisticated chat history saving schemes + if (isAdHoc()) + { + //in case of outgoing ad-hoc sessions + if (mInitialTargetIDs.size()) + { + std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); + mHistoryFileName = mName + " hash" + generateHash(sorted_uuids); + return; + } + + //in case of incoming ad-hoc sessions + mHistoryFileName = mName + " " + LLLogChat::timestamp(true) + " " + mSessionID.asString().substr(0, 4); + } +} + +//static +std::string LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_uuids) +{ + LLMD5 md5_uuid; + + std::set<LLUUID>::const_iterator it = sorted_uuids.begin(); + while (it != sorted_uuids.end()) + { + md5_uuid.update((unsigned char*)(*it).mData, 16); + it++; + } + md5_uuid.finalize(); + + LLUUID participants_md5_hash; + md5_uuid.raw_digest((unsigned char*) participants_md5_hash.mData); + return participants_md5_hash.asString(); +} + + +void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id) { LLIMSession* session = findIMSession(old_session_id); if (session) @@ -225,8 +546,8 @@ void LLIMModel::updateSessionID(const LLUUID& old_session_id, const LLUUID& new_ if (old_session_id != new_session_id) { - sSessionsMap.erase(old_session_id); - sSessionsMap[new_session_id] = session; + mId2SessionMap.erase(old_session_id); + mId2SessionMap[new_session_id] = session; gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); } @@ -236,13 +557,12 @@ void LLIMModel::updateSessionID(const LLUUID& old_session_id, const LLUUID& new_ { im_floater->sessionInitReplyReceived(new_session_id); } - } - //*TODO remove this "floater" stuff when Communicate Floater is gone - LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(old_session_id); - if (floater) - { - floater->sessionInitReplyReceived(new_session_id); + // auto-start the call on session initialization? + if (session->mStartCallOnInitialize) + { + gIMMgr->startCall(new_session_id); + } } } @@ -267,21 +587,28 @@ void LLIMModel::testMessages() bot2_id.generate(from); LLUUID bot2_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot2_id); newSession(bot2_session_id, from, IM_NOTHING_SPECIAL, bot2_id); - addMessage(bot2_session_id, from, bot2_id, "Test Message: Can I haz bear? "); + addMessage(bot2_session_id, from, bot2_id, "Test Message: Hello there, I have a question. Can I bother you for a second? "); addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ."); } - -bool LLIMModel::newSession(LLUUID session_id, std::string name, EInstantMessage type, LLUUID other_participant_id, const std::vector<LLUUID>& ids) +//session name should not be empty +bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, + const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) { - if (is_in_map(sSessionsMap, session_id)) + if (name.empty()) + { + llwarns << "Attempt to create a new session with empty name; id = " << session_id << llendl; + return false; + } + + if (findIMSession(session_id)) { llwarns << "IM Session " << session_id << " already exists" << llendl; return false; } - LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids); - sSessionsMap[session_id] = session; + LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice); + mId2SessionMap[session_id] = session; LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, name, other_participant_id); @@ -289,52 +616,69 @@ bool LLIMModel::newSession(LLUUID session_id, std::string name, EInstantMessage } -bool LLIMModel::clearSession(LLUUID session_id) +bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice) { - if (sSessionsMap.find(session_id) == sSessionsMap.end()) return false; - delete (sSessionsMap[session_id]); - sSessionsMap.erase(session_id); - return true; + uuid_vec_t no_ids; + return newSession(session_id, name, type, other_participant_id, no_ids, voice); } -//*TODO remake it, instead of returing the list pass it as as parameter (IB) -std::list<LLSD> LLIMModel::getMessages(LLUUID session_id, int start_index) +bool LLIMModel::clearSession(const LLUUID& session_id) { - std::list<LLSD> return_list; + if (mId2SessionMap.find(session_id) == mId2SessionMap.end()) return false; + delete (mId2SessionMap[session_id]); + mId2SessionMap.erase(session_id); + return true; +} +void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) +{ LLIMSession* session = findIMSession(session_id); - if (!session) + if (!session) { llwarns << "session " << session_id << "does not exist " << llendl; - return return_list; + return; } int i = session->mMsgs.size() - start_index; - for (std::list<LLSD>::iterator iter = session->mMsgs.begin(); + for (std::list<LLSD>::iterator iter = session->mMsgs.begin(); iter != session->mMsgs.end() && i > 0; iter++) { LLSD msg; msg = *iter; - return_list.push_back(*iter); + messages.push_back(*iter); i--; } +} + +void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id) +{ + LLIMSession* session = findIMSession(session_id); + if (!session) + { + llwarns << "session " << session_id << "does not exist " << llendl; + return; + } session->mNumUnread = 0; + session->mParticipantUnreadMessageCount = 0; LLSD arg; arg["session_id"] = session_id; arg["num_unread"] = 0; + arg["participant_unread"] = session->mParticipantUnreadMessageCount; mNoUnreadMsgsSignal(arg); +} - // TODO: in the future is there a more efficient way to return these - //of course there is - return as parameter (IB) - return return_list; +void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) +{ + getMessagesSilently(session_id, messages, start_index); + sendNoUnreadMessages(session_id); } -bool LLIMModel::addToHistory(LLUUID session_id, std::string from, LLUUID from_id, std::string utf8_text) { +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); @@ -344,64 +688,54 @@ bool LLIMModel::addToHistory(LLUUID session_id, std::string from, LLUUID from_id return false; } - LLSD message; - message["from"] = from; - message["from_id"] = from_id; - message["message"] = utf8_text; - message["time"] = LLLogChat::timestamp(false); //might want to add date separately - message["index"] = (LLSD::Integer)session->mMsgs.size(); - - session->mMsgs.push_front(message); + session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false)); //might want to add date separately return true; - } -//*TODO rewrite chat history persistence using LLSD serialization (IB) -bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const std::string& utf8_text) +bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { - S32 im_log_option = gSavedPerAccountSettings.getS32("IMLogOptions"); - if (im_log_option != LOG_CHAT) + if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")) { - std::string histstr; - if (gSavedPerAccountSettings.getBOOL("LogTimestamp")) - histstr = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")) + from + IM_SEPARATOR + utf8_text; - else - histstr = from + IM_SEPARATOR + utf8_text; - - if(im_log_option == LOG_BOTH_TOGETHER) - { - LLLogChat::saveHistory(std::string("chat"), histstr); - return true; - } - else - { - LLLogChat::saveHistory(LLIMModel::getInstance()->getName(session_id), histstr); - return true; - } + LLLogChat::saveHistory(file_name, from, from_id, utf8_text); + return true; } - return false; -} - -//*TODO add const qualifier and pass by references (IB) -bool LLIMModel::addMessage(LLUUID session_id, std::string from, LLUUID from_id, std::string utf8_text, bool log2file /* = true */) { - LLIMSession* session = findIMSession(session_id); - - if (!session) + else { - llwarns << "session " << session_id << "does not exist " << llendl; return false; } +} - addToHistory(session_id, from, from_id, utf8_text); - if (log2file) logToFile(session_id, from, utf8_text); +bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) +{ + return logToFile(LLIMModel::getInstance()->getHistoryFileName(session_id), from, from_id, utf8_text); +} - session->mNumUnread++; +bool LLIMModel::proccessOnlineOfflineNotification( + const LLUUID& session_id, + const std::string& utf8_text) +{ + // Add system message to history + return addMessage(session_id, SYSTEM_FROM, LLUUID::null, utf8_text); +} + +bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, + const std::string& utf8_text, bool log2file /* = true */) { + + LLIMSession* session = addMessageSilently(session_id, from, from_id, utf8_text, log2file); + if (!session) return false; + + //good place to add some1 to recent list + //other places may be called from message history. + if( !from_id.isNull() && + ( session->isP2PSessionType() || session->isAdHocSessionType() ) ) + LLRecentPeople::instance().add(from_id); // notify listeners LLSD arg; arg["session_id"] = session_id; arg["num_unread"] = session->mNumUnread; + arg["participant_unread"] = session->mParticipantUnreadMessageCount; arg["message"] = utf8_text; arg["from"] = from; arg["from_id"] = from_id; @@ -411,6 +745,40 @@ bool LLIMModel::addMessage(LLUUID session_id, std::string from, LLUUID from_id, return true; } +LLIMModel::LLIMSession* LLIMModel::addMessageSilently(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, + const std::string& utf8_text, bool log2file /* = true */) +{ + LLIMSession* session = findIMSession(session_id); + + if (!session) + { + llwarns << "session " << session_id << "does not exist " << llendl; + return NULL; + } + + // replace interactive system message marker with correct from string value + std::string from_name = from; + if (INTERACTIVE_SYSTEM_FROM == from) + { + from_name = SYSTEM_FROM; + } + + addToHistory(session_id, from_name, from_id, utf8_text); + if (log2file) logToFile(session_id, from_name, from_id, utf8_text); + + session->mNumUnread++; + + //update count of unread messages from real participant + if (!(from_id.isNull() || from_id == gAgentID || SYSTEM_FROM == from) + // we should increment counter for interactive system messages() + || INTERACTIVE_SYSTEM_FROM == from) + { + ++(session->mParticipantUnreadMessageCount); + } + + return session; +} + const std::string& LLIMModel::getName(const LLUUID& session_id) const { @@ -419,7 +787,7 @@ const std::string& LLIMModel::getName(const LLUUID& session_id) const if (!session) { llwarns << "session " << session_id << "does not exist " << llendl; - return LLStringUtil::null; + return NO_SESSION; } return session->mName; @@ -478,13 +846,25 @@ LLIMSpeakerMgr* LLIMModel::getSpeakerManager( 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 NULL; } return session->mSpeakers; } +const std::string& LLIMModel::getHistoryFileName(const LLUUID& session_id) const +{ + LLIMSession* session = findIMSession(session_id); + if (!session) + { + llwarns << "session " << session_id << " does not exist " << llendl; + return LLStringUtil::null; + } + + return session->mHistoryFileName; +} + // TODO get rid of other participant ID void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing) @@ -506,7 +886,7 @@ void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, gAgent.sendReliableMessage(); } -void LLIMModel::sendLeaveSession(LLUUID session_id, LLUUID other_participant_id) +void LLIMModel::sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id) { if(session_id.notNull()) { @@ -527,8 +907,7 @@ void LLIMModel::sendLeaveSession(LLUUID session_id, LLUUID other_participant_id) } } - -//*TODO update list of messages in a LLIMSession (IB) +//*TODO this method is better be moved to the LLIMMgr void LLIMModel::sendMessage(const std::string& utf8_text, const LLUUID& im_session_id, const LLUUID& other_participant_id, @@ -608,24 +987,9 @@ void LLIMModel::sendMessage(const std::string& utf8_text, //local echo for the legacy communicate panel std::string history_echo; - std::string utf8_copy = utf8_text; LLAgentUI::buildFullname(history_echo); - // Look for IRC-style emotes here. - - std::string prefix = utf8_copy.substr(0, 4); - if (prefix == "/me " || prefix == "/me'") - { - utf8_copy.replace(0,3,""); - } - else - { - history_echo += ": "; - } - history_echo += utf8_copy; - - LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(im_session_id); - if (floater) floater->addHistoryLine(history_echo, LLUIColorTable::instance().getColor("IMChatColor"), true, gAgent.getID()); + history_echo += ": " + utf8_text; LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id); if (speaker_mgr) @@ -636,7 +1000,42 @@ void LLIMModel::sendMessage(const std::string& utf8_text, } // Add the recipient to the recent people list. - LLRecentPeople::instance().add(other_participant_id); + bool is_not_group_id = LLGroupMgr::getInstance()->getGroupData(other_participant_id) == NULL; + + if (is_not_group_id) + { + +#if 0 + //use this code to add only online members + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id); + LLSpeakerMgr::speaker_list_t speaker_list; + speaker_mgr->getSpeakerList(&speaker_list, true); + for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) + { + const LLPointer<LLSpeaker>& speakerp = *it; + + LLRecentPeople::instance().add(speakerp->mID); + } +#else + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(im_session_id); + if( session == 0)//??? shouldn't really happen + { + LLRecentPeople::instance().add(other_participant_id); + } + else + { + for(uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); + it!=session->mInitialTargetIDs.end();++it) + { + const LLUUID id = *it; + + LLRecentPeople::instance().add(id); + } + } +#endif + } + + } void session_starter_helper( @@ -757,7 +1156,7 @@ private: bool LLIMModel::sendStartSession( const LLUUID& temp_session_id, const LLUUID& other_participant_id, - const std::vector<LLUUID>& ids, + const uuid_vec_t& ids, EInstantMessage dialog) { if ( dialog == IM_SESSION_GROUP_START ) @@ -819,8 +1218,6 @@ bool LLIMModel::sendStartSession( return false; } - - // // Helper Functions // @@ -867,15 +1264,15 @@ public: if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType) { - gIMMgr->startCall(mSessionID); + gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL); } if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE) && LLIMModel::getInstance()->findIMSession(mSessionID)) { - // always open IM window when connecting to voice - LLIMFloater::show(mSessionID); + // TODO remove in 2010, for voice calls we do not open an IM window + //LLIMFloater::show(mSessionID); } gIMMgr->clearPendingAgentListUpdates(mSessionID); @@ -890,20 +1287,11 @@ public: { gIMMgr->clearPendingAgentListUpdates(mSessionID); gIMMgr->clearPendingInvitation(mSessionID); - - LLFloaterIMPanel* floaterp = - gIMMgr->findFloaterBySession(mSessionID); - - if ( floaterp ) + if ( 404 == statusNum ) { - if ( 404 == statusNum ) - { - std::string error_string; - error_string = "does not exist"; - - floaterp->showSessionStartError( - error_string); - } + std::string error_string; + error_string = "session_does_not_exist_error"; + gIMMgr->showSessionStartError(error_string, mSessionID); } } } @@ -955,48 +1343,611 @@ LLUUID LLIMMgr::computeSessionID( return session_id; } +void +LLIMMgr::showSessionStartError( + const std::string& error_string, + const LLUUID session_id) +{ + if (!hasSession(session_id)) return; + + LLSD args; + args["REASON"] = LLTrans::getString(error_string); + args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id); + + LLSD payload; + payload["session_id"] = session_id; + + LLNotificationsUtil::add( + "ChatterBoxSessionStartError", + args, + payload, + LLIMMgr::onConfirmForceCloseError); +} + +void +LLIMMgr::showSessionEventError( + const std::string& event_string, + const std::string& error_string, + const LLUUID session_id) +{ + LLSD args; + LLStringUtil::format_map_t event_args; + + event_args["RECIPIENT"] = LLIMModel::getInstance()->getName(session_id); + + args["REASON"] = + LLTrans::getString(error_string); + args["EVENT"] = + LLTrans::getString(event_string, event_args); + + LLNotificationsUtil::add( + "ChatterBoxSessionEventError", + args); +} + +void +LLIMMgr::showSessionForceClose( + const std::string& reason_string, + const LLUUID session_id) +{ + if (!hasSession(session_id)) return; + + LLSD args; + + args["NAME"] = LLIMModel::getInstance()->getName(session_id); + args["REASON"] = LLTrans::getString(reason_string); + + LLSD payload; + payload["session_id"] = session_id; + + LLNotificationsUtil::add( + "ForceCloseChatterBoxSession", + args, + payload, + LLIMMgr::onConfirmForceCloseError); +} + +//static +bool +LLIMMgr::onConfirmForceCloseError( + const LLSD& notification, + const LLSD& response) +{ + //only 1 option really + LLUUID session_id = notification["payload"]["session_id"]; + + LLFloater* floater = LLIMFloater::findInstance(session_id); + if ( floater ) + { + floater->closeFloater(FALSE); + } + return false; +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLCallDialogManager +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +LLCallDialogManager::LLCallDialogManager() +{ +} + +LLCallDialogManager::~LLCallDialogManager() +{ +} + +void LLCallDialogManager::initClass() +{ + LLVoiceChannel::setCurrentVoiceChannelChangedCallback(LLCallDialogManager::onVoiceChannelChanged); +} + +void LLCallDialogManager::onVoiceChannelChanged(const LLUUID &session_id) +{ + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); + if(!session) + { + sPreviousSessionlName = sCurrentSessionlName; + sCurrentSessionlName = ""; // Empty string results in "Nearby Voice Chat" after substitution + return; + } + + if (sSession) + { + // store previous session type to process Avaline calls in dialogs + sPreviousSessionType = sSession->mSessionType; + } + + sSession = session; + sSession->mVoiceChannel->setStateChangedCallback(boost::bind(LLCallDialogManager::onVoiceChannelStateChanged, _1, _2, _3, _4)); + if(sCurrentSessionlName != session->mName) + { + sPreviousSessionlName = sCurrentSessionlName; + sCurrentSessionlName = session->mName; + } + + if (LLVoiceChannel::getCurrentVoiceChannel()->getState() == LLVoiceChannel::STATE_CALL_STARTED && + LLVoiceChannel::getCurrentVoiceChannel()->getCallDirection() == LLVoiceChannel::OUTGOING_CALL) + { + + //*TODO get rid of duplicated code + LLSD mCallDialogPayload; + mCallDialogPayload["session_id"] = sSession->mSessionID; + mCallDialogPayload["session_name"] = sSession->mName; + mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID; + mCallDialogPayload["old_channel_name"] = sPreviousSessionlName; + mCallDialogPayload["old_session_type"] = sPreviousSessionType; + mCallDialogPayload["state"] = LLVoiceChannel::STATE_CALL_STARTED; + mCallDialogPayload["disconnected_channel_name"] = sSession->mName; + mCallDialogPayload["session_type"] = sSession->mSessionType; + + LLOutgoingCallDialog* ocd = LLFloaterReg::getTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY); + if(ocd) + { + ocd->show(mCallDialogPayload); + } + } + +} + +void LLCallDialogManager::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction, bool ended_by_agent) +{ + LLSD mCallDialogPayload; + LLOutgoingCallDialog* ocd = NULL; + + if(sOldState == new_state) + { + return; + } + + sOldState = new_state; + + mCallDialogPayload["session_id"] = sSession->mSessionID; + mCallDialogPayload["session_name"] = sSession->mName; + mCallDialogPayload["other_user_id"] = sSession->mOtherParticipantID; + mCallDialogPayload["old_channel_name"] = sPreviousSessionlName; + mCallDialogPayload["old_session_type"] = sPreviousSessionType; + mCallDialogPayload["state"] = new_state; + mCallDialogPayload["disconnected_channel_name"] = sSession->mName; + mCallDialogPayload["session_type"] = sSession->mSessionType; + mCallDialogPayload["ended_by_agent"] = ended_by_agent; + + switch(new_state) + { + case LLVoiceChannel::STATE_CALL_STARTED : + // do not show "Calling to..." if it is incoming call + if(direction == LLVoiceChannel::INCOMING_CALL) + { + return; + } + break; + + case LLVoiceChannel::STATE_HUNG_UP: + // this state is coming before session is changed, so, put it into payload map + mCallDialogPayload["old_session_type"] = sSession->mSessionType; + break; + + case LLVoiceChannel::STATE_CONNECTED : + ocd = LLFloaterReg::findTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY); + if (ocd) + { + ocd->closeFloater(); + } + return; + + default: + break; + } + + ocd = LLFloaterReg::getTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY); + if(ocd) + { + ocd->show(mCallDialogPayload); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLCallDialog +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LLCallDialog::LLCallDialog(const LLSD& payload) + : LLDockableFloater(NULL, false, payload), + + mPayload(payload), + mLifetime(DEFAULT_LIFETIME) +{ + setAutoFocus(FALSE); + // force docked state since this floater doesn't save it between recreations + setDocked(true); +} + +LLCallDialog::~LLCallDialog() +{ + LLUI::removePopup(this); +} + +void LLCallDialog::getAllowedRect(LLRect& rect) +{ + rect = gViewerWindow->getWorldViewRectScaled(); +} + +BOOL LLCallDialog::postBuild() +{ + if (!LLDockableFloater::postBuild()) + return FALSE; + + // dock the dialog to the Speak Button, where other sys messages appear + LLView *anchor_panel = LLBottomTray::getInstance()->getChild<LLView>("speak_panel"); + + setDockControl(new LLDockControl( + anchor_panel, this, + getDockTongue(), LLDockControl::TOP, + boost::bind(&LLCallDialog::getAllowedRect, this, _1))); + + return TRUE; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLOutgoingCallDialog +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LLOutgoingCallDialog::LLOutgoingCallDialog(const LLSD& payload) : +LLCallDialog(payload) +{ + LLOutgoingCallDialog* instance = LLFloaterReg::findTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY); + if(instance && instance->getVisible()) + { + instance->onCancel(instance); + } +} + +void LLCallDialog::draw() +{ + if (lifetimeHasExpired()) + { + onLifetimeExpired(); + } + + if (getDockControl() != NULL) + { + LLDockableFloater::draw(); + } +} + +// virtual +void LLCallDialog::onOpen(const LLSD& key) +{ + LLDockableFloater::onOpen(key); + + // it should be over the all floaters. EXT-5116 + LLUI::addPopup(this); +} + +void LLCallDialog::setIcon(const LLSD& session_id, const LLSD& participant_id) +{ + // *NOTE: 12/28/2009: check avaline calls: LLVoiceClient::isParticipantAvatar returns false for them + bool participant_is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); + + bool is_group = participant_is_avatar && gAgent.isInGroup(session_id); + + LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon"); + + avatar_icon->setVisible(!is_group); + group_icon->setVisible(is_group); + + if (is_group) + { + group_icon->setValue(session_id); + } + else if (participant_is_avatar) + { + avatar_icon->setValue(participant_id); + } + else + { + avatar_icon->setValue("Avaline_Icon"); + avatar_icon->setToolTip(std::string("")); + } +} + +bool LLCallDialog::lifetimeHasExpired() +{ + if (mLifetimeTimer.getStarted()) + { + F32 elapsed_time = mLifetimeTimer.getElapsedTimeF32(); + if (elapsed_time > mLifetime) + { + return true; + } + } + return false; +} + +void LLCallDialog::onLifetimeExpired() +{ + mLifetimeTimer.stop(); + closeFloater(); +} + +void LLOutgoingCallDialog::show(const LLSD& key) +{ + mPayload = key; + + //will be false only if voice in parcel is disabled and channel we leave is nearby(checked further) + bool show_oldchannel = LLViewerParcelMgr::getInstance()->allowAgentVoice(); + + // hide all text at first + hideAllText(); + + // init notification's lifetime + std::istringstream ss( getString("lifetime") ); + if (!(ss >> mLifetime)) + { + mLifetime = DEFAULT_LIFETIME; + } + + // customize text strings + // tell the user which voice channel they are leaving + if (!mPayload["old_channel_name"].asString().empty()) + { + bool was_avaline_call = LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["old_session_type"].asInteger(); + + std::string old_caller_name = mPayload["old_channel_name"].asString(); + if (was_avaline_call) + { + old_caller_name = LLTextUtil::formatPhoneNumber(old_caller_name); + } + + childSetTextArg("leaving", "[CURRENT_CHAT]", old_caller_name); + show_oldchannel = true; + } + else + { + childSetTextArg("leaving", "[CURRENT_CHAT]", getString("localchat")); + } + + if (!mPayload["disconnected_channel_name"].asString().empty()) + { + std::string channel_name = mPayload["disconnected_channel_name"].asString(); + if (LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["session_type"].asInteger()) + { + channel_name = LLTextUtil::formatPhoneNumber(channel_name); + } + childSetTextArg("nearby", "[VOICE_CHANNEL_NAME]", channel_name); + childSetTextArg("nearby_P2P_by_other", "[VOICE_CHANNEL_NAME]", mPayload["disconnected_channel_name"].asString()); + + // skipping "You will now be reconnected to nearby" in notification when call is ended by disabling voice, + // so no reconnection to nearby chat happens (EXT-4397) + bool voice_works = LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); + std::string reconnect_nearby = voice_works ? LLTrans::getString("reconnect_nearby") : std::string(); + childSetTextArg("nearby", "[RECONNECT_NEARBY]", reconnect_nearby); + + const std::string& nearby_str = mPayload["ended_by_agent"] ? NEARBY_P2P_BY_AGENT : NEARBY_P2P_BY_OTHER; + childSetTextArg(nearby_str, "[RECONNECT_NEARBY]", reconnect_nearby); + } + + std::string callee_name = mPayload["session_name"].asString(); + + LLUUID session_id = mPayload["session_id"].asUUID(); + bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); + + if (callee_name == "anonymous") + { + callee_name = getString("anonymous"); + } + else if (!is_avatar) + { + callee_name = LLTextUtil::formatPhoneNumber(callee_name); + } + + setTitle(callee_name); + + LLSD callee_id = mPayload["other_user_id"]; + childSetTextArg("calling", "[CALLEE_NAME]", callee_name); + childSetTextArg("connecting", "[CALLEE_NAME]", callee_name); + + // for outgoing group calls callee_id == group id == session id + setIcon(callee_id, callee_id); + + // stop timer by default + mLifetimeTimer.stop(); + + // show only necessary strings and controls + switch(mPayload["state"].asInteger()) + { + case LLVoiceChannel::STATE_CALL_STARTED : + getChild<LLTextBox>("calling")->setVisible(true); + getChild<LLButton>("Cancel")->setVisible(true); + if(show_oldchannel) + { + getChild<LLTextBox>("leaving")->setVisible(true); + } + break; + case LLVoiceChannel::STATE_RINGING : + if(show_oldchannel) + { + getChild<LLTextBox>("leaving")->setVisible(true); + } + getChild<LLTextBox>("connecting")->setVisible(true); + break; + case LLVoiceChannel::STATE_ERROR : + getChild<LLTextBox>("noanswer")->setVisible(true); + getChild<LLButton>("Cancel")->setVisible(false); + setCanClose(true); + mLifetimeTimer.start(); + break; + case LLVoiceChannel::STATE_HUNG_UP : + if (mPayload["session_type"].asInteger() == LLIMModel::LLIMSession::P2P_SESSION) + { + const std::string& nearby_str = mPayload["ended_by_agent"] ? NEARBY_P2P_BY_AGENT : NEARBY_P2P_BY_OTHER; + getChild<LLTextBox>(nearby_str)->setVisible(true); + } + else + { + getChild<LLTextBox>("nearby")->setVisible(true); + } + getChild<LLButton>("Cancel")->setVisible(false); + setCanClose(true); + mLifetimeTimer.start(); + } + + openFloater(LLOutgoingCallDialog::OCD_KEY); +} + +void LLOutgoingCallDialog::hideAllText() +{ + getChild<LLTextBox>("calling")->setVisible(false); + getChild<LLTextBox>("leaving")->setVisible(false); + getChild<LLTextBox>("connecting")->setVisible(false); + getChild<LLTextBox>("nearby_P2P_by_other")->setVisible(false); + getChild<LLTextBox>("nearby_P2P_by_agent")->setVisible(false); + getChild<LLTextBox>("nearby")->setVisible(false); + getChild<LLTextBox>("noanswer")->setVisible(false); +} + +//static +void LLOutgoingCallDialog::onCancel(void* user_data) +{ + LLOutgoingCallDialog* self = (LLOutgoingCallDialog*)user_data; + + if (!gIMMgr) + return; + + LLUUID session_id = self->mPayload["session_id"].asUUID(); + gIMMgr->endCall(session_id); + + self->closeFloater(); +} + + +BOOL LLOutgoingCallDialog::postBuild() +{ + BOOL success = LLCallDialog::postBuild(); + + childSetAction("Cancel", onCancel, this); + + setCanDrag(FALSE); + + return success; +} + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLIncomingCallDialog //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) : - LLModalDialog(payload), - mPayload(payload) +LLCallDialog(payload) { } +void LLIncomingCallDialog::onLifetimeExpired() +{ + // check whether a call is valid or not + if (LLVoiceClient::getInstance()->findSession(mPayload["caller_id"].asUUID())) + { + // restart notification's timer if call is still valid + mLifetimeTimer.start(); + } + else + { + // close invitation if call is already not valid + mLifetimeTimer.stop(); + LLUUID session_id = mPayload["session_id"].asUUID(); + gIMMgr->clearPendingAgentListUpdates(session_id); + gIMMgr->clearPendingInvitation(session_id); + closeFloater(); + } +} + BOOL LLIncomingCallDialog::postBuild() { - LLSD caller_id = mPayload["caller_id"]; - EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); + LLCallDialog::postBuild(); - std::string call_type = getString("VoiceInviteP2P"); + LLUUID session_id = mPayload["session_id"].asUUID(); + LLSD caller_id = mPayload["caller_id"]; std::string caller_name = mPayload["caller_name"].asString(); + + // init notification's lifetime + std::istringstream ss( getString("lifetime") ); + if (!(ss >> mLifetime)) + { + mLifetime = DEFAULT_LIFETIME; + } + + std::string call_type; + if (gAgent.isInGroup(session_id)) + { + LLStringUtil::format_map_t args; + LLGroupData data; + if (gAgent.getGroupData(session_id, data)) + { + args["[GROUP]"] = data.mName; + call_type = getString(mPayload["notify_box_type"], args); + } + } + else + { + call_type = getString(mPayload["notify_box_type"]); + } + + + // check to see if this is an Avaline call + bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); + childSetVisible("Start IM", is_avatar); // no IM for avaline + if (caller_name == "anonymous") { caller_name = getString("anonymous"); } - - setTitle(caller_name + " " + call_type); - - // If it is not a P2P invite, then it's an AdHoc invite - if ( type != IM_SESSION_P2P_INVITE ) + else if (!is_avatar) { - call_type = getString("VoiceInviteAdHoc"); + caller_name = LLTextUtil::formatPhoneNumber(caller_name); } + setTitle(caller_name + " " + call_type); + LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name"); caller_name_widget->setValue(caller_name + " " + call_type); - LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); - icon->setValue(caller_id); + setIcon(session_id, caller_id); childSetAction("Accept", onAccept, this); childSetAction("Reject", onReject, this); childSetAction("Start IM", onStartIM, this); - childSetFocus("Accept"); + setDefaultBtn("Accept"); + + std::string notify_box_type = mPayload["notify_box_type"].asString(); + if(notify_box_type != "VoiceInviteGroup" && notify_box_type != "VoiceInviteAdHoc") + { + // starting notification's timer for P2P and AVALINE invitations + mLifetimeTimer.start(); + } + else + { + mLifetimeTimer.stop(); + } + + setCanDrag(FALSE); return TRUE; } + +void LLIncomingCallDialog::onOpen(const LLSD& key) +{ + LLCallDialog::onOpen(key); + + // tell the user which voice channel they would be leaving + LLVoiceChannel *voice = LLVoiceChannel::getCurrentVoiceChannel(); + if (voice && !voice->getSessionName().empty()) + { + childSetTextArg("question", "[CURRENT_CHAT]", voice->getSessionName()); + } + else + { + childSetTextArg("question", "[CURRENT_CHAT]", getString("localchat")); + } +} + //static void LLIncomingCallDialog::onAccept(void* user_data) { @@ -1023,7 +1974,12 @@ void LLIncomingCallDialog::onStartIM(void* user_data) void LLIncomingCallDialog::processCallResponse(S32 response) { + if (!gIMMgr || gDisconnected) + return; + LLUUID session_id = mPayload["session_id"].asUUID(); + LLUUID caller_id = mPayload["caller_id"].asUUID(); + std::string session_name = mPayload["session_name"].asString(); EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)mPayload["inv_type"].asInteger(); bool voice = true; @@ -1040,17 +1996,14 @@ void LLIncomingCallDialog::processCallResponse(S32 response) { // create a normal IM session session_id = gIMMgr->addP2PSession( - mPayload["session_name"].asString(), - mPayload["caller_id"].asUUID(), - mPayload["session_handle"].asString()); + session_name, + caller_id, + mPayload["session_handle"].asString(), + mPayload["session_uri"].asString()); if (voice) { - if (gIMMgr->startCall(session_id)) - { - // always open IM window when connecting to voice - LLIMFloater::show(session_id); - } + gIMMgr->startCall(session_id, LLVoiceChannel::INCOMING_CALL); } gIMMgr->clearPendingAgentListUpdates(session_id); @@ -1058,10 +2011,38 @@ void LLIncomingCallDialog::processCallResponse(S32 response) } else { - gIMMgr->addSession( - mPayload["session_name"].asString(), - type, - session_id); + //session name should not be empty, but it can contain spaces so we don't trim + std::string correct_session_name = session_name; + if (session_name.empty()) + { + llwarns << "Received an empty session name from a server" << llendl; + + switch(type){ + case IM_SESSION_CONFERENCE_START: + case IM_SESSION_GROUP_START: + case IM_SESSION_INVITE: + if (gAgent.isInGroup(session_id)) + { + LLGroupData data; + if (!gAgent.getGroupData(session_id, data)) break; + correct_session_name = data.mName; + } + else + { + if (gCacheName->getFullName(caller_id, correct_session_name)) + { + correct_session_name.append(ADHOC_NAME_SUFFIX); + } + } + llinfos << "Corrected session name is " << correct_session_name << llendl; + break; + default: + llwarning("Received an empty session name from a server and failed to generate a new proper session name", 0); + break; + } + } + + LLUUID new_session_id = gIMMgr->addSession(correct_session_name, type, session_id, true); std::string url = gAgent.getRegion()->getCapability( "ChatSessionRequest"); @@ -1077,6 +2058,14 @@ void LLIncomingCallDialog::processCallResponse(S32 response) new LLViewerChatterBoxInvitationAcceptResponder( session_id, inv_type)); + + // send notification message to the corresponding chat + if (mPayload["notify_box_type"].asString() == "VoiceInviteGroup" || mPayload["notify_box_type"].asString() == "VoiceInviteAdHoc") + { + std::string started_call = LLTrans::getString("started_call"); + std::string message = mPayload["caller_name"].asString() + " " + started_call; + LLIMModel::getInstance()->addMessageSilently(session_id, SYSTEM_FROM, LLUUID::null, message); + } } } if (voice) @@ -1114,36 +2103,16 @@ void LLIncomingCallDialog::processCallResponse(S32 response) } } -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLIMViewFriendObserver -// -// Bridge to suport knowing when the inventory has changed. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLIMViewFriendObserver : public LLFriendObserver -{ -public: - LLIMViewFriendObserver(LLIMMgr* tv) : mTV(tv) {} - virtual ~LLIMViewFriendObserver() {} - virtual void changed(U32 mask) - { - if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE)) - { - mTV->refresh(); - } - } -protected: - LLIMMgr* mTV; -}; - - bool inviteUserResponse(const LLSD& notification, const LLSD& response) { + if (!gIMMgr) + return false; + const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"].asUUID(); EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); switch(option) { case 0: // accept @@ -1157,21 +2126,17 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) payload["session_handle"].asString(), payload["session_uri"].asString()); - if (gIMMgr->startCall(session_id)) - { - // always open IM window when connecting to voice - LLIMFloater::show(session_id); - } + gIMMgr->startCall(session_id); gIMMgr->clearPendingAgentListUpdates(session_id); gIMMgr->clearPendingInvitation(session_id); } else { - gIMMgr->addSession( + LLUUID new_session_id = gIMMgr->addSession( payload["session_name"].asString(), type, - session_id); + session_id, true); std::string url = gAgent.getRegion()->getCapability( "ChatSessionRequest"); @@ -1236,29 +2201,12 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) // Member Functions // -LLIMMgr::LLIMMgr() : - mFriendObserver(NULL), - mIMReceived(FALSE) +LLIMMgr::LLIMMgr() { - static bool registered_dialog = false; - if (!registered_dialog) - { - LLFloaterReg::add("incoming_call", "floater_incoming_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIncomingCallDialog>); - registered_dialog = true; - } - - mFriendObserver = new LLIMViewFriendObserver(this); - LLAvatarTracker::instance().addObserver(mFriendObserver); - mPendingInvitations = LLSD::emptyMap(); mPendingAgentListUpdates = LLSD::emptyMap(); -} -LLIMMgr::~LLIMMgr() -{ - LLAvatarTracker::instance().removeObserver(mFriendObserver); - delete mFriendObserver; - // Children all cleaned up by default view destructor. + LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLIMFloater::sRemoveTypingIndicator, _1)); } // Add a message to a session. @@ -1284,7 +2232,6 @@ void LLIMMgr::addMessage( return; } - LLFloaterIMPanel* floater; LLUUID new_session_id = session_id; if (new_session_id.isNull()) { @@ -1299,33 +2246,10 @@ void LLIMMgr::addMessage( fixed_session_name = session_name; } - if (!LLIMModel::getInstance()->findIMSession(new_session_id)) - { - LLIMModel::getInstance()->newSession(session_id, fixed_session_name, dialog, other_participant_id); - } - - floater = findFloaterBySession(new_session_id); - if (!floater) - { - floater = findFloaterBySession(other_participant_id); - if (floater) - { - llinfos << "found the IM session " << session_id - << " by participant " << other_participant_id << llendl; - } - } - - // create IM window as necessary - if(!floater) + bool new_session = !hasSession(new_session_id); + if (new_session) { - - - floater = createFloater( - new_session_id, - other_participant_id, - fixed_session_name, - dialog, - FALSE); + LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id); // When we get a new IM, and if you are a god, display a bit // of information about the source. This is to help liaisons @@ -1336,7 +2260,7 @@ void LLIMMgr::addMessage( std::ostringstream bonus_info; bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + ":" + " " << parent_estate_id - << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "") + << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "") << ((parent_estate_id == 5) ? "," + LLTrans::getString ("IMTeen") : ""); // once we have web-services (or something) which returns @@ -1345,58 +2269,13 @@ void LLIMMgr::addMessage( //<< "*** region_id: " << region_id << std::endl //<< "*** position: " << position << std::endl; - floater->addHistoryLine(bonus_info.str(), LLUIColorTable::instance().getColor("SystemChatColor")); LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str()); } make_ui_sound("UISndNewIncomingIMSession"); } - // now add message to floater - bool is_from_system = target_id.isNull() || (from == SYSTEM_FROM); - const LLColor4& color = ( is_from_system ? - LLUIColorTable::instance().getColor("SystemChatColor") : - LLUIColorTable::instance().getColor("IMChatColor")); - if ( !link_name ) - { - floater->addHistoryLine(msg,color); // No name to prepend, so just add the message normally - } - else - { - floater->addHistoryLine(msg, color, true, other_participant_id, from); // Insert linked name to front of message - - //*TODO consider moving that speaker management stuff into model (IB) - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(new_session_id); - if (speaker_mgr) - { - speaker_mgr->speakerChatted(gAgentID); - speaker_mgr->setSpeakerTyping(gAgentID, FALSE); - } - } - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg); - - if( !LLFloaterReg::instanceVisible("communicate") && !floater->getVisible()) - { - LLFloaterChatterBox* chat_floater = LLFloaterChatterBox::getInstance(); - - //if the IM window is not open and the floater is not visible (i.e. not torn off) - LLFloater* previouslyActiveFloater = chat_floater->getActiveFloater(); - - // select the newly added floater (or the floater with the new line added to it). - // it should be there. - chat_floater->selectFloater(floater); - - //there was a previously unseen IM, make that old tab flashing - //it is assumed that the most recently unseen IM tab is the one current selected/active - if ( previouslyActiveFloater && getIMReceived() ) - { - chat_floater->setFloaterFlashing(previouslyActiveFloater, TRUE); - } - - //notify of a new IM - notifyNewIM(); - } } void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args) @@ -1411,7 +2290,12 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloaterChat::addChatHistory(chat); + + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + if(nearby_chat) + { + nearby_chat->addMessage(chat); + } } else // going to IM session { @@ -1424,47 +2308,45 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess } } -void LLIMMgr::notifyNewIM() -{ - if(!LLFloaterReg::instanceVisible("communicate")) - { - mIMReceived = TRUE; - } -} - S32 LLIMMgr::getNumberOfUnreadIM() { std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it; S32 num = 0; - for(it = LLIMModel::sSessionsMap.begin(); it != LLIMModel::sSessionsMap.end(); ++it) + for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it) { - if((*it).first != mBeingRemovedSessionID) - { - num += (*it).second->mNumUnread; - } + num += (*it).second->mNumUnread; } return num; } -void LLIMMgr::clearNewIMNotification() +S32 LLIMMgr::getNumberOfUnreadParticipantMessages() { - mIMReceived = FALSE; -} + std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it; -BOOL LLIMMgr::getIMReceived() const -{ - return mIMReceived; + S32 num = 0; + for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it) + { + num += (*it).second->mParticipantUnreadMessageCount; + } + + return num; } -// This method returns TRUE if the local viewer has a session -// currently open keyed to the uuid. -BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid) +void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id) { - LLFloaterIMPanel* floater = findFloaterBySession(uuid); - if(floater) return TRUE; - return FALSE; + LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(session_id); + if (!session) return; + + if (session->mSessionInitialized) + { + startCall(session_id); + } + else + { + session->mStartCallOnInitialize = true; + } } LLUUID LLIMMgr::addP2PSession(const std::string& name, @@ -1472,7 +2354,7 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name, const std::string& voice_session_handle, const std::string& caller_uri) { - LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id); + LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id, true); LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); if (speaker_mgr) @@ -1493,11 +2375,11 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name, LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, - const LLUUID& other_participant_id) + const LLUUID& other_participant_id, bool voice) { LLDynamicArray<LLUUID> ids; ids.put(other_participant_id); - return addSession(name, dialog, other_participant_id, ids); + return addSession(name, dialog, other_participant_id, ids, voice); } // Adds a session using the given session_id. If the session already exists @@ -1506,47 +2388,52 @@ LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids) + const LLDynamicArray<LLUUID>& ids, bool voice) { if (0 == ids.getLength()) { return LLUUID::null; } + if (name.empty()) + { + llwarning("Session name cannot be null!", 0); + return LLUUID::null; + } + LLUUID session_id = computeSessionID(dialog,other_participant_id); - if (!LLIMModel::getInstance()->findIMSession(session_id)) + bool new_session = !LLIMModel::getInstance()->findIMSession(session_id); + + //works only for outgoing ad-hoc sessions + if (new_session && IM_SESSION_CONFERENCE_START == dialog && ids.size()) { - LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids); + LLIMModel::LLIMSession* ad_hoc_found = LLIMModel::getInstance()->findAdHocIMSession(ids); + if (ad_hoc_found) + { + new_session = false; + session_id = ad_hoc_found->mSessionID; + } } - LLIMFloater::show(session_id); - - //*TODO remove this "floater" thing when Communicate Floater's gone - LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if(!floater) + if (new_session) { - // On creation, use the first element of ids as the - // "other_participant_id" - floater = createFloater( - session_id, - other_participant_id, - name, - dialog, - TRUE, - ids); + LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voice); } - noteOfflineUsers(session_id, floater, ids); + //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions + if (!new_session) return session_id; + + //Per Plan's suggestion commented "explicit offline status warning" out to make Dessie happier (see EXT-3609) + //*TODO After February 2010 remove this commented out line if no one will be missing that warning + //noteOfflineUsers(session_id, floater, ids); // Only warn for regular IMs - not group IMs if( dialog == IM_NOTHING_SPECIAL ) { - noteMutedUsers(session_id, floater, ids); + noteMutedUsers(session_id, ids); } - - return session_id; } @@ -1560,41 +2447,17 @@ bool LLIMMgr::leaveSession(const LLUUID& session_id) return true; } -// This removes the panel referenced by the uuid, and then restores -// internal consistency. The internal pointer is not deleted? Did you mean -// a pointer to the corresponding LLIMSession? Session data is cleared now. -// Put a copy of UUID to avoid problem when passed reference becames invalid -// if it has been come from the object removed in observer. -void LLIMMgr::removeSession(LLUUID session_id) +// Removes data associated with a particular session specified by session_id +void LLIMMgr::removeSession(const LLUUID& session_id) { - if (mBeingRemovedSessionID == session_id) - { - return; - } + llassert_always(hasSession(session_id)); - LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if(floater) - { - mFloaters.erase(floater->getHandle()); - LLFloaterChatterBox::getInstance()->removeFloater(floater); - //mTabContainer->removeTabPanel(floater); - - clearPendingInvitation(session_id); - clearPendingAgentListUpdates(session_id); - } + clearPendingInvitation(session_id); + clearPendingAgentListUpdates(session_id); - // for some purposes storing ID of a sessios that is being removed - mBeingRemovedSessionID = session_id; - notifyObserverSessionRemoved(session_id); - - //if we don't clear session data on removing the session - //we can't use LLBottomTray as observer of session creation/delettion and - //creating chiclets only on session created even, we need to handle chiclets creation - //the same way as LLFloaterIMPanels were managed. LLIMModel::getInstance()->clearSession(session_id); - // now this session is completely removed - mBeingRemovedSessionID.setNull(); + notifyObserverSessionRemoved(session_id); } void LLIMMgr::inviteToSession( @@ -1682,18 +2545,7 @@ void LLIMMgr::inviteToSession( } else { - if (notify_box_type == "VoiceInviteP2P" || notify_box_type == "VoiceInviteAdHoc") - { - LLFloaterReg::showInstance("incoming_call", payload, TRUE); - } - else - { - LLSD args; - args["NAME"] = caller_name; - args["GROUP"] = session_name; - - LLNotifications::instance().add(notify_box_type, args, payload, &inviteUserResponse); - } + LLFloaterReg::showInstance("incoming_call", payload, FALSE); } mPendingInvitations[session_id.asString()] = LLSD(); } @@ -1706,71 +2558,15 @@ void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::stri std::string notify_box_type = payload["notify_box_type"].asString(); - if (notify_box_type == "VoiceInviteP2P" || notify_box_type == "VoiceInviteAdHoc") - { - LLFloaterReg::showInstance("incoming_call", payload, TRUE); - } - else - { - LLSD args; - args["NAME"] = payload["caller_name"].asString(); - - LLNotifications::instance().add( - payload["notify_box_type"].asString(), - args, - payload, - &inviteUserResponse); - } -} - -void LLIMMgr::refresh() -{ + LLFloaterReg::showInstance("incoming_call", payload, FALSE); } +//*TODO disconnects all sessions void LLIMMgr::disconnectAllSessions() { - LLFloaterIMPanel* floater = NULL; - std::set<LLHandle<LLFloater> >::iterator handle_it; - for(handle_it = mFloaters.begin(); - handle_it != mFloaters.end(); - ) - { - floater = (LLFloaterIMPanel*)handle_it->get(); - - // MUST do this BEFORE calling floater->onClose() because that may remove the item from the set, causing the subsequent increment to crash. - ++handle_it; - - if (floater) - { - floater->setEnabled(FALSE); - floater->closeFloater(TRUE); - } - } -} - - -// This method returns the im panel corresponding to the uuid -// provided. The uuid can either be a session id or an agent -// id. Returns NULL if there is no matching panel. -LLFloaterIMPanel* LLIMMgr::findFloaterBySession(const LLUUID& session_id) -{ - LLFloaterIMPanel* rv = NULL; - std::set<LLHandle<LLFloater> >::iterator handle_it; - for(handle_it = mFloaters.begin(); - handle_it != mFloaters.end(); - ++handle_it) - { - rv = (LLFloaterIMPanel*)handle_it->get(); - if(rv && session_id == rv->getSessionID()) - { - break; - } - rv = NULL; - } - return rv; + //*TODO disconnects all IM sessions } - BOOL LLIMMgr::hasSession(const LLUUID& session_id) { return LLIMModel::getInstance()->findIMSession(session_id) != NULL; @@ -1784,6 +2580,34 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) } } +void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) +{ + LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + if ( im_floater ) + { + im_floater->processAgentListUpdates(body); + } + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); + if (speaker_mgr) + { + speaker_mgr->updateSpeakers(body); + + // also the same call is added into LLVoiceClient::participantUpdatedEvent because + // sometimes it is called AFTER LLViewerChatterBoxSessionAgentListUpdates::post() + // when moderation state changed too late. See EXT-3544. + speaker_mgr->update(true); + } + else + { + //we don't have a speaker manager yet..something went wrong + //we are probably receiving an update here before + //a start or an acceptance of an invitation. Race condition. + gIMMgr->addPendingAgentListUpdates( + session_id, + body); + } +} + LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id) { if ( mPendingAgentListUpdates.has(session_id.asString()) ) @@ -1899,11 +2723,12 @@ void LLIMMgr::removeSessionObserver(LLIMSessionObserver *observer) mSessionObservers.remove(observer); } -bool LLIMMgr::startCall(const LLUUID& session_id) +bool LLIMMgr::startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction) { LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id); if (!voice_channel) return false; + voice_channel->setCallDirection(direction); voice_channel->activate(); return true; } @@ -1917,56 +2742,29 @@ bool LLIMMgr::endCall(const LLUUID& session_id) return true; } -// create a floater and update internal representation for -// consistency. Returns the pointer, caller (the class instance since -// it is a private method) is not responsible for deleting the -// pointer. Add the floater to this but do not select it. -LLFloaterIMPanel* LLIMMgr::createFloater( - const LLUUID& session_id, - const LLUUID& other_participant_id, - const std::string& session_label, - EInstantMessage dialog, - BOOL user_initiated, - const LLDynamicArray<LLUUID>& ids) +bool LLIMMgr::isVoiceCall(const LLUUID& session_id) { - if (session_id.isNull()) - { - llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl; - } + LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id); + if (!im_session) return false; - llinfos << "LLIMMgr::createFloater: from " << other_participant_id - << " in session " << session_id << llendl; - LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label, - session_id, - other_participant_id, - ids, - dialog); - LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; - LLFloaterChatterBox::getInstance()->addFloater(floater, FALSE, i_pt); - mFloaters.insert(floater->getHandle()); - return floater; + return im_session->mStartedAsIMCall; } void LLIMMgr::noteOfflineUsers( const LLUUID& session_id, - LLFloaterIMPanel* floater, const LLDynamicArray<LLUUID>& ids) { S32 count = ids.count(); if(count == 0) { const std::string& only_user = LLTrans::getString("only_user_message"); - if (floater) - { - floater->addHistoryLine(only_user, LLUIColorTable::instance().getColor("SystemChatColor")); - } LLIMModel::getInstance()->addMessage(session_id, SYSTEM_FROM, LLUUID::null, only_user); } else { const LLRelationship* info = NULL; LLAvatarTracker& at = LLAvatarTracker::instance(); - LLIMModel* im_model = LLIMModel::getInstance(); + LLIMModel& im_model = LLIMModel::instance(); for(S32 i = 0; i < count; ++i) { info = at.getBuddyInfo(ids.get(i)); @@ -1977,19 +2775,13 @@ void LLIMMgr::noteOfflineUsers( LLUIString offline = LLTrans::getString("offline_message"); offline.setArg("[FIRST]", first); offline.setArg("[LAST]", last); - - if (floater) - { - floater->addHistoryLine(offline, LLUIColorTable::instance().getColor("SystemChatColor")); - } - - im_model->addMessage(session_id, SYSTEM_FROM, LLUUID::null, offline); + im_model.proccessOnlineOfflineNotification(session_id, offline); } } } } -void LLIMMgr::noteMutedUsers(const LLUUID& session_id, LLFloaterIMPanel* floater, +void LLIMMgr::noteMutedUsers(const LLUUID& session_id, const LLDynamicArray<LLUUID>& ids) { // Don't do this if we don't have a mute list. @@ -2010,9 +2802,6 @@ void LLIMMgr::noteMutedUsers(const LLUUID& session_id, LLFloaterIMPanel* floater { LLUIString muted = LLTrans::getString("muted_message"); - //*TODO remove this "floater" thing when Communicate Floater's gone - floater->addHistoryLine(muted); - im_model->addMessage(session_id, SYSTEM_FROM, LLUUID::null, muted); break; } @@ -2033,10 +2822,10 @@ 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); - LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if (floater) + LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + if ( im_floater ) { - floater->processIMTyping(im_info, typing); + im_floater->processIMTyping(im_info, typing); } } @@ -2069,7 +2858,7 @@ public: { session_id = body["session_id"].asUUID(); - LLIMModel::getInstance()->updateSessionID(temp_session_id, session_id); + LLIMModel::getInstance()->processSessionInitializedReply(temp_session_id, session_id); LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); if (speaker_mgr) @@ -2078,12 +2867,12 @@ public: speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id)); } - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) + LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + if ( im_floater ) { if ( body.has("session_info") ) { - floaterp->processSessionUpdate(body["session_info"]); + im_floater->processSessionUpdate(body["session_info"]); } } @@ -2091,15 +2880,8 @@ public: } else { - //throw an error dialog and close the temp session's - //floater - LLFloaterIMPanel* floater = - gIMMgr->findFloaterBySession(temp_session_id); - - if ( floater ) - { - floater->showSessionStartError(body["error"].asString()); - } + //throw an error dialog and close the temp session's floater + gIMMgr->showSessionStartError(body["error"].asString(), temp_session_id); } gIMMgr->clearPendingAgentListUpdates(session_id); @@ -2132,15 +2914,10 @@ public: if ( !success ) { //throw an error dialog - LLFloaterIMPanel* floater = - gIMMgr->findFloaterBySession(session_id); - - if (floater) - { - floater->showSessionEventError( - body["event"].asString(), - body["error"].asString()); - } + gIMMgr->showSessionEventError( + body["event"].asString(), + body["error"].asString(), + session_id); } } }; @@ -2158,13 +2935,7 @@ public: session_id = input["body"]["session_id"].asUUID(); reason = input["body"]["reason"].asString(); - LLFloaterIMPanel* floater = - gIMMgr ->findFloaterBySession(session_id); - - if ( floater ) - { - floater->showSessionForceClose(reason); - } + gIMMgr->showSessionForceClose(reason, session_id); } }; @@ -2177,20 +2948,7 @@ public: const LLSD& input) const { const LLUUID& session_id = input["body"]["session_id"].asUUID(); - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - if (speaker_mgr) - { - speaker_mgr->updateSpeakers(input["body"]); - } - else - { - //we don't have a speaker manager yet..something went wrong - //we are probably receiving an update here before - //a start or an acceptance of an invitation. Race condition. - gIMMgr->addPendingAgentListUpdates( - input["body"]["session_id"].asUUID(), - input["body"]); - } + gIMMgr->processAgentListUpdates(session_id, input["body"]); } }; @@ -2202,10 +2960,16 @@ public: const LLSD& context, const LLSD& input) const { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); - if (floaterp) + LLUUID session_id = input["body"]["session_id"].asUUID(); + LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + if ( im_floater ) { - floaterp->processSessionUpdate(input["body"]["info"]); + im_floater->processSessionUpdate(input["body"]["info"]); + } + LLIMSpeakerMgr* im_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); + if (im_mgr) + { + im_mgr->processSessionUpdate(input["body"]["info"]); } } }; @@ -2255,15 +3019,6 @@ public: BOOL is_linden = LLMuteList::getInstance()->isLinden(name); std::string separator_string(": "); - int message_offset=0; - - //Handle IRC styled /me messages. - std::string prefix = message.substr(0, 4); - if (prefix == "/me " || prefix == "/me'") - { - separator_string = ""; - message_offset = 3; - } chat.mMuted = is_muted && !is_linden; chat.mFromID = from_id; @@ -2280,7 +3035,7 @@ public: { saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); } - std::string buffer = saved + message.substr(message_offset); + std::string buffer = saved + message; BOOL is_this_agent = FALSE; if(from_id == gAgentID) @@ -2299,9 +3054,6 @@ public: ll_vector3_from_sd(message_params["position"]), true); - chat.mText = std::string("IM: ") + name + separator_string + saved + message.substr(message_offset); - LLFloaterChat::addChat(chat, TRUE, is_this_agent); - //K now we want to accept the invitation std::string url = gAgent.getRegion()->getCapability( "ChatSessionRequest"); |