summaryrefslogtreecommitdiff
path: root/indra/newview/llimview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llimview.cpp')
-rw-r--r--indra/newview/llimview.cpp3289
1 files changed, 2454 insertions, 835 deletions
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 4dc5bfddec..c865dcf9a3 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -2,30 +2,25 @@
* @file LLIMMgr.cpp
* @brief Container for Instant Messaging
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2007, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlife.com/developers/opensource/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlife.com/developers/opensource/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -33,771 +28,1241 @@
#include "llimview.h"
+#include "llavatarnamecache.h" // IDEVO
+#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 "llvieweruictrlfactory.h"
+#include "lltextutil.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 "llfloaternewim.h"
-#include "llhttpnode.h"
-#include "llimpanel.h"
-#include "llresizebar.h"
-#include "lltabcontainer.h"
-#include "llviewercontrol.h"
-#include "llfloater.h"
+#include "llimfloater.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 "llfirstuse.h"
+#include "llnotifications.h"
+#include "llnotificationsutil.h"
+#include "llnearbychat.h"
+#include "llspeakers.h" //for LLIMSpeakerMgr
+#include "lltextbox.h"
+#include "llviewercontrol.h"
+#include "llviewerparcelmgr.h"
-const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START;
-const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL;
-//
-// Globals
-//
-LLIMMgr* gIMMgr = NULL;
+const static std::string ADHOC_NAME_SUFFIX(" Conference");
-//
-// Statics
-//
-//*FIXME: make these all either UIStrings or Strings
-static LLString sOnlyUserMessage;
-static LLUIString sOfflineMessage;
-static LLUIString sInviteMessage;
+const static std::string NEARBY_P2P_BY_OTHER("nearby_P2P_by_other");
+const static std::string NEARBY_P2P_BY_AGENT("nearby_P2P_by_agent");
-std::map<std::string,LLString> LLFloaterIM::sEventStringsMap;
-std::map<std::string,LLString> LLFloaterIM::sErrorStringsMap;
-std::map<std::string,LLString> LLFloaterIM::sForceCloseSessionMap;
+/** 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");
//
-// Helper Functions
+// Globals
//
+LLIMMgr* gIMMgr = NULL;
-// returns true if a should appear before b
-//static BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b )
-//{
-// return (LLString::compareDict( a->mName, b->mName ) < 0);
-//}
+BOOL LLSessionTimeoutTimer::tick()
+{
+ if (mSessionId.isNull()) return TRUE;
-// the other_participant_id is either an agent_id, a group_id, or an inventory
-// folder item_id (collection of calling cards)
+ LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId);
+ if (session && !session->mSessionInitialized)
+ {
+ gIMMgr->showSessionStartError("session_initialization_timed_out_error", mSessionId);
+ }
+ return TRUE;
+}
-// static
-LLUUID LLIMMgr::computeSessionID(
- EInstantMessage dialog,
- const LLUUID& other_participant_id)
+static void on_avatar_name_cache_toast(const LLUUID& agent_id,
+ const LLAvatarName& av_name,
+ LLSD msg)
{
- LLUUID session_id;
- if (IM_SESSION_GROUP_START == dialog)
+ LLSD args;
+ args["MESSAGE"] = msg["message"];
+ args["TIME"] = msg["time"];
+ // *TODO: Can this ever be an object name or group name?
+ args["FROM"] = av_name.getCompleteName();
+ args["FROM_ID"] = msg["from_id"];
+ args["SESSION_ID"] = msg["session_id"];
+ LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID()));
+}
+
+void toast_callback(const LLSD& msg){
+ // do not show toast in busy mode or it goes from agent
+ if (gAgent.getBusy() || gAgent.getID() == msg["from_id"])
{
- // slam group session_id to the group_id (other_participant_id)
- session_id = other_participant_id;
+ return;
}
- else if (IM_SESSION_CONFERENCE_START == dialog)
+
+ // check whether incoming IM belongs to an active session or not
+ if (LLIMModel::getInstance()->getActiveSessionID().notNull()
+ && LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"])
{
- session_id.generate();
+ return;
}
- else if (IM_SESSION_INVITE == dialog)
+
+ // Skip toasting for system messages
+ if (msg["from_id"].asUUID() == LLUUID::null)
{
- // use provided session id for invites
- session_id = other_participant_id;
+ return;
}
- else
+
+ // 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())
{
- LLUUID agent_id = gAgent.getID();
- if (other_participant_id == agent_id)
- {
- // if we try to send an IM to ourselves then the XOR would be null
- // so we just make the session_id the same as the agent_id
- session_id = agent_id;
- }
- else
- {
- // peer-to-peer or peer-to-asset session_id is the XOR
- session_id = other_participant_id ^ agent_id;
- }
+ return;
}
- return session_id;
-}
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// LLFloaterIM
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ LLAvatarNameCache::get(msg["from_id"].asUUID(),
+ boost::bind(&on_avatar_name_cache_toast,
+ _1, _2, msg));
+}
-LLFloaterIM::LLFloaterIM()
+void LLIMModel::setActiveSessionID(const LLUUID& session_id)
{
- // autoresize=false is necessary to avoid resizing of the IM window whenever
- // a session is opened or closed (it would otherwise resize the window to match
- // the size of the im-sesssion when they were created. This happens in
- // LLMultiFloater::resizeToContents() when called through LLMultiFloater::addFloater())
- this->mAutoResize = FALSE;
- gUICtrlFactory->buildFloater(this, "floater_im.xml");
+ // check if such an ID really exists
+ if (!findIMSession(session_id))
+ {
+ llwarns << "Trying to set as active a non-existent session!" << llendl;
+ return;
+ }
+
+ mActiveSessionID = session_id;
}
-BOOL LLFloaterIM::postBuild()
+LLIMModel::LLIMModel()
{
- sOnlyUserMessage = getString("only_user_message");
- sOfflineMessage = getUIString("offline_message");
+ addNewMsgCallback(LLIMFloater::newIMCallback);
+ addNewMsgCallback(toast_callback);
+}
- sInviteMessage = getUIString("invite_message");
+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),
+ mCallBackEnabled(true),
+ mTextIMPossible(true),
+ mOtherParticipantIsAvatar(true),
+ mStartCallOnInitialize(false),
+ mStartedAsIMCall(voice)
+{
+ // set P2P type by default
+ mSessionType = P2P_SESSION;
- if ( sErrorStringsMap.find("generic") == sErrorStringsMap.end() )
+ if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type)
{
- sErrorStringsMap["generic"] =
- getString("generic_request_error");
- }
+ mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id);
+ mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID);
- if ( sErrorStringsMap.find("unverified") ==
- sErrorStringsMap.end() )
+ // check if it was AVALINE call
+ if (!mOtherParticipantIsAvatar)
+ {
+ mSessionType = AVALINE_SESSION;
+ }
+ }
+ else
{
- sErrorStringsMap["unverified"] =
- getString("insufficient_perms_error");
+ 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 ( sErrorStringsMap.end() ==
- sErrorStringsMap.find("no_ability") )
+ if(mVoiceChannel)
{
- sErrorStringsMap["no_ability"] =
- getString("no_ability_error");
+ 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.
- if ( sErrorStringsMap.end() ==
- sErrorStringsMap.find("muted") )
+ // 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
+ if (!LLIMModel::getInstance()->sendStartSession(mSessionID, mOtherParticipantID,
+ mInitialTargetIDs, mType))
{
- sErrorStringsMap["muted"] =
- getString("muted_error");
+ //we don't need to wait for any responses
+ //so we're already initialized
+ mSessionInitialized = true;
}
-
- if ( sErrorStringsMap.end() ==
- sErrorStringsMap.find("not_a_moderator") )
+ else
{
- sErrorStringsMap["not_a_moderator"] =
- getString("not_a_mod_error");
+ //tick returns TRUE - timer will be deleted after the tick
+ new LLSessionTimeoutTimer(mSessionID, SESSION_INITIALIZATION_TIMEOUT);
}
- if ( sErrorStringsMap.end() ==
- sErrorStringsMap.find("does not exist") )
+ if (IM_NOTHING_SPECIAL == type)
{
- sErrorStringsMap["does not exist"] =
- getString("session_does_not_exist_error");
+ mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID);
+ mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionID);
}
- if ( sEventStringsMap.end() == sEventStringsMap.find("add") )
+ buildHistoryFileName();
+
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
{
- sEventStringsMap["add"] =
- getString("add_session_event");
+ std::list<LLSD> chat_history;
+
+ //involves parsing of a chat history
+ LLLogChat::loadAllHistory(mHistoryFileName, chat_history);
+ addMessagesFromHistory(chat_history);
}
- if ( sEventStringsMap.end() == sEventStringsMap.find("message") )
+ // Localizing name of ad-hoc session. STORM-153
+ // Changing name should happen here- after the history file was created, so that
+ // history files have consistent (English) names in different locales.
+ if (isAdHocSessionType() && IM_SESSION_INVITE == type)
{
- sEventStringsMap["message"] =
- getString("message_session_event");
+ // Name here has a form of "<Avatar's name> Conference"
+ // Lets update it to localize the "Conference" word. See EXT-8429.
+ S32 separator_index = mName.rfind(" ");
+ std::string name = mName.substr(0, separator_index);
+ ++separator_index;
+ std::string conference_word = mName.substr(separator_index, mName.length());
+
+ // additional check that session name is what we expected
+ if ("Conference" == conference_word)
+ {
+ LLStringUtil::format_map_t args;
+ args["[AGENT_NAME]"] = name;
+ LLTrans::findString(mName, "conference-title-incoming", args);
+ }
}
+}
+void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction)
+{
+ std::string you_joined_call = LLTrans::getString("you_joined_call");
+ std::string you_started_call = LLTrans::getString("you_started_call");
+ std::string other_avatar_name = "";
- if ( sEventStringsMap.end() == sEventStringsMap.find("mute") )
- {
- sEventStringsMap["mute"] =
- getString("mute_agent_event");
- }
+ std::string message;
- if ( sForceCloseSessionMap.end() ==
- sForceCloseSessionMap.find("removed") )
+ switch(mSessionType)
{
- sForceCloseSessionMap["removed"] =
- getString("removed_from_group");
- }
+ case AVALINE_SESSION:
+ // no text notifications
+ break;
+ case P2P_SESSION:
+ gCacheName->getFullName(mOtherParticipantID, other_avatar_name); // voice
- if ( sForceCloseSessionMap.end() ==
- sForceCloseSessionMap.find("no ability") )
+ if(direction == LLVoiceChannel::INCOMING_CALL)
+ {
+ switch(new_state)
+ {
+ case LLVoiceChannel::STATE_CALL_STARTED :
+ {
+ LLStringUtil::format_map_t string_args;
+ string_args["[NAME]"] = other_avatar_name;
+ message = LLTrans::getString("name_started_call", string_args);
+ LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, message);
+ break;
+ }
+ case LLVoiceChannel::STATE_CONNECTED :
+ LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_joined_call);
+ default:
+ break;
+ }
+ }
+ else // outgoing call
+ {
+ switch(new_state)
+ {
+ case LLVoiceChannel::STATE_CALL_STARTED :
+ LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_started_call);
+ break;
+ case LLVoiceChannel::STATE_CONNECTED :
+ message = LLTrans::getString("answered_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 :
+ LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_joined_call);
+ default:
+ break;
+ }
+ }
+ else // outgoing call
+ {
+ switch(new_state)
+ {
+ case LLVoiceChannel::STATE_CALL_STARTED :
+ LLIMModel::getInstance()->addMessage(mSessionID, SYSTEM_FROM, LLUUID::null, you_started_call);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ // Update speakers list when connected
+ if (LLVoiceChannel::STATE_CONNECTED == new_state)
{
- sForceCloseSessionMap["no ability"] =
- getString("close_on_no_ability");
+ mSpeakers->update(true);
}
-
- return TRUE;
}
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLIMViewFriendObserver
-//
-// Bridge to suport knowing when the inventory has changed.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLIMViewFriendObserver : public LLFriendObserver
+LLIMModel::LLIMSession::~LLIMSession()
{
-public:
- LLIMViewFriendObserver(LLIMMgr* tv) : mTV(tv) {}
- virtual ~LLIMViewFriendObserver() {}
- virtual void changed(U32 mask)
+ delete mSpeakers;
+ mSpeakers = NULL;
+
+ // End the text IM session if necessary
+ if(LLVoiceClient::getInstance() && mOtherParticipantID.notNull())
{
- if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
+ switch(mType)
{
- mTV->refresh();
+ case IM_NOTHING_SPECIAL:
+ case IM_SESSION_P2P_INVITE:
+ LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantID);
+ break;
+
+ default:
+ // Appease the linux compiler
+ break;
}
}
-protected:
- LLIMMgr* mTV;
-};
+ 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();
-class LLIMMgr::LLIMSessionInvite
+ delete mVoiceChannel;
+ mVoiceChannel = NULL;
+}
+
+void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_id)
{
-public:
- LLIMSessionInvite(
- const LLUUID& session_id,
- const LLString& session_name,
- const LLUUID& caller_id,
- const LLString& caller_name,
- EInstantMessage type,
- EInvitationType inv_type,
- const LLString& session_handle,
- const LLString& notify_box) :
- mSessionID(session_id),
- mSessionName(session_name),
- mCallerID(caller_id),
- mCallerName(caller_name),
- mType(type),
- mInvType(inv_type),
- mSessionHandle(session_handle),
- mNotifyBox(notify_box)
- {};
-
- LLUUID mSessionID;
- LLString mSessionName;
- LLUUID mCallerID;
- LLString mCallerName;
- EInstantMessage mType;
- EInvitationType mInvType;
- LLString mSessionHandle;
- LLString mNotifyBox;
-};
+ mSessionInitialized = true;
+
+ if (new_session_id != mSessionID)
+ {
+ mSessionID = new_session_id;
+ mVoiceChannel->updateSessionID(new_session_id);
+ }
+}
+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;
-//
-// Public Static Member Functions
-//
+ mMsgs.push_front(message);
-// This is a helper function to determine what kind of im session
-// should be used for the given agent.
-// static
-EInstantMessage LLIMMgr::defaultIMTypeForAgent(const LLUUID& agent_id)
+ if (mSpeakers && from_id.notNull())
+ {
+ mSpeakers->speakerChatted(from_id);
+ mSpeakers->setSpeakerTyping(from_id, FALSE);
+ }
+}
+
+void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& history)
{
- EInstantMessage type = IM_NOTHING_SPECIAL;
- if(is_agent_friend(agent_id))
+ std::list<LLSD>::const_iterator it = history.begin();
+ while (it != history.end())
{
- if(LLAvatarTracker::instance().isBuddyOnline(agent_id))
+ const LLSD& msg = *it;
+
+ std::string from = msg[IM_FROM];
+ LLUUID from_id;
+ if (msg[IM_FROM_ID].isDefined())
{
- type = IM_SESSION_CONFERENCE_START;
+ from_id = msg[IM_FROM_ID].asUUID();
}
+ else
+ {
+ // Legacy chat logs only wrote the legacy name, not the agent_id
+ 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++;
}
- return type;
}
-// static
-//void LLIMMgr::onPinButton(void*)
-//{
-// BOOL state = gSavedSettings.getBOOL( "PinTalkViewOpen" );
-// gSavedSettings.setBOOL( "PinTalkViewOpen", !state );
-//}
-
-// static
-void LLIMMgr::toggle(void*)
+void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata)
{
- static BOOL return_to_mouselook = FALSE;
+ if (!userdata) return;
- // Hide the button and show the floater or vice versa.
- llassert( gIMMgr );
- BOOL old_state = gIMMgr->getFloaterOpen();
-
- // If we're in mouselook and we triggered the Talk View, we want to talk.
- if( gAgent.cameraMouselook() && old_state )
+ LLIMSession* self = (LLIMSession*) userdata;
+
+ if (type == LLLogChat::LOG_LINE)
{
- return_to_mouselook = TRUE;
- gAgent.changeCameraToDefault();
- return;
+ 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(mId2SessionMap, session_id,
+ (LLIMModel::LLIMSession*) NULL);
+}
+
+//*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;
- BOOL new_state = !old_state;
+ if (mId2SessionMap.empty()) return NULL;
- if (new_state)
+ std::map<LLUUID, LLIMSession*>::const_iterator it = mId2SessionMap.begin();
+ for (; it != mId2SessionMap.end(); ++it)
{
- // ...making visible
- if ( gAgent.cameraMouselook() )
+ 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())
{
- return_to_mouselook = TRUE;
- gAgent.changeCameraToDefault();
+ tmp_list.remove(*iter);
+ ++iter;
+
+ if (tmp_list.empty())
+ {
+ break;
+ }
}
- }
- else
- {
- // ...hiding
- if ( gAgent.cameraThirdPerson() && return_to_mouselook )
+
+ if (tmp_list.empty() && iter == ids.end())
{
- gAgent.changeCameraToMouselook();
+ return session;
}
- return_to_mouselook = FALSE;
}
- gIMMgr->setFloaterOpen( new_state );
+ return NULL;
}
-//
-// Member Functions
-//
-
-LLIMMgr::LLIMMgr() :
- mFriendObserver(NULL),
- mIMReceived(FALSE)
+bool LLIMModel::LLIMSession::isOutgoingAdHoc()
{
- mFriendObserver = new LLIMViewFriendObserver(this);
- LLAvatarTracker::instance().addObserver(mFriendObserver);
+ return IM_SESSION_CONFERENCE_START == mType;
+}
- //*HACK: use floater to initialize string constants from xml file
- // then delete it right away
- LLFloaterIM* dummy_floater = new LLFloaterIM();
- delete dummy_floater;
+bool LLIMModel::LLIMSession::isAdHoc()
+{
+ return IM_SESSION_CONFERENCE_START == mType || (IM_SESSION_INVITE == mType && !gAgent.isInGroup(mSessionID));
+}
- mPendingInvitations = LLSD::emptyMap();
- mPendingAgentListUpdates = LLSD::emptyMap();
+bool LLIMModel::LLIMSession::isP2P()
+{
+ return IM_NOTHING_SPECIAL == mType;
}
-LLIMMgr::~LLIMMgr()
+bool LLIMModel::LLIMSession::isOtherParticipantAvaline()
{
- LLAvatarTracker::instance().removeObserver(mFriendObserver);
- delete mFriendObserver;
- // Children all cleaned up by default view destructor.
+ return !mOtherParticipantIsAvatar;
}
-// Add a message to a session.
-void LLIMMgr::addMessage(
- const LLUUID& session_id,
- const LLUUID& target_id,
- const char* from,
- const char* msg,
- const char* session_name,
- EInstantMessage dialog,
- U32 parent_estate_id,
- const LLUUID& region_id,
- const LLVector3& position)
+void LLIMModel::LLIMSession::onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name)
{
- LLUUID other_participant_id = target_id;
- bool is_from_system = target_id.isNull();
+ // if username is empty, display names isn't enabled, use the display name
+ mHistoryFileName = av_name.mUsername.empty() ? av_name.mDisplayName : av_name.mUsername;
+}
- // don't process muted IMs
- if (gMuteListp->isMuted(
- other_participant_id,
- LLMute::flagTextChat) && !gMuteListp->isLinden(from))
+void LLIMModel::LLIMSession::buildHistoryFileName()
+{
+ mHistoryFileName = mName;
+
+ //ad-hoc requires sophisticated chat history saving schemes
+ if (isAdHoc())
{
- return;
+ //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);
}
- //not sure why...but if it is from ourselves we set the target_id
- //to be NULL
- if( other_participant_id == gAgent.getID() )
+ // look up username to use as the log name
+ if (isP2P())
{
- other_participant_id = LLUUID::null;
+ LLAvatarNameCache::get(mOtherParticipantID, boost::bind(&LLIMModel::LLIMSession::onAvatarNameCache, this, _1, _2));
}
+}
- LLFloaterIMPanel* floater;
- LLUUID new_session_id = session_id;
- if (new_session_id.isNull())
+//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())
{
- //no session ID...compute new one
- new_session_id = computeSessionID(dialog, other_participant_id);
+ md5_uuid.update((unsigned char*)(*it).mData, 16);
+ it++;
}
- floater = findFloaterBySession(new_session_id);
- if (!floater)
+ 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)
{
- floater = findFloaterBySession(other_participant_id);
- if (floater)
+ session->sessionInitReplyReceived(new_session_id);
+
+ if (old_session_id != new_session_id)
{
- llinfos << "found the IM session " << session_id
- << " by participant " << other_participant_id << llendl;
+ mId2SessionMap.erase(old_session_id);
+ mId2SessionMap[new_session_id] = session;
+
+ gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id);
}
- }
- // create IM window as necessary
- if(!floater)
- {
- const char* name = from;
- if(session_name && (strlen(session_name)>1))
+ LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id);
+ if (im_floater)
{
- name = session_name;
+ im_floater->sessionInitReplyReceived(new_session_id);
}
-
- floater = createFloater(
- new_session_id,
- other_participant_id,
- name,
- dialog,
- FALSE);
-
- // 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
- // when answering questions.
- if(gAgent.isGodlike())
+ // auto-start the call on session initialization?
+ if (session->mStartCallOnInitialize)
{
- // *TODO:translate (low priority, god ability)
- std::ostringstream bonus_info;
- bonus_info << "*** parent estate: "
- << parent_estate_id
- << ((parent_estate_id == 1) ? ", mainland" : "")
- << ((parent_estate_id == 5) ? ", teen" : "");
+ gIMMgr->startCall(new_session_id);
+ }
+ }
+}
- // once we have web-services (or something) which returns
- // information about a region id, we can print this out
- // and even have it link to map-teleport or something.
- //<< "*** region_id: " << region_id << std::endl
- //<< "*** position: " << position << std::endl;
+void LLIMModel::testMessages()
+{
+ LLUUID bot1_id("d0426ec6-6535-4c11-a5d9-526bb0c654d9");
+ LLUUID bot1_session_id;
+ std::string from = "IM Tester";
- floater->addHistoryLine(bonus_info.str(), gSavedSettings.getColor4("SystemChatColor"));
- }
+ bot1_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot1_id);
+ newSession(bot1_session_id, from, IM_NOTHING_SPECIAL, bot1_id);
+ addMessage(bot1_session_id, from, bot1_id, "Test Message: Hi from testerbot land!");
- make_ui_sound("UISndNewIncomingIMSession");
- }
+ LLUUID bot2_id;
+ std::string firstname[] = {"Roflcopter", "Joe"};
+ std::string lastname[] = {"Linden", "Tester", "Resident", "Schmoe"};
- // now add message to floater
- if ( is_from_system ) // chat came from system
+ S32 rand1 = ll_rand(sizeof firstname)/(sizeof firstname[0]);
+ S32 rand2 = ll_rand(sizeof lastname)/(sizeof lastname[0]);
+
+ from = firstname[rand1] + " " + lastname[rand2];
+ 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: Hello there, I have a question. Can I bother you for a second? ");
+ addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ.");
+}
+
+//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 (name.empty())
{
- floater->addHistoryLine(
- msg,
- gSavedSettings.getColor4("SystemChatColor"));
+ llwarns << "Attempt to create a new session with empty name; id = " << session_id << llendl;
+ return false;
}
- else
+
+ if (findIMSession(session_id))
{
- floater->addHistoryLine(other_participant_id, msg);
+ llwarns << "IM Session " << session_id << " already exists" << llendl;
+ return false;
}
- LLFloaterChatterBox* chat_floater = LLFloaterChatterBox::getInstance(LLSD());
+ LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice);
+ mId2SessionMap[session_id] = session;
- if( !chat_floater->getVisible() && !floater->getVisible())
- {
- //if the IM window is not open and the floater is not visible (i.e. not torn off)
- LLFloater* previouslyActiveFloater = chat_floater->getActiveFloater();
+ // When notifying observer, name of session is used instead of "name", because they may not be the
+ // same if it is an adhoc session (in this case name is localized in LLIMSession constructor).
+ std::string session_name = LLIMModel::getInstance()->getName(session_id);
+ LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, session_name, other_participant_id);
- // select the newly added floater (or the floater with the new line added to it).
- // it should be there.
- chat_floater->selectFloater(floater);
+ return true;
- //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();
- }
+bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice)
+{
+ uuid_vec_t no_ids;
+ return newSession(session_id, name, type, other_participant_id, no_ids, voice);
}
-void LLIMMgr::addSystemMessage(const LLUUID& session_id, const LLString& message_name, const LLString::format_map_t& args)
+bool LLIMModel::clearSession(const LLUUID& session_id)
{
- LLUIString message;
-
- // null session id means near me (chat history)
- if (session_id.isNull())
+ 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)
{
- LLFloaterChat* floaterp = LLFloaterChat::getInstance();
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return;
+ }
- message = floaterp->getUIString(message_name);
- message.setArgList(args);
+ int i = session->mMsgs.size() - start_index;
- LLChat chat(message);
- chat.mSourceType = CHAT_SOURCE_SYSTEM;
- LLFloaterChat::getInstance()->addChatHistory(chat);
- }
- else // going to IM session
+ for (std::list<LLSD>::iterator iter = session->mMsgs.begin();
+ iter != session->mMsgs.end() && i > 0;
+ iter++)
{
- LLFloaterIMPanel* floaterp = findFloaterBySession(session_id);
- if (floaterp)
- {
- message = floaterp->getUIString(message_name);
- message.setArgList(args);
+ LLSD msg;
+ msg = *iter;
+ messages.push_back(*iter);
+ i--;
+ }
+}
- gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString().c_str());
- }
+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);
}
-void LLIMMgr::notifyNewIM()
+void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index)
{
- if(!gIMMgr->getFloaterOpen())
+ getMessagesSilently(session_id, messages, start_index);
+
+ sendNoUnreadMessages(session_id);
+}
+
+bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) {
+
+ LLIMSession* session = findIMSession(session_id);
+
+ if (!session)
{
- mIMReceived = TRUE;
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return false;
}
+
+ session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false)); //might want to add date separately
+
+ return true;
}
-void LLIMMgr::clearNewIMNotification()
+bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
{
- mIMReceived = FALSE;
+ if (gSavedPerAccountSettings.getBOOL("LogInstantMessages"))
+ {
+ LLLogChat::saveHistory(file_name, from, from_id, utf8_text);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
-BOOL LLIMMgr::getIMReceived() const
+bool LLIMModel::logToFile(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
{
- return mIMReceived;
+ return logToFile(LLIMModel::getInstance()->getHistoryFileName(session_id), from, from_id, utf8_text);
}
-// This method returns TRUE if the local viewer has a session
-// currently open keyed to the uuid.
-BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid)
+bool LLIMModel::proccessOnlineOfflineNotification(
+ const LLUUID& session_id,
+ const std::string& utf8_text)
{
- LLFloaterIMPanel* floater = findFloaterBySession(uuid);
- if(floater) return TRUE;
- return FALSE;
+ // Add system message to history
+ return addMessage(session_id, SYSTEM_FROM, LLUUID::null, utf8_text);
}
-LLUUID LLIMMgr::addP2PSession(const std::string& name,
- const LLUUID& other_participant_id,
- const LLString& voice_session_handle)
+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;
+ arg["time"] = LLLogChat::timestamp(false);
+ mNewMsgSignal(arg);
+
+ 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 */)
{
- LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id);
+ LLIMSession* session = findIMSession(session_id);
- LLFloaterIMPanel* floater = findFloaterBySession(session_id);
- if(floater)
+ if (!session)
{
- LLVoiceChannelP2P* voice_channelp = (LLVoiceChannelP2P*)floater->getVoiceChannel();
- voice_channelp->setSessionHandle(voice_session_handle);
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return NULL;
}
- return session_id;
+ // 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;
}
-// This adds a session to the talk view. The name is the local name of
-// the session, dialog specifies the type of session. If the session
-// exists, it is brought forward. Specifying id = NULL results in an
-// im session to everyone. Returns the uuid of the session.
-LLUUID LLIMMgr::addSession(
- const std::string& name,
- EInstantMessage dialog,
- const LLUUID& other_participant_id)
+
+const std::string LLIMModel::getName(const LLUUID& session_id) const
{
- LLUUID session_id = computeSessionID(dialog, other_participant_id);
+ LLIMSession* session = findIMSession(session_id);
- LLFloaterIMPanel* floater = findFloaterBySession(session_id);
- if(!floater)
+ if (!session)
{
- LLDynamicArray<LLUUID> ids;
- ids.put(other_participant_id);
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return LLTrans::getString("no_session_message");
+ }
- floater = createFloater(
- session_id,
- other_participant_id,
- name,
- ids,
- dialog,
- TRUE);
+ return session->mName;
+}
- noteOfflineUsers(floater, ids);
- LLFloaterChatterBox::showInstance(session_id);
- }
- else
+const S32 LLIMModel::getNumUnread(const LLUUID& session_id) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
{
- floater->open();
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return -1;
}
- //mTabContainer->selectTabPanel(panel);
- floater->setInputFocus(TRUE);
- return floater->getSessionID();
+
+ return session->mNumUnread;
}
-// Adds a session using the given session_id. If the session already exists
-// the dialog type is assumed correct. Returns the uuid of the session.
-LLUUID LLIMMgr::addSession(
- const std::string& name,
- EInstantMessage dialog,
- const LLUUID& other_participant_id,
- const LLDynamicArray<LLUUID>& ids)
+const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const
{
- if (0 == ids.getLength())
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
{
+ llwarns << "session " << session_id << "does not exist " << llendl;
return LLUUID::null;
}
- LLUUID session_id = computeSessionID(
- dialog,
- other_participant_id);
+ return session->mOtherParticipantID;
+}
- LLFloaterIMPanel* floater = findFloaterBySession(session_id);
- if(!floater)
+EInstantMessage LLIMModel::getType(const LLUUID& session_id) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
{
- // On creation, use the first element of ids as the
- // "other_participant_id"
- floater = createFloater(
- session_id,
- other_participant_id,
- name,
- ids,
- dialog,
- TRUE);
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return IM_COUNT;
+ }
- if ( !floater ) return LLUUID::null;
+ return session->mType;
+}
- noteOfflineUsers(floater, ids);
- LLFloaterChatterBox::showInstance(session_id);
- }
- else
+LLVoiceChannel* LLIMModel::getVoiceChannel( const LLUUID& session_id ) const
+{
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
{
- floater->open();
+ llwarns << "session " << session_id << "does not exist " << llendl;
+ return NULL;
}
- //mTabContainer->selectTabPanel(panel);
- floater->setInputFocus(TRUE);
- return floater->getSessionID();
+
+ return session->mVoiceChannel;
}
-// This removes the panel referenced by the uuid, and then restores
-// internal consistency. The internal pointer is not deleted.
-void LLIMMgr::removeSession(const LLUUID& session_id)
+LLIMSpeakerMgr* LLIMModel::getSpeakerManager( const LLUUID& session_id ) const
{
- LLFloaterIMPanel* floater = findFloaterBySession(session_id);
- if(floater)
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
{
- mFloaters.erase(floater->getHandle());
- LLFloaterChatterBox::getInstance(LLSD())->removeFloater(floater);
- //mTabContainer->removeTabPanel(floater);
-
- clearPendingInviation(session_id);
- clearPendingAgentListUpdates(session_id);
+ llwarns << "session " << session_id << " does not exist " << llendl;
+ return NULL;
}
+
+ return session->mSpeakers;
}
-void LLIMMgr::inviteToSession(
- const LLUUID& session_id,
- const LLString& session_name,
- const LLUUID& caller_id,
- const LLString& caller_name,
- EInstantMessage type,
- EInvitationType inv_type,
- const LLString& session_handle)
+const std::string& LLIMModel::getHistoryFileName(const LLUUID& session_id) const
{
- //ignore invites from muted residents
- if (gMuteListp->isMuted(caller_id))
+ LLIMSession* session = findIMSession(session_id);
+ if (!session)
{
- return;
+ llwarns << "session " << session_id << " does not exist " << llendl;
+ return LLStringUtil::null;
}
- LLString notify_box_type;
+ return session->mHistoryFileName;
+}
- BOOL ad_hoc_invite = FALSE;
- if(type == IM_SESSION_P2P_INVITE)
- {
- //P2P is different...they only have voice invitations
- notify_box_type = "VoiceInviteP2P";
- }
- else if ( gAgent.isInGroup(session_id) )
- {
- //only really old school groups have voice invitations
- notify_box_type = "VoiceInviteGroup";
+
+// TODO get rid of other participant ID
+void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing)
+{
+ std::string name;
+ LLAgentUI::buildFullname(name);
+
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ other_participant_id,
+ name,
+ std::string("typing"),
+ IM_ONLINE,
+ (typing ? IM_TYPING_START : IM_TYPING_STOP),
+ session_id);
+ gAgent.sendReliableMessage();
+}
+
+void LLIMModel::sendLeaveSession(const LLUUID& session_id, const LLUUID& other_participant_id)
+{
+ if(session_id.notNull())
+ {
+ std::string name;
+ LLAgentUI::buildFullname(name);
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ other_participant_id,
+ name,
+ LLStringUtil::null,
+ IM_ONLINE,
+ IM_SESSION_LEAVE,
+ session_id);
+ gAgent.sendReliableMessage();
}
- else if ( inv_type == INVITATION_TYPE_VOICE )
+}
+
+//*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,
+ EInstantMessage dialog)
+{
+ std::string name;
+ bool sent = false;
+ LLAgentUI::buildFullname(name);
+
+ const LLRelationship* info = NULL;
+ info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id);
+
+ U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
+
+ if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
{
- //else it's an ad-hoc
- //and a voice ad-hoc
- notify_box_type = "VoiceInviteAdHoc";
- ad_hoc_invite = TRUE;
+ // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice.
+ sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text);
}
- else if ( inv_type == INVITATION_TYPE_IMMEDIATE )
+
+ if(!sent)
{
- notify_box_type = "InviteAdHoc";
- ad_hoc_invite = TRUE;
+ // Send message normally.
+
+ // default to IM_SESSION_SEND unless it's nothing special - in
+ // which case it's probably an IM to everyone.
+ U8 new_dialog = dialog;
+
+ if ( dialog != IM_NOTHING_SPECIAL )
+ {
+ new_dialog = IM_SESSION_SEND;
+ }
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ other_participant_id,
+ name.c_str(),
+ utf8_text.c_str(),
+ offline,
+ (EInstantMessage)new_dialog,
+ im_session_id);
+ gAgent.sendReliableMessage();
}
- LLIMSessionInvite* invite = new LLIMSessionInvite(
- session_id,
- session_name,
- caller_id,
- caller_name,
- type,
- inv_type,
- session_handle,
- notify_box_type);
-
- LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
- if (channelp && channelp->callStarted())
+ // If there is a mute list and this is not a group chat...
+ if ( LLMuteList::getInstance() )
{
- // you have already started a call to the other user, so just accept the invite
- inviteUserResponse(0, invite);
- return;
+ // ... the target should not be in our mute list for some message types.
+ // Auto-remove them if present.
+ switch( dialog )
+ {
+ case IM_NOTHING_SPECIAL:
+ case IM_GROUP_INVITATION:
+ case IM_INVENTORY_OFFERED:
+ case IM_SESSION_INVITE:
+ case IM_SESSION_P2P_INVITE:
+ case IM_SESSION_CONFERENCE_START:
+ case IM_SESSION_SEND: // This one is marginal - erring on the side of hearing.
+ case IM_LURE_USER:
+ case IM_GODLIKE_LURE_USER:
+ case IM_FRIENDSHIP_OFFERED:
+ LLMuteList::getInstance()->autoRemove(other_participant_id, LLMuteList::AR_IM);
+ break;
+ default: ; // do nothing
+ }
}
- if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite)
+ if((dialog == IM_NOTHING_SPECIAL) &&
+ (other_participant_id.notNull()))
{
- // is the inviter a friend?
- if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)
+ // Do we have to replace the /me's here?
+ std::string from;
+ LLAgentUI::buildFullname(from);
+ LLIMModel::getInstance()->addMessage(im_session_id, from, gAgentID, utf8_text);
+
+ //local echo for the legacy communicate panel
+ std::string history_echo;
+ LLAgentUI::buildFullname(history_echo);
+
+ history_echo += ": " + utf8_text;
+
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id);
+ if (speaker_mgr)
{
- // if not, and we are ignoring voice invites from non-friends
- // then silently decline
- if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly"))
- {
- // invite not from a friend, so decline
- inviteUserResponse(1, invite);
- return;
- }
+ speaker_mgr->speakerChatted(gAgentID);
+ speaker_mgr->setSpeakerTyping(gAgentID, FALSE);
}
}
- if ( !mPendingInvitations.has(session_id.asString()) )
+ // Add the recipient to the recent people list.
+ bool is_not_group_id = LLGroupMgr::getInstance()->getGroupData(other_participant_id) == NULL;
+
+ if (is_not_group_id)
{
- if (caller_name.empty())
+ LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(im_session_id);
+ if( session == 0)//??? shouldn't really happen
{
- gCacheName->getName(caller_id, onInviteNameLookup, invite);
+ LLRecentPeople::instance().add(other_participant_id);
}
else
{
- LLString::format_map_t args;
- args["[NAME]"] = caller_name;
- args["[GROUP]"] = session_name;
+ // IM_SESSION_INVITE means that this is an Ad-hoc incoming chat
+ // (it can be also Group chat but it is checked above)
+ // In this case mInitialTargetIDs contains Ad-hoc session ID and it should not be added
+ // to Recent People to prevent showing of an item with (???)(???). See EXT-8246.
+ // Concrete participants will be added into this list once they sent message in chat.
+ if (IM_SESSION_INVITE == dialog) return;
+ // Add only online members to recent (EXT-8658)
+ addSpeakersToRecent(im_session_id);
+ }
+ }
+}
- LLNotifyBox::showXml(notify_box_type,
- args,
- inviteUserResponse,
- (void*)invite);
+void LLIMModel::addSpeakersToRecent(const LLUUID& im_session_id)
+{
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(im_session_id);
+ LLSpeakerMgr::speaker_list_t speaker_list;
+ if(speaker_mgr != NULL)
+ {
+ 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);
+ }
+}
- }
- mPendingInvitations[session_id.asString()] = LLSD();
+void session_starter_helper(
+ const LLUUID& temp_session_id,
+ const LLUUID& other_participant_id,
+ EInstantMessage im_type)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
+ msg->addUUIDFast(_PREHASH_ToAgentID, other_participant_id);
+ msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
+ msg->addU8Fast(_PREHASH_Dialog, im_type);
+ msg->addUUIDFast(_PREHASH_ID, temp_session_id);
+ msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
+
+ std::string name;
+ LLAgentUI::buildFullname(name);
+
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ msg->addStringFast(_PREHASH_Message, LLStringUtil::null);
+ msg->addU32Fast(_PREHASH_ParentEstateID, 0);
+ msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
+ msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
+}
+
+void start_deprecated_conference_chat(
+ const LLUUID& temp_session_id,
+ const LLUUID& creator_id,
+ const LLUUID& other_participant_id,
+ const LLSD& agents_to_invite)
+{
+ U8* bucket;
+ U8* pos;
+ S32 count;
+ S32 bucket_size;
+
+ // *FIX: this could suffer from endian issues
+ count = agents_to_invite.size();
+ bucket_size = UUID_BYTES * count;
+ bucket = new U8[bucket_size];
+ pos = bucket;
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID agent_id = agents_to_invite[i].asUUID();
+
+ memcpy(pos, &agent_id, UUID_BYTES);
+ pos += UUID_BYTES;
}
+
+ session_starter_helper(
+ temp_session_id,
+ other_participant_id,
+ IM_SESSION_CONFERENCE_START);
+
+ gMessageSystem->addBinaryDataFast(
+ _PREHASH_BinaryBucket,
+ bucket,
+ bucket_size);
+
+ gAgent.sendReliableMessage();
+
+ delete[] bucket;
}
-//static
-void LLIMMgr::onInviteNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* userdata)
+class LLStartConferenceChatResponder : public LLHTTPClient::Responder
+{
+public:
+ LLStartConferenceChatResponder(
+ const LLUUID& temp_session_id,
+ const LLUUID& creator_id,
+ const LLUUID& other_participant_id,
+ const LLSD& agents_to_invite)
+ {
+ mTempSessionID = temp_session_id;
+ mCreatorID = creator_id;
+ mOtherParticipantID = other_participant_id;
+ mAgents = agents_to_invite;
+ }
+
+ virtual void error(U32 statusNum, const std::string& reason)
+ {
+ //try an "old school" way.
+ if ( statusNum == 400 )
+ {
+ start_deprecated_conference_chat(
+ mTempSessionID,
+ mCreatorID,
+ mOtherParticipantID,
+ mAgents);
+ }
+
+ //else throw an error back to the client?
+ //in theory we should have just have these error strings
+ //etc. set up in this file as opposed to the IMMgr,
+ //but the error string were unneeded here previously
+ //and it is not worth the effort switching over all
+ //the possible different language translations
+ }
+
+private:
+ LLUUID mTempSessionID;
+ LLUUID mCreatorID;
+ LLUUID mOtherParticipantID;
+
+ LLSD mAgents;
+};
+
+// Returns true if any messages were sent, false otherwise.
+// Is sort of equivalent to "does the server need to do anything?"
+bool LLIMModel::sendStartSession(
+ const LLUUID& temp_session_id,
+ const LLUUID& other_participant_id,
+ const uuid_vec_t& ids,
+ EInstantMessage dialog)
{
- LLIMSessionInvite* invite = (LLIMSessionInvite*)userdata;
+ if ( dialog == IM_SESSION_GROUP_START )
+ {
+ session_starter_helper(
+ temp_session_id,
+ other_participant_id,
+ dialog);
+ gMessageSystem->addBinaryDataFast(
+ _PREHASH_BinaryBucket,
+ EMPTY_BINARY_BUCKET,
+ EMPTY_BINARY_BUCKET_SIZE);
+ gAgent.sendReliableMessage();
+
+ return true;
+ }
+ else if ( dialog == IM_SESSION_CONFERENCE_START )
+ {
+ LLSD agents;
+ for (int i = 0; i < (S32) ids.size(); i++)
+ {
+ agents.append(ids[i]);
+ }
+
+ //we have a new way of starting conference calls now
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string url = region->getCapability(
+ "ChatSessionRequest");
+ LLSD data;
+ data["method"] = "start conference";
+ data["session-id"] = temp_session_id;
+
+ data["params"] = agents;
- invite->mCallerName = llformat("%s %s", first, last);
- invite->mSessionName = invite->mCallerName;
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLStartConferenceChatResponder(
+ temp_session_id,
+ gAgent.getID(),
+ other_participant_id,
+ data["params"]));
+ }
+ else
+ {
+ start_deprecated_conference_chat(
+ temp_session_id,
+ gAgent.getID(),
+ other_participant_id,
+ agents);
+ }
- LLString::format_map_t args;
- args["[NAME]"] = invite->mCallerName;
+ //we also need to wait for reply from the server in case of ad-hoc chat (we'll get new session id)
+ return true;
+ }
- LLNotifyBox::showXml(
- invite->mNotifyBox,
- args,
- inviteUserResponse,
- (void*)invite);
+ return false;
}
+//
+// Helper Functions
+//
+
class LLViewerChatterBoxInvitationAcceptResponder :
public LLHTTPClient::Responder
{
@@ -814,10 +1279,8 @@ public:
{
if ( gIMMgr)
{
- LLFloaterIMPanel* floaterp =
- gIMMgr->findFloaterBySession(mSessionID);
-
- if (floaterp)
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
+ if (speaker_mgr)
{
//we've accepted our invitation
//and received a list of agents that were
@@ -831,30 +1294,30 @@ public:
//but unfortunately, our base that we are receiving here
//may not be the most up to date. It was accurate at
//some point in time though.
- floaterp->setSpeakers(content);
+ speaker_mgr->setSpeakers(content);
//we now have our base of users in the session
//that was accurate at some point, but maybe not now
//so now we apply all of the udpates we've received
//in case of race conditions
- floaterp->updateSpeakersList(
- gIMMgr->getPendingAgentListUpdates(mSessionID));
+ speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID));
+ }
- if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE )
- {
- floaterp->requestAutoConnect();
- LLFloaterIMPanel::onClickStartCall(floaterp);
- // always open IM window when connecting to voice
- LLFloaterChatterBox::showInstance(TRUE);
- }
- else if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE )
- {
- LLFloaterChatterBox::showInstance(TRUE);
- }
+ if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType)
+ {
+ gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL);
+ }
+
+ if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE
+ || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE)
+ && LLIMModel::getInstance()->findIMSession(mSessionID))
+ {
+ // TODO remove in 2010, for voice calls we do not open an IM window
+ //LLIMFloater::show(mSessionID);
}
gIMMgr->clearPendingAgentListUpdates(mSessionID);
- gIMMgr->clearPendingInviation(mSessionID);
+ gIMMgr->clearPendingInvitation(mSessionID);
}
}
@@ -864,26 +1327,12 @@ public:
if ( gIMMgr )
{
gIMMgr->clearPendingAgentListUpdates(mSessionID);
- gIMMgr->clearPendingInviation(mSessionID);
-
- LLFloaterIMPanel* floaterp =
- gIMMgr->findFloaterBySession(mSessionID);
-
- if ( floaterp )
+ gIMMgr->clearPendingInvitation(mSessionID);
+ if ( 404 == statusNum )
{
std::string error_string;
-
- if ( 404 == statusNum )
- {
- error_string = "does not exist";
- }
- else
- {
- error_string = "generic";
- }
-
- floaterp->showSessionStartError(
- error_string);
+ error_string = "session_does_not_exist_error";
+ gIMMgr->showSessionStartError(error_string, mSessionID);
}
}
}
@@ -893,78 +1342,932 @@ private:
LLIMMgr::EInvitationType mInvitiationType;
};
+
+// the other_participant_id is either an agent_id, a group_id, or an inventory
+// folder item_id (collection of calling cards)
+
+// static
+LLUUID LLIMMgr::computeSessionID(
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id)
+{
+ LLUUID session_id;
+ if (IM_SESSION_GROUP_START == dialog)
+ {
+ // slam group session_id to the group_id (other_participant_id)
+ session_id = other_participant_id;
+ }
+ else if (IM_SESSION_CONFERENCE_START == dialog)
+ {
+ session_id.generate();
+ }
+ else if (IM_SESSION_INVITE == dialog)
+ {
+ // use provided session id for invites
+ session_id = other_participant_id;
+ }
+ else
+ {
+ LLUUID agent_id = gAgent.getID();
+ if (other_participant_id == agent_id)
+ {
+ // if we try to send an IM to ourselves then the XOR would be null
+ // so we just make the session_id the same as the agent_id
+ session_id = agent_id;
+ }
+ else
+ {
+ // peer-to-peer or peer-to-asset session_id is the XOR
+ session_id = other_participant_id ^ agent_id;
+ }
+ }
+ 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
-void LLIMMgr::inviteUserResponse(S32 option, void* user_data)
+bool
+LLIMMgr::onConfirmForceCloseError(
+ const LLSD& notification,
+ const LLSD& response)
{
- LLIMSessionInvite* invitep = (LLIMSessionInvite*)user_data;
+ //only 1 option really
+ LLUUID session_id = notification["payload"]["session_id"];
- switch(option)
+ 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;
+
+ static boost::signals2::connection prev_channel_state_changed_connection;
+ // disconnect previously connected callback to avoid have invalid sSession in onVoiceChannelStateChanged()
+ prev_channel_state_changed_connection.disconnect();
+ prev_channel_state_changed_connection =
+ 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);
+ }
+
+ getChild<LLUICtrl>("leaving")->setTextArg("[CURRENT_CHAT]", old_caller_name);
+ show_oldchannel = true;
+ }
+ else
+ {
+ getChild<LLUICtrl>("leaving")->setTextArg("[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);
+ }
+ getChild<LLUICtrl>("nearby")->setTextArg("[VOICE_CHANNEL_NAME]", channel_name);
+
+ // 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::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
+ std::string reconnect_nearby = voice_works ? LLTrans::getString("reconnect_nearby") : std::string();
+ getChild<LLUICtrl>("nearby")->setTextArg("[RECONNECT_NEARBY]", reconnect_nearby);
+
+ const std::string& nearby_str = mPayload["ended_by_agent"] ? NEARBY_P2P_BY_AGENT : NEARBY_P2P_BY_OTHER;
+ getChild<LLUICtrl>(nearby_str)->setTextArg("[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);
+ }
+
+ LLSD callee_id = mPayload["other_user_id"];
+ // Beautification: Since you know who you called, just show display name
+ std::string title = callee_name;
+ std::string final_callee_name = callee_name;
+ if (mPayload["session_type"].asInteger() == LLIMModel::LLIMSession::P2P_SESSION)
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(callee_id, &av_name))
+ {
+ final_callee_name = av_name.mDisplayName;
+ title = av_name.getCompleteName();
+ }
+ }
+ getChild<LLUICtrl>("calling")->setTextArg("[CALLEE_NAME]", final_callee_name);
+ getChild<LLUICtrl>("connecting")->setTextArg("[CALLEE_NAME]", final_callee_name);
+
+ setTitle(title);
+
+ // 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;
+ // STATE_READY is here to show appropriate text for ad-hoc and group calls when floater is shown(EXT-6893)
+ case LLVoiceChannel::STATE_READY :
+ 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) :
+LLCallDialog(payload)
+{
+}
+
+void LLIncomingCallDialog::onLifetimeExpired()
+{
+ std::string session_handle = mPayload["session_handle"].asString();
+ if (LLVoiceClient::getInstance()->isValidChannel(session_handle))
+ {
+ // 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()
+{
+ LLCallDialog::postBuild();
+
+ 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);
+ if (caller_name == "anonymous")
+ {
+ caller_name = getString("anonymous");
+ setCallerName(caller_name, caller_name, call_type);
+ }
+ else if (!is_avatar)
+ {
+ caller_name = LLTextUtil::formatPhoneNumber(caller_name);
+ setCallerName(caller_name, caller_name, call_type);
+ }
+ else
+ {
+ // Get the full name information
+ LLAvatarNameCache::get(caller_id,
+ boost::bind(&LLIncomingCallDialog::onAvatarNameCache,
+ this, _1, _2, call_type));
+ }
+
+ setIcon(session_id, caller_id);
+
+ childSetAction("Accept", onAccept, this);
+ childSetAction("Reject", onReject, this);
+ childSetAction("Start IM", onStartIM, this);
+ 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();
+ }
+
+ //it's not possible to connect to existing Ad-Hoc/Group chat through incoming ad-hoc call
+ //and no IM for avaline
+ getChildView("Start IM")->setVisible( is_avatar && notify_box_type != "VoiceInviteAdHoc" && notify_box_type != "VoiceInviteGroup");
+
+ setCanDrag(FALSE);
+
+ return TRUE;
+}
+
+void LLIncomingCallDialog::setCallerName(const std::string& ui_title,
+ const std::string& ui_label,
+ const std::string& call_type)
+{
+ setTitle(ui_title);
+
+ // call_type may be a string like " is calling."
+ LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name");
+ caller_name_widget->setValue(ui_label + " " + call_type);
+}
+
+void LLIncomingCallDialog::onAvatarNameCache(const LLUUID& agent_id,
+ const LLAvatarName& av_name,
+ const std::string& call_type)
+{
+ std::string title = av_name.getCompleteName();
+ setCallerName(title, av_name.mDisplayName, call_type);
+}
+
+void LLIncomingCallDialog::onOpen(const LLSD& key)
+{
+ LLCallDialog::onOpen(key);
+
+ LLStringUtil::format_map_t args;
+ LLGroupData data;
+ // if it's a group call, retrieve group name to use it in question
+ if (gAgent.getGroupData(key["session_id"].asUUID(), data))
+ {
+ args["[GROUP]"] = data.mName;
+ }
+ // tell the user which voice channel they would be leaving
+ LLVoiceChannel *voice = LLVoiceChannel::getCurrentVoiceChannel();
+ if (voice && !voice->getSessionName().empty())
+ {
+ args["[CURRENT_CHAT]"] = voice->getSessionName();
+ getChild<LLUICtrl>("question")->setValue(getString(key["question_type"].asString(), args));
+ }
+ else
+ {
+ args["[CURRENT_CHAT]"] = getString("localchat");
+ getChild<LLUICtrl>("question")->setValue(getString(key["question_type"].asString(), args));
+ }
+}
+
+//static
+void LLIncomingCallDialog::onAccept(void* user_data)
+{
+ LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
+ self->processCallResponse(0);
+ self->closeFloater();
+}
+
+//static
+void LLIncomingCallDialog::onReject(void* user_data)
+{
+ LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
+ self->processCallResponse(1);
+ self->closeFloater();
+}
+
+//static
+void LLIncomingCallDialog::onStartIM(void* user_data)
+{
+ LLIncomingCallDialog* self = (LLIncomingCallDialog*)user_data;
+ self->processCallResponse(2);
+ self->closeFloater();
+}
+
+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;
+ switch(response)
+ {
+ case 2: // start IM: just don't start the voice chat
+ {
+ voice = false;
+ /* FALLTHROUGH */
+ }
case 0: // accept
+ {
+ if (type == IM_SESSION_P2P_INVITE)
{
- if (invitep->mType == IM_SESSION_P2P_INVITE)
+ // create a normal IM session
+ session_id = gIMMgr->addP2PSession(
+ session_name,
+ caller_id,
+ mPayload["session_handle"].asString(),
+ mPayload["session_uri"].asString());
+
+ if (voice)
{
- // create a normal IM session
- invitep->mSessionID = gIMMgr->addP2PSession(
- invitep->mSessionName,
- invitep->mCallerID,
- invitep->mSessionHandle);
-
- LLFloaterIMPanel* im_floater =
- gIMMgr->findFloaterBySession(
- invitep->mSessionID);
- if (im_floater)
+ gIMMgr->startCall(session_id, LLVoiceChannel::INCOMING_CALL);
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
+ }
+ else
+ {
+ //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
+ {
+ // *NOTE: really should be using callbacks here
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(caller_id, &av_name))
+ {
+ correct_session_name = av_name.mDisplayName + " (" + av_name.mUsername + ")";
+ 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");
+
+ if (voice)
+ {
+ LLSD data;
+ data["method"] = "accept invitation";
+ data["session-id"] = session_id;
+ LLHTTPClient::post(
+ url,
+ data,
+ 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")
{
- im_floater->requestAutoConnect();
- LLFloaterIMPanel::onClickStartCall(im_floater);
- // always open IM window when connecting to voice
- LLFloaterChatterBox::showInstance(invitep->mSessionID);
+ LLStringUtil::format_map_t string_args;
+ string_args["[NAME]"] = mPayload["caller_name"].asString();
+ std::string message = LLTrans::getString("name_started_call", string_args);
+ LLIMModel::getInstance()->addMessageSilently(session_id, SYSTEM_FROM, LLUUID::null, message);
}
+ }
+ }
+ if (voice)
+ {
+ break;
+ }
+ }
+ case 1: // decline
+ {
+ if (type == IM_SESSION_P2P_INVITE)
+ {
+ if(LLVoiceClient::getInstance())
+ {
+ std::string s = mPayload["session_handle"].asString();
+ LLVoiceClient::getInstance()->declineInvite(s);
+ }
+ }
+ else
+ {
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ LLSD data;
+ data["method"] = "decline invitation";
+ data["session-id"] = session_id;
+ LLHTTPClient::post(
+ url,
+ data,
+ NULL);
+ }
+ }
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
+ }
+}
- gIMMgr->clearPendingAgentListUpdates(invitep->mSessionID);
- gIMMgr->clearPendingInviation(invitep->mSessionID);
+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 = LLNotificationsUtil::getSelectedOption(notification, response);
+ switch(option)
+ {
+ case 0: // accept
+ {
+ if (type == IM_SESSION_P2P_INVITE)
+ {
+ // create a normal IM session
+ session_id = gIMMgr->addP2PSession(
+ payload["session_name"].asString(),
+ payload["caller_id"].asUUID(),
+ payload["session_handle"].asString(),
+ payload["session_uri"].asString());
+
+ gIMMgr->startCall(session_id);
+
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
}
else
{
- gIMMgr->addSession(
- invitep->mSessionName,
- invitep->mType,
- invitep->mSessionID);
+ LLUUID new_session_id = gIMMgr->addSession(
+ payload["session_name"].asString(),
+ type,
+ session_id, true);
std::string url = gAgent.getRegion()->getCapability(
"ChatSessionRequest");
LLSD data;
data["method"] = "accept invitation";
- data["session-id"] = invitep->mSessionID;
+ data["session-id"] = session_id;
LLHTTPClient::post(
url,
data,
new LLViewerChatterBoxInvitationAcceptResponder(
- invitep->mSessionID,
- invitep->mInvType));
+ session_id,
+ inv_type));
}
}
break;
case 2: // mute (also implies ignore, so this falls through to the "ignore" case below)
{
// mute the sender of this invite
- if (!gMuteListp->isMuted(invitep->mCallerID))
+ if (!LLMuteList::getInstance()->isMuted(payload["caller_id"].asUUID()))
{
- LLMute mute(invitep->mCallerID, invitep->mCallerName, LLMute::AGENT);
- gMuteListp->add(mute);
+ LLMute mute(payload["caller_id"].asUUID(), payload["caller_name"].asString(), LLMute::AGENT);
+ LLMuteList::getInstance()->add(mute);
}
}
/* FALLTHROUGH */
case 1: // decline
{
- if (invitep->mType == IM_SESSION_P2P_INVITE)
+ if (type == IM_SESSION_P2P_INVITE)
{
- if(gVoiceClient)
- {
- gVoiceClient->declineInvite(invitep->mSessionHandle);
- }
+ std::string s = payload["session_handle"].asString();
+ LLVoiceClient::getInstance()->declineInvite(s);
}
else
{
@@ -973,7 +2276,7 @@ void LLIMMgr::inviteUserResponse(S32 option, void* user_data)
LLSD data;
data["method"] = "decline invitation";
- data["session-id"] = invitep->mSessionID;
+ data["session-id"] = session_id;
LLHTTPClient::post(
url,
data,
@@ -981,86 +2284,403 @@ void LLIMMgr::inviteUserResponse(S32 option, void* user_data)
}
}
- gIMMgr->clearPendingAgentListUpdates(invitep->mSessionID);
- gIMMgr->clearPendingInviation(invitep->mSessionID);
+ gIMMgr->clearPendingAgentListUpdates(session_id);
+ gIMMgr->clearPendingInvitation(session_id);
break;
}
- delete invitep;
+ return false;
+}
+
+//
+// Member Functions
+//
+
+LLIMMgr::LLIMMgr()
+{
+ mPendingInvitations = LLSD::emptyMap();
+ mPendingAgentListUpdates = LLSD::emptyMap();
+
+ LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLIMFloater::sRemoveTypingIndicator, _1));
+}
+
+// Add a message to a session.
+void LLIMMgr::addMessage(
+ const LLUUID& session_id,
+ const LLUUID& target_id,
+ const std::string& from,
+ const std::string& msg,
+ const std::string& session_name,
+ EInstantMessage dialog,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ bool link_name) // If this is true, then we insert the name and link it to a profile
+{
+ LLUUID other_participant_id = target_id;
+
+ // don't process muted IMs
+ if (LLMuteList::getInstance()->isMuted(
+ other_participant_id,
+ LLMute::flagTextChat) && !LLMuteList::getInstance()->isLinden(from))
+ {
+ return;
+ }
+
+ LLUUID new_session_id = session_id;
+ if (new_session_id.isNull())
+ {
+ //no session ID...compute new one
+ new_session_id = computeSessionID(dialog, other_participant_id);
+ }
+
+ //*NOTE session_name is empty in case of incoming P2P sessions
+ std::string fixed_session_name = from;
+ if(!session_name.empty() && session_name.size()>1)
+ {
+ fixed_session_name = session_name;
+ }
+
+ bool new_session = !hasSession(new_session_id);
+ if (new_session)
+ {
+ 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
+ // when answering questions.
+ if(gAgent.isGodlike())
+ {
+ // *TODO:translate (low priority, god ability)
+ std::ostringstream bonus_info;
+ bonus_info << LLTrans::getString("***")+ " "+ LLTrans::getString("IMParentEstate") + ":" + " "
+ << parent_estate_id
+ << ((parent_estate_id == 1) ? "," + LLTrans::getString("IMMainland") : "")
+ << ((parent_estate_id == 5) ? "," + LLTrans::getString ("IMTeen") : "");
+
+ // once we have web-services (or something) which returns
+ // information about a region id, we can print this out
+ // and even have it link to map-teleport or something.
+ //<< "*** region_id: " << region_id << std::endl
+ //<< "*** position: " << position << std::endl;
+
+ LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str());
+ }
+
+ make_ui_sound("UISndNewIncomingIMSession");
+ }
+
+ LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg);
+}
+
+void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args)
+{
+ LLUIString message;
+
+ // null session id means near me (chat history)
+ if (session_id.isNull())
+ {
+ message = LLTrans::getString(message_name);
+ message.setArgs(args);
+
+ LLChat chat(message);
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
+
+ LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD());
+ if(nearby_chat)
+ {
+ nearby_chat->addMessage(chat);
+ }
+ }
+ else // going to IM session
+ {
+ message = LLTrans::getString(message_name + "-im");
+ message.setArgs(args);
+ if (hasSession(session_id))
+ {
+ gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString());
+ }
+ // log message to file
+ else
+ {
+ std::string session_name;
+ // since we select user to share item with - his name is already in cache
+ gCacheName->getFullName(args["user_id"], session_name);
+ LLIMModel::instance().logToFile(session_name, SYSTEM_FROM, LLUUID::null, message.getString());
+ }
+ }
+}
+
+S32 LLIMMgr::getNumberOfUnreadIM()
+{
+ std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it;
+
+ S32 num = 0;
+ for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it)
+ {
+ num += (*it).second->mNumUnread;
+ }
+
+ return num;
}
-void LLIMMgr::refresh()
+S32 LLIMMgr::getNumberOfUnreadParticipantMessages()
{
+ std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it;
+
+ S32 num = 0;
+ for(it = LLIMModel::getInstance()->mId2SessionMap.begin(); it != LLIMModel::getInstance()->mId2SessionMap.end(); ++it)
+ {
+ num += (*it).second->mParticipantUnreadMessageCount;
+ }
+
+ return num;
}
-void LLIMMgr::setFloaterOpen(BOOL set_open)
+void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id)
{
- if (set_open)
+ LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (!session) return;
+
+ if (session->mSessionInitialized)
{
- LLFloaterChatterBox::showInstance();
+ startCall(session_id);
}
else
{
- LLFloaterChatterBox::hideInstance();
- }
+ session->mStartCallOnInitialize = true;
+ }
}
+LLUUID LLIMMgr::addP2PSession(const std::string& name,
+ const LLUUID& other_participant_id,
+ const std::string& voice_session_handle,
+ const std::string& caller_uri)
+{
+ LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id, true);
+
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
+ if (speaker_mgr)
+ {
+ LLVoiceChannelP2P* voice_channel = dynamic_cast<LLVoiceChannelP2P*>(speaker_mgr->getVoiceChannel());
+ if (voice_channel)
+ {
+ voice_channel->setSessionHandle(voice_session_handle, caller_uri);
+ }
+ }
+ return session_id;
+}
-BOOL LLIMMgr::getFloaterOpen()
+// This adds a session to the talk view. The name is the local name of
+// the session, dialog specifies the type of session. If the session
+// exists, it is brought forward. Specifying id = NULL results in an
+// im session to everyone. Returns the uuid of the session.
+LLUUID LLIMMgr::addSession(
+ const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id, bool voice)
{
- return LLFloaterChatterBox::instanceVisible(LLSD());
+ LLDynamicArray<LLUUID> ids;
+ ids.put(other_participant_id);
+ return addSession(name, dialog, other_participant_id, ids, voice);
}
-
-void LLIMMgr::disconnectAllSessions()
+
+// Adds a session using the given session_id. If the session already exists
+// the dialog type is assumed correct. Returns the uuid of the session.
+LLUUID LLIMMgr::addSession(
+ const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id,
+ const LLDynamicArray<LLUUID>& ids, bool voice)
{
- LLFloaterIMPanel* floater = NULL;
- std::set<LLHandle<LLFloater> >::iterator handle_it;
- for(handle_it = mFloaters.begin();
- handle_it != mFloaters.end();
- )
+ if (0 == ids.getLength())
{
- floater = (LLFloaterIMPanel*)handle_it->get();
+ return LLUUID::null;
+ }
- // 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 (name.empty())
+ {
+ llwarning("Session name cannot be null!", 0);
+ return LLUUID::null;
+ }
+
+ LLUUID session_id = computeSessionID(dialog,other_participant_id);
+
+ bool new_session = !LLIMModel::getInstance()->findIMSession(session_id);
- if (floater)
+ //works only for outgoing ad-hoc sessions
+ if (new_session && IM_SESSION_CONFERENCE_START == dialog && ids.size())
+ {
+ LLIMModel::LLIMSession* ad_hoc_found = LLIMModel::getInstance()->findAdHocIMSession(ids);
+ if (ad_hoc_found)
{
- floater->setEnabled(FALSE);
- floater->close(TRUE);
+ new_session = false;
+ session_id = ad_hoc_found->mSessionID;
}
}
+
+ if (new_session)
+ {
+ LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voice);
+ }
+
+ //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, ids);
+ }
+
+ return session_id;
}
+bool LLIMMgr::leaveSession(const LLUUID& session_id)
+{
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (!im_session) return false;
+
+ LLIMModel::getInstance()->sendLeaveSession(session_id, im_session->mOtherParticipantID);
+ gIMMgr->removeSession(session_id);
+ return 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)
+// Removes data associated with a particular session specified by session_id
+void LLIMMgr::removeSession(const LLUUID& session_id)
+{
+ llassert_always(hasSession(session_id));
+
+ clearPendingInvitation(session_id);
+ clearPendingAgentListUpdates(session_id);
+
+ LLIMModel::getInstance()->clearSession(session_id);
+
+ notifyObserverSessionRemoved(session_id);
+}
+
+void LLIMMgr::inviteToSession(
+ const LLUUID& session_id,
+ const std::string& session_name,
+ const LLUUID& caller_id,
+ const std::string& caller_name,
+ EInstantMessage type,
+ EInvitationType inv_type,
+ const std::string& session_handle,
+ const std::string& session_uri)
{
- LLFloaterIMPanel* rv = NULL;
- std::set<LLHandle<LLFloater> >::iterator handle_it;
- for(handle_it = mFloaters.begin();
- handle_it != mFloaters.end();
- ++handle_it)
+ //ignore invites from muted residents
+ if (LLMuteList::getInstance()->isMuted(caller_id))
+ {
+ return;
+ }
+
+ std::string notify_box_type;
+ // voice invite question is different from default only for group call (EXT-7118)
+ std::string question_type = "VoiceInviteQuestionDefault";
+
+ BOOL ad_hoc_invite = FALSE;
+ if(type == IM_SESSION_P2P_INVITE)
+ {
+ //P2P is different...they only have voice invitations
+ notify_box_type = "VoiceInviteP2P";
+ }
+ else if ( gAgent.isInGroup(session_id) )
+ {
+ //only really old school groups have voice invitations
+ notify_box_type = "VoiceInviteGroup";
+ question_type = "VoiceInviteQuestionGroup";
+ }
+ else if ( inv_type == INVITATION_TYPE_VOICE )
+ {
+ //else it's an ad-hoc
+ //and a voice ad-hoc
+ notify_box_type = "VoiceInviteAdHoc";
+ ad_hoc_invite = TRUE;
+ }
+ else if ( inv_type == INVITATION_TYPE_IMMEDIATE )
+ {
+ notify_box_type = "InviteAdHoc";
+ ad_hoc_invite = TRUE;
+ }
+
+ LLSD payload;
+ payload["session_id"] = session_id;
+ payload["session_name"] = session_name;
+ payload["caller_id"] = caller_id;
+ payload["caller_name"] = caller_name;
+ payload["type"] = type;
+ payload["inv_type"] = inv_type;
+ payload["session_handle"] = session_handle;
+ payload["session_uri"] = session_uri;
+ payload["notify_box_type"] = notify_box_type;
+ payload["question_type"] = question_type;
+
+ LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
+ if (channelp && channelp->callStarted())
{
- rv = (LLFloaterIMPanel*)handle_it->get();
- if(rv && session_id == rv->getSessionID())
+ // you have already started a call to the other user, so just accept the invite
+ LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 0);
+ return;
+ }
+
+ if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite)
+ {
+ // is the inviter a friend?
+ if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)
{
- break;
+ // if not, and we are ignoring voice invites from non-friends
+ // then silently decline
+ if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly"))
+ {
+ // invite not from a friend, so decline
+ LLNotifications::instance().forceResponse(LLNotification::Params("VoiceInviteP2P").payload(payload), 1);
+ return;
+ }
}
- rv = NULL;
}
- return rv;
+
+ if ( !mPendingInvitations.has(session_id.asString()) )
+ {
+ if (caller_name.empty())
+ {
+ gCacheName->get(caller_id, false, // voice
+ boost::bind(&LLIMMgr::onInviteNameLookup, payload, _1, _2, _3));
+ }
+ else
+ {
+ LLFloaterReg::showInstance("incoming_call", payload, FALSE);
+ }
+ mPendingInvitations[session_id.asString()] = LLSD();
+ }
}
+void LLIMMgr::onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group)
+{
+ payload["caller_name"] = name;
+ payload["session_name"] = payload["caller_name"].asString();
+
+ std::string notify_box_type = payload["notify_box_type"].asString();
+
+ LLFloaterReg::showInstance("incoming_call", payload, FALSE);
+}
+
+//*TODO disconnects all sessions
+void LLIMMgr::disconnectAllSessions()
+{
+ //*TODO disconnects all IM sessions
+}
BOOL LLIMMgr::hasSession(const LLUUID& session_id)
{
- return (findFloaterBySession(session_id) != NULL);
+ return LLIMModel::getInstance()->findIMSession(session_id) != NULL;
}
-void LLIMMgr::clearPendingInviation(const LLUUID& session_id)
+void LLIMMgr::clearPendingInvitation(const LLUUID& session_id)
{
if ( mPendingInvitations.has(session_id.asString()) )
{
@@ -1068,6 +2688,34 @@ void LLIMMgr::clearPendingInviation(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()) )
@@ -1148,84 +2796,129 @@ void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id)
}
}
-// 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)
+void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
{
- if (session_id.isNull())
+ for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
{
- llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
+ (*it)->sessionAdded(session_id, name, other_participant_id);
}
+}
- llinfos << "LLIMMgr::createFloater: from " << other_participant_id
- << " in session " << session_id << llendl;
- LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
- session_id,
- other_participant_id,
- dialog);
- LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END;
- LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt);
- mFloaters.insert(floater->getHandle());
- return floater;
+void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id)
+{
+ for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
+ {
+ (*it)->sessionRemoved(session_id);
+ }
}
-LLFloaterIMPanel* LLIMMgr::createFloater(
- const LLUUID& session_id,
- const LLUUID& other_participant_id,
- const std::string& session_label,
- const LLDynamicArray<LLUUID>& ids,
- EInstantMessage dialog,
- BOOL user_initiated)
+void LLIMMgr::notifyObserverSessionIDUpdated( const LLUUID& old_session_id, const LLUUID& new_session_id )
{
- if (session_id.isNull())
+ for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
{
- llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
+ (*it)->sessionIDUpdated(old_session_id, new_session_id);
}
- 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(LLSD())->addFloater(floater, FALSE, i_pt);
- mFloaters.insert(floater->getHandle());
- return floater;
+}
+
+void LLIMMgr::addSessionObserver(LLIMSessionObserver *observer)
+{
+ mSessionObservers.push_back(observer);
+}
+
+void LLIMMgr::removeSessionObserver(LLIMSessionObserver *observer)
+{
+ mSessionObservers.remove(observer);
+}
+
+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;
+}
+
+bool LLIMMgr::endCall(const LLUUID& session_id)
+{
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(session_id);
+ if (!voice_channel) return false;
+
+ voice_channel->deactivate();
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (im_session)
+ {
+ // need to update speakers' state
+ im_session->mSpeakers->update(FALSE);
+ }
+ return true;
+}
+
+bool LLIMMgr::isVoiceCall(const LLUUID& session_id)
+{
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (!im_session) return false;
+
+ return im_session->mStartedAsIMCall;
}
void LLIMMgr::noteOfflineUsers(
- LLFloaterIMPanel* floater,
+ const LLUUID& session_id,
const LLDynamicArray<LLUUID>& ids)
{
S32 count = ids.count();
if(count == 0)
{
- floater->addHistoryLine(sOnlyUserMessage, gSavedSettings.getColor4("SystemChatColor"));
+ const std::string& only_user = LLTrans::getString("only_user_message");
+ LLIMModel::getInstance()->addMessage(session_id, SYSTEM_FROM, LLUUID::null, only_user);
}
else
{
const LLRelationship* info = NULL;
LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLIMModel& im_model = LLIMModel::instance();
for(S32 i = 0; i < count; ++i)
{
info = at.getBuddyInfo(ids.get(i));
- std::string first, last;
- if(info && !info->isOnline()
- && gCacheName->getName(ids.get(i), first, last))
+ LLAvatarName av_name;
+ if (info
+ && !info->isOnline()
+ && LLAvatarNameCache::get(ids.get(i), &av_name))
{
- LLUIString offline = sOfflineMessage;
- offline.setArg("[FIRST]", first);
- offline.setArg("[LAST]", last);
- floater->addHistoryLine(offline, gSavedSettings.getColor4("SystemChatColor"));
+ LLUIString offline = LLTrans::getString("offline_message");
+ // Use display name only because this user is your friend
+ offline.setArg("[NAME]", av_name.mDisplayName);
+ im_model.proccessOnlineOfflineNotification(session_id, offline);
+ }
+ }
+ }
+}
+
+void LLIMMgr::noteMutedUsers(const LLUUID& session_id,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ // Don't do this if we don't have a mute list.
+ LLMuteList *ml = LLMuteList::getInstance();
+ if( !ml )
+ {
+ return;
+ }
+
+ S32 count = ids.count();
+ if(count > 0)
+ {
+ LLIMModel* im_model = LLIMModel::getInstance();
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ if( ml->isMuted(ids.get(i)) )
+ {
+ LLUIString muted = LLTrans::getString("muted_message");
+
+ im_model->addMessage(session_id, SYSTEM_FROM, LLUUID::null, muted);
+ break;
}
}
}
@@ -1244,29 +2937,13 @@ 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)
- {
- floater->processIMTyping(im_info, typing);
- }
-}
-
-void LLIMMgr::updateFloaterSessionID(
- const LLUUID& old_session_id,
- const LLUUID& new_session_id)
-{
- LLFloaterIMPanel* floater = findFloaterBySession(old_session_id);
- if (floater)
+ LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ if ( im_floater )
{
- floater->sessionInitReplyReceived(new_session_id);
+ im_floater->processIMTyping(im_info, typing);
}
}
-LLFloaterChatterBox* LLIMMgr::getFloater()
-{
- return LLFloaterChatterBox::getInstance(LLSD());
-}
-
class LLViewerChatterBoxSessionStartReply : public LLHTTPNode
{
public:
@@ -1295,40 +2972,31 @@ public:
if ( success )
{
session_id = body["session_id"].asUUID();
- gIMMgr->updateFloaterSessionID(
- temp_session_id,
- session_id);
- LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id);
- if (floaterp)
- {
- floaterp->setSpeakers(body);
- //apply updates we've possibly received previously
- floaterp->updateSpeakersList(
- gIMMgr->getPendingAgentListUpdates(session_id));
+ LLIMModel::getInstance()->processSessionInitializedReply(temp_session_id, session_id);
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
+ if (speaker_mgr)
+ {
+ speaker_mgr->setSpeakers(body);
+ speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id));
+ }
+
+ 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"]);
}
-
- //aply updates we've possibly received previously
- floaterp->updateSpeakersList(
- gIMMgr->getPendingAgentListUpdates(session_id));
}
+
gIMMgr->clearPendingAgentListUpdates(session_id);
}
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);
@@ -1361,15 +3029,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);
}
}
};
@@ -1382,18 +3045,12 @@ public:
const LLSD& input) const
{
LLUUID session_id;
- LLString reason;
+ std::string reason;
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);
}
};
@@ -1405,21 +3062,8 @@ public:
const LLSD& context,
const LLSD& input) const
{
- LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID());
- if (floaterp)
- {
- floaterp->updateSpeakersList(
- input["body"]);
- }
- else
- {
- //we don't have a floater 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"]);
- }
+ const LLUUID& session_id = input["body"]["session_id"].asUUID();
+ gIMMgr->processAgentListUpdates(session_id, input["body"]);
}
};
@@ -1431,10 +3075,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 )
+ {
+ im_floater->processSessionUpdate(input["body"]["info"]);
+ }
+ LLIMSpeakerMgr* im_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id);
+ if (im_mgr)
{
- floaterp->processSessionUpdate(input["body"]["info"]);
+ im_mgr->processSessionUpdate(input["body"]["info"]);
}
}
};
@@ -1464,7 +3114,6 @@ public:
{
return;
}
- char buffer[DB_IM_MSG_BUF_SIZE * 2]; /* Flawfinder: ignore */
LLChat chat;
std::string message = message_params["message"].asString();
@@ -1478,23 +3127,13 @@ public:
(time_t) message_params["timestamp"].asInteger();
BOOL is_busy = gAgent.getBusy();
- BOOL is_muted = gMuteListp->isMuted(
+ BOOL is_muted = LLMuteList::getInstance()->isMuted(
from_id,
- name.c_str(),
+ name,
LLMute::flagTextChat);
- BOOL is_linden = gMuteListp->isLinden(
- name.c_str());
- char separator_string[3]=": "; /* Flawfinder: ignore */
- int message_offset=0;
-
- //Handle IRC styled /me messages.
- if (!strncmp(message.c_str(), "/me ", 4) ||
- !strncmp(message.c_str(), "/me'", 4))
- {
- strcpy(separator_string,""); /* Flawfinder: ignore */
- message_offset = 3;
- }
+ BOOL is_linden = LLMuteList::getInstance()->isLinden(name);
+ std::string separator_string(": ");
chat.mMuted = is_muted && !is_linden;
chat.mFromID = from_id;
@@ -1506,24 +3145,14 @@ public:
}
// standard message, not from system
- char saved[MAX_STRING]; /* Flawfinder: ignore */
- saved[0] = '\0';
+ std::string saved;
if(offline == IM_OFFLINE)
{
- char time_buf[TIME_STR_LENGTH]; /* Flawfinder: ignore */
- snprintf(saved, /* Flawfinder: ignore */
- MAX_STRING,
- "(Saved %s) ",
- formatted_time(timestamp, time_buf));
+ LLStringUtil::format_map_t args;
+ args["[LONG_TIMESTAMP]"] = formatted_time(timestamp);
+ saved = LLTrans::getString("Saved_message", args);
}
- snprintf(
- buffer,
- sizeof(buffer),
- "%s%s%s%s",
- name.c_str(),
- separator_string,
- saved,
- (message.c_str() + message_offset)); /*Flawfinder: ignore*/
+ std::string buffer = saved + message;
BOOL is_this_agent = FALSE;
if(from_id == gAgentID)
@@ -1533,24 +3162,14 @@ public:
gIMMgr->addMessage(
session_id,
from_id,
- name.c_str(),
+ name,
buffer,
- (char*)&bin_bucket[0],
+ std::string((char*)&bin_bucket[0]),
IM_SESSION_INVITE,
message_params["parent_estate_id"].asInteger(),
message_params["region_id"].asUUID(),
- ll_vector3_from_sd(message_params["position"]));
-
- snprintf(
- buffer,
- sizeof(buffer),
- "IM: %s%s%s%s",
- name.c_str(),
- separator_string,
- saved,
- (message.c_str()+message_offset)); /* Flawfinder: ignore */
- chat.mText = buffer;
- LLFloaterChat::addChat(chat, TRUE, is_this_agent);
+ ll_vector3_from_sd(message_params["position"]),
+ true);
//K now we want to accept the invitation
std::string url = gAgent.getRegion()->getCapability(
@@ -1576,7 +3195,7 @@ public:
return;
}
- if(!LLVoiceClient::voiceEnabled())
+ if(!LLVoiceClient::getInstance()->voiceEnabled() || !LLVoiceClient::getInstance()->isVoiceWorking())
{
// Don't display voice invites unless the user has voice enabled.
return;