diff options
author | Monroe Williams <monroe@lindenlab.com> | 2007-08-02 01:18:34 +0000 |
---|---|---|
committer | Monroe Williams <monroe@lindenlab.com> | 2007-08-02 01:18:34 +0000 |
commit | 7138fb673ac3df46b9cb5f23d0d74e70fdd2b6b3 (patch) | |
tree | 3c34a3a180b5275bd4166b0056765c5868f56447 /indra/newview/llimpanel.cpp | |
parent | f6a10b3214d79df4e8f5768acaa68edbd2de5620 (diff) |
Merge down from Branch_1-18-1:
svn merge --ignore-ancestry svn+ssh://svn.lindenlab.com/svn/linden/release@66449 svn+ssh://svn.lindenlab.com/svn/linden/branches/Branch_1-18-1@67131
Diffstat (limited to 'indra/newview/llimpanel.cpp')
-rw-r--r-- | indra/newview/llimpanel.cpp | 990 |
1 files changed, 933 insertions, 57 deletions
diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 015f90a180..3a873bc1a8 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -13,7 +13,6 @@ #include "indra_constants.h" #include "llfocusmgr.h" #include "llfontgl.h" -#include "llhttpclient.h" #include "llrect.h" #include "llerror.h" #include "llstring.h" @@ -23,12 +22,14 @@ #include "llagent.h" #include "llbutton.h" #include "llcallingcard.h" +#include "llchat.h" #include "llconsole.h" #include "llfloater.h" #include "llinventory.h" #include "llinventorymodel.h" #include "llinventoryview.h" #include "llfloateravatarinfo.h" +#include "llfloaterchat.h" #include "llkeyboard.h" #include "lllineeditor.h" #include "llresmgr.h" @@ -41,8 +42,13 @@ #include "llvieweruictrlfactory.h" #include "lllogchat.h" #include "llfloaterhtml.h" -#include "llviewerregion.h" #include "llweb.h" +#include "llhttpclient.h" +#include "llfloateractivespeakers.h" // LLSpeakerMgr +#include "llfloatergroupinfo.h" +#include "llsdutil.h" +#include "llnotify.h" +#include "llmutelist.h" // // Constants @@ -59,6 +65,10 @@ static LLString sTitleString = "Instant Message with [NAME]"; static LLString sTypingStartString = "[NAME]: ..."; static LLString sSessionStartString = "Starting session with [NAME] please wait."; +LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; +LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; +LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; + void session_starter_helper(const LLUUID& temp_session_id, const LLUUID& other_participant_id, EInstantMessage im_type) @@ -141,45 +151,669 @@ bool send_start_session_messages(const LLUUID& temp_session_id, return false; } -// Member Functions +class LLVoiceCallCapResponder : public LLHTTPClient::Responder +{ +public: + LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; + + virtual void error(U32 status, const std::string& reason); // called with bad status codes + virtual void result(const LLSD& content); + +private: + LLUUID mSessionID; +}; + + +void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) +{ + llwarns << "LLVoiceCallCapResponder::error(" + << status << ": " << reason << ")" + << llendl; + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if (channelp) + { + channelp->deactivate(); + } +} + +void LLVoiceCallCapResponder::result(const LLSD& content) +{ + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if (channelp) + { + //*TODO: DEBUG SPAM + LLSD::map_const_iterator iter; + for(iter = content.beginMap(); iter != content.endMap(); ++iter) + { + llinfos << "LLVoiceCallCapResponder::result got " + << iter->first << llendl; + } + + channelp->setChannelInfo( + content["voice_credentials"]["channel_uri"].asString(), + content["voice_credentials"]["channel_credentials"].asString()); + } +} + +// +// LLVoiceChannel +// +LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const LLString& session_name) : + mSessionID(session_id), + mState(STATE_NO_CHANNEL_INFO), + mSessionName(session_name), + mIgnoreNextSessionLeave(FALSE) +{ + mNotifyArgs["[VOICE_CHANNEL_NAME]"] = mSessionName; + + if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second) + { + // a voice channel already exists for this session id, so this instance will be orphaned + // the end result should simply be the failure to make voice calls + llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; + } + + LLVoiceClient::getInstance()->addStatusObserver(this); +} + +LLVoiceChannel::~LLVoiceChannel() +{ + // CANNOT do this here, since it will crash on quit in the LLVoiceChannelProximal singleton destructor. + // Do it in all other subclass destructors instead. + // deactivate(); + + // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed. + if(gVoiceClient) + { + gVoiceClient->removeStatusObserver(this); + } + + sVoiceChannelMap.erase(mSessionID); + sVoiceChannelURIMap.erase(mURI); +} + +void LLVoiceChannel::setChannelInfo( + const LLString& uri, + const LLString& credentials) +{ + setURI(uri); + + mCredentials = credentials; + + if (mState == STATE_NO_CHANNEL_INFO) + { + if(!mURI.empty() && !mCredentials.empty()) + { + setState(STATE_READY); + + // if we are supposed to be active, reconnect + // this will happen on initial connect, as we request credentials on first use + if (sCurrentVoiceChannel == this) + { + // just in case we got new channel info while active + // should move over to new channel + activate(); + } + } + else + { + //*TODO: notify user + llwarns << "Received invalid credentials for channel " << mSessionName << llendl; + deactivate(); + } + } +} + +void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) +{ + if (channelURI != mURI) + { + return; + } + + if (type < BEGIN_ERROR_STATUS) + { + handleStatusChange(type); + } + else + { + handleError(type); + } +} + +void LLVoiceChannel::handleStatusChange(EStatusType type) +{ + // status updates + switch(type) + { + case STATUS_LOGIN_RETRY: + mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle(); + break; + case STATUS_LOGGED_IN: + if (!mLoginNotificationHandle.isDead()) + { + LLNotifyBox* notifyp = (LLNotifyBox*)LLPanel::getPanelByHandle(mLoginNotificationHandle); + if (notifyp) + { + notifyp->close(); + } + mLoginNotificationHandle.markDead(); + } + break; + case STATUS_LEFT_CHANNEL: + if (callStarted() && !mIgnoreNextSessionLeave) + { + // if forceably removed from channel + // update the UI and revert to default channel + LLNotifyBox::showXml("VoiceChannelDisconnected", mNotifyArgs); + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + break; + case STATUS_JOINING: + if (callStarted()) + { + setState(STATE_RINGING); + } + break; + case STATUS_JOINED: + if (callStarted()) + { + setState(STATE_CONNECTED); + } + default: + break; + } +} + +// default behavior is to just deactivate channel +// derived classes provide specific error messages +void LLVoiceChannel::handleError(EStatusType type) +{ + deactivate(); + setState(STATE_ERROR); +} + +BOOL LLVoiceChannel::isActive() +{ + // only considered active when currently bound channel matches what our channel + return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; +} + +BOOL LLVoiceChannel::callStarted() +{ + return mState >= STATE_CALL_STARTED; +} + +void LLVoiceChannel::deactivate() +{ + if (mState >= STATE_RINGING) + { + // ignore session leave event + mIgnoreNextSessionLeave = TRUE; + } + + if (callStarted()) + { + setState(STATE_HUNG_UP); + } + if (sCurrentVoiceChannel == this) + { + // default channel is proximal channel + sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); + sCurrentVoiceChannel->activate(); + } +} + +void LLVoiceChannel::activate() +{ + if (callStarted()) + { + return; + } + + // deactivate old channel and mark ourselves as the active one + if (sCurrentVoiceChannel != this) + { + if (sCurrentVoiceChannel) + { + sCurrentVoiceChannel->deactivate(); + } + sCurrentVoiceChannel = this; + } + + if (mState == STATE_NO_CHANNEL_INFO) + { + // responsible for setting status to active + getChannelInfo(); + } + else + { + setState(STATE_CALL_STARTED); + } +} + +void LLVoiceChannel::getChannelInfo() +{ + // pretend we have everything we need + if (sCurrentVoiceChannel == this) + { + setState(STATE_CALL_STARTED); + } +} + +//static +LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) +{ + voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id); + if (found_it == sVoiceChannelMap.end()) + { + return NULL; + } + else + { + return found_it->second; + } +} + +//static +LLVoiceChannel* LLVoiceChannel::getChannelByURI(LLString uri) +{ + voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); + if (found_it == sVoiceChannelURIMap.end()) + { + return NULL; + } + else + { + return found_it->second; + } +} + + +void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) +{ + sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); + mSessionID = new_session_id; + sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); +} + +void LLVoiceChannel::setURI(LLString uri) +{ + sVoiceChannelURIMap.erase(mURI); + mURI = uri; + sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); +} + +void LLVoiceChannel::setState(EState state) +{ + switch(state) + { + case STATE_RINGING: + gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); + break; + case STATE_CONNECTED: + gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs); + break; + case STATE_HUNG_UP: + gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs); + break; + default: + break; + } + + mState = state; +} + + +//static +void LLVoiceChannel::initClass() +{ + sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); +} + +// +// LLVoiceChannelGroup +// + +LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name) : + LLVoiceChannel(session_id, session_name) +{ +} + +LLVoiceChannelGroup::~LLVoiceChannelGroup() +{ + deactivate(); +} + +void LLVoiceChannelGroup::deactivate() +{ + if (callStarted()) + { + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + } + LLVoiceChannel::deactivate(); +} + +void LLVoiceChannelGroup::activate() +{ + if (callStarted()) return; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // we have the channel info, just need to use it now + LLVoiceClient::getInstance()->setNonSpatialChannel( + mURI, + mCredentials); + } +} + +void LLVoiceChannelGroup::getChannelInfo() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability("ChatSessionRequest"); + LLSD data; + data["method"] = "call"; + data["session-id"] = mSessionID; + LLHTTPClient::post(url, + data, + new LLVoiceCallCapResponder(mSessionID)); + } +} + +void LLVoiceChannelGroup::handleError(EStatusType status) +{ + std::string notify; + switch(status) + { + case ERROR_CHANNEL_LOCKED: + case ERROR_CHANNEL_FULL: + notify = "VoiceChannelFull"; + break; + case ERROR_UNKNOWN: + break; + default: + break; + } + + // notification + if (!notify.empty()) + { + LLNotifyBox::showXml(notify, mNotifyArgs); + // echo to im window + gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, LLNotifyBox::getTemplateMessage(notify, mNotifyArgs).c_str()); + } + + LLVoiceChannel::handleError(status); +} + +// +// LLVoiceChannelProximal +// +LLVoiceChannelProximal::LLVoiceChannelProximal() : + LLVoiceChannel(LLUUID::null, LLString::null) +{ + activate(); +} + +LLVoiceChannelProximal::~LLVoiceChannelProximal() +{ + // DO NOT call deactivate() here, since this will only happen at atexit() time. +} + +BOOL LLVoiceChannelProximal::isActive() +{ + return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); +} + +void LLVoiceChannelProximal::activate() +{ + if (callStarted()) return; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // this implicitly puts you back in the spatial channel + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + } +} + +void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) +{ + if (!proximal) + { + return; + } + + if (type < BEGIN_ERROR_STATUS) + { + handleStatusChange(type); + } + else + { + handleError(type); + } +} + +void LLVoiceChannelProximal::handleStatusChange(EStatusType status) +{ + // status updates + switch(status) + { + case STATUS_LEFT_CHANNEL: + // do not notify user when leaving proximal channel + return; + default: + break; + } + LLVoiceChannel::handleStatusChange(status); +} + + +void LLVoiceChannelProximal::handleError(EStatusType status) +{ + std::string notify; + switch(status) + { + case ERROR_CHANNEL_LOCKED: + case ERROR_CHANNEL_FULL: + notify = "ProximalVoiceChannelFull"; + break; + default: + break; + } + + // notification + if (!notify.empty()) + { + LLNotifyBox::showXml(notify, mNotifyArgs); + } + + LLVoiceChannel::handleError(status); +} + +void LLVoiceChannelProximal::deactivate() +{ + if (callStarted()) + { + setState(STATE_HUNG_UP); + } +} + // +// LLVoiceChannelP2P +// +LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id) : + LLVoiceChannelGroup(session_id, session_name), + mOtherUserID(other_user_id) +{ + // make sure URI reflects encoded version of other user's agent id + setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); +} + +LLVoiceChannelP2P::~LLVoiceChannelP2P() +{ + deactivate(); +} + +void LLVoiceChannelP2P::handleStatusChange(EStatusType type) +{ + // status updates + switch(type) + { + case STATUS_LEFT_CHANNEL: + if (callStarted() && !mIgnoreNextSessionLeave) + { + if (mState == STATE_RINGING) + { + // other user declined call + LLNotifyBox::showXml("P2PCallDeclined", mNotifyArgs); + } + else + { + // other user hung up + LLNotifyBox::showXml("VoiceChannelDisconnectedP2P", mNotifyArgs); + } + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + return; + default: + break; + } -LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, - const LLRect& rect, - const std::string& session_label, - const LLUUID& session_id, - const LLUUID& other_participant_id, - EInstantMessage dialog) : + LLVoiceChannelGroup::handleStatusChange(type); +} + +void LLVoiceChannelP2P::handleError(EStatusType type) +{ + switch(type) + { + case ERROR_NOT_AVAILABLE: + LLNotifyBox::showXml("P2PCallNoAnswer", mNotifyArgs); + break; + default: + break; + } + + LLVoiceChannelGroup::handleError(type); +} + +void LLVoiceChannelP2P::activate() +{ + if (callStarted()) return; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // no session handle yet, we're starting the call + if (mSessionHandle.empty()) + { + LLVoiceClient::getInstance()->callUser(mOtherUserID); + } + // otherwise answering the call + else + { + LLVoiceClient::getInstance()->answerInvite(mSessionHandle, mOtherUserID); + // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. + mSessionHandle.clear(); + } + } +} + +void LLVoiceChannelP2P::getChannelInfo() +{ + // pretend we have everything we need, since P2P doesn't use channel info + if (sCurrentVoiceChannel == this) + { + setState(STATE_CALL_STARTED); + } +} + +// receiving session from other user who initiated call +void LLVoiceChannelP2P::setSessionHandle(const LLString& handle) +{ + BOOL needs_activate = FALSE; + if (callStarted()) + { + // defer to lower agent id when already active + if (mOtherUserID < gAgent.getID()) + { + // pretend we haven't started the call yet, so we can connect to this session instead + deactivate(); + needs_activate = TRUE; + } + else + { + // we are active and have priority, invite the other user again + // under the assumption they will join this new session + mSessionHandle.clear(); + LLVoiceClient::getInstance()->callUser(mOtherUserID); + return; + } + } + + mSessionHandle = handle; + // The URI of a p2p session should always be the other end's SIP URI. + setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); + + if (needs_activate) + { + activate(); + } +} + +// +// LLFloaterIMPanel +// +LLFloaterIMPanel::LLFloaterIMPanel( + const std::string& name, + const LLRect& rect, + const std::string& session_label, + const LLUUID& session_id, + const LLUUID& other_participant_id, + EInstantMessage dialog) : LLFloater(name, rect, session_label), mInputEditor(NULL), mHistoryEditor(NULL), mSessionUUID(session_id), - mSessionInitRequested(FALSE), + mVoiceChannel(NULL), mSessionInitialized(FALSE), + mOtherParticipantUUID(other_participant_id), mDialog(dialog), mTyping(FALSE), mOtherTyping(FALSE), mTypingLineStartIndex(0), mSentTypingState(TRUE), + mShowSpeakersOnConnect(TRUE), + mAutoConnect(FALSE), + mSpeakerPanel(NULL), mFirstKeystrokeTimer(), mLastKeystrokeTimer() { init(session_label); } -LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, - const LLRect& rect, - const std::string& session_label, - const LLUUID& session_id, - const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, - EInstantMessage dialog) : +LLFloaterIMPanel::LLFloaterIMPanel( + const std::string& name, + const LLRect& rect, + const std::string& session_label, + const LLUUID& session_id, + const LLUUID& other_participant_id, + const LLDynamicArray<LLUUID>& ids, + EInstantMessage dialog) : LLFloater(name, rect, session_label), mInputEditor(NULL), mHistoryEditor(NULL), mSessionUUID(session_id), - mSessionInitRequested(FALSE), + mVoiceChannel(NULL), mSessionInitialized(FALSE), mOtherParticipantUUID(other_participant_id), mDialog(dialog), @@ -187,6 +821,10 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, mOtherTyping(FALSE), mTypingLineStartIndex(0), mSentTypingState(TRUE), + mShowSpeakersOnConnect(TRUE), + mAutoConnect(FALSE), + mSpeakers(NULL), + mSpeakerPanel(NULL), mFirstKeystrokeTimer(), mLastKeystrokeTimer() { @@ -197,11 +835,53 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, void LLFloaterIMPanel::init(const LLString& session_label) { + LLString xml_filename; + switch(mDialog) + { + case IM_SESSION_GROUP_START: + mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); + xml_filename = "floater_instant_message_group.xml"; + mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); + break; + case IM_SESSION_INVITE: + mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); + if (gAgent.isInGroup(mSessionUUID)) + { + xml_filename = "floater_instant_message_group.xml"; + } + else // must be invite to ad hoc IM + { + xml_filename = "floater_instant_message_ad_hoc.xml"; + } + mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); + break; + case IM_SESSION_P2P_INVITE: + xml_filename = "floater_instant_message.xml"; + mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID); + break; + case IM_SESSION_CONFERENCE_START: + mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); + xml_filename = "floater_instant_message_ad_hoc.xml"; + mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); + break; + // just received text from another user + case IM_NOTHING_SPECIAL: + xml_filename = "floater_instant_message.xml"; + mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID); + break; + default: + llwarns << "Unknown session type" << llendl; + xml_filename = "floater_instant_message.xml"; + break; + } + + mSpeakers = new LLIMSpeakerMgr(mVoiceChannel); + gUICtrlFactory->buildFloater(this, - "floater_instant_message.xml", - NULL, - FALSE); - + xml_filename, + &getFactoryMap(), + FALSE); + setLabel(session_label); setTitle(session_label); mInputEditor->setMaxTextLength(1023); @@ -238,18 +918,30 @@ void LLFloaterIMPanel::init(const LLString& session_label) addHistoryLine( session_start, - LLColor4::grey, + gSavedSettings.getColor4("SystemChatColor"), false); } } } +LLFloaterIMPanel::~LLFloaterIMPanel() +{ + delete mSpeakers; + mSpeakers = NULL; + + //kicks you out of the voice channel if it is currently active + + // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point). + mVoiceChannel->deactivate(); + + delete mVoiceChannel; + mVoiceChannel = NULL; +} + BOOL LLFloaterIMPanel::postBuild() { requires("chat_editor", WIDGET_TYPE_LINE_EDITOR); - requires("profile_btn", WIDGET_TYPE_BUTTON); - requires("close_btn", WIDGET_TYPE_BUTTON); requires("im_history", WIDGET_TYPE_TEXT_EDITOR); requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX); requires("title_string", WIDGET_TYPE_TEXT_BOX); @@ -262,22 +954,28 @@ BOOL LLFloaterIMPanel::postBuild() mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived ); mInputEditor->setFocusLostCallback( onInputEditorFocusLost ); mInputEditor->setKeystrokeCallback( onInputEditorKeystroke ); + mInputEditor->setCommitCallback( onCommitChat ); mInputEditor->setCallbackUserData(this); mInputEditor->setCommitOnFocusLost( FALSE ); mInputEditor->setRevertOnEsc( FALSE ); - LLButton* profile_btn = LLUICtrlFactory::getButtonByName(this, "profile_btn"); - profile_btn->setClickedCallback(&LLFloaterIMPanel::onClickProfile, this); + childSetAction("profile_callee_btn", onClickProfile, this); + childSetAction("group_info_btn", onClickGroupInfo, this); - LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn"); - close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); + childSetAction("start_call_btn", onClickStartCall, this); + childSetAction("end_call_btn", onClickEndCall, this); + childSetAction("send_btn", onClickSend, this); + childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this); + + //LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn"); + //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); mHistoryEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "im_history"); mHistoryEditor->setParseHTML(TRUE); - if (IM_SESSION_GROUP_START == mDialog) + if ( IM_SESSION_GROUP_START == mDialog ) { - profile_btn->setEnabled(FALSE); + childSetEnabled("profile_btn", FALSE); } LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string"); sTitleString = title->getText(); @@ -290,16 +988,91 @@ BOOL LLFloaterIMPanel::postBuild() this, "session_start_string"); sSessionStartString = session_start->getText(); + if (mSpeakerPanel) + { + mSpeakerPanel->refreshSpeakers(); + } + if (mDialog == IM_NOTHING_SPECIAL) + { + childSetCommitCallback("mute_btn", onClickMuteVoice, this); + childSetCommitCallback("speaker_volume", onVolumeChange, this); + } + + setDefaultBtn("send_btn"); return TRUE; } return FALSE; } +void* LLFloaterIMPanel::createSpeakersPanel(void* data) +{ + LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)data; + floaterp->mSpeakerPanel = new LLPanelActiveSpeakers(floaterp->mSpeakers, TRUE); + return floaterp->mSpeakerPanel; +} + +//static +void LLFloaterIMPanel::onClickMuteVoice(LLUICtrl* source, void* user_data) +{ + LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; + if (floaterp) + { + BOOL is_muted = gMuteListp->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat); + + LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT); + if (!is_muted) + { + gMuteListp->add(mute, LLMute::flagVoiceChat); + } + else + { + gMuteListp->remove(mute, LLMute::flagVoiceChat); + } + } +} + +//static +void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data) +{ + LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; + if (floaterp) + { + gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); + } +} + + // virtual void LLFloaterIMPanel::draw() -{ +{ + LLViewerRegion* region = gAgent.getRegion(); + + BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "") + && mSessionInitialized + && LLVoiceClient::voiceEnabled(); + + // hide/show start call and end call buttons + childSetVisible("end_call_btn", mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); + childSetVisible("start_call_btn", mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); + childSetEnabled("start_call_btn", enable_connect); + childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty()); + + if (mAutoConnect && enable_connect) + { + onClickStartCall(this); + mAutoConnect = FALSE; + } + + // show speakers window when voice first connects + if (mShowSpeakersOnConnect && mVoiceChannel->isActive()) + { + childSetVisible("active_speakers_panel", TRUE); + mShowSpeakersOnConnect = FALSE; + } + childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel")); + if (mTyping) { // Time out if user hasn't typed for a while. @@ -318,6 +1091,19 @@ void LLFloaterIMPanel::draw() } } + if (mSpeakerPanel) + { + mSpeakerPanel->refreshSpeakers(); + } + else + { + // refresh volume and mute checkbox + childSetEnabled("speaker_volume", mVoiceChannel->isActive()); + childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID)); + + childSetValue("mute_btn", gMuteListp->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)); + childSetEnabled("mute_btn", mVoiceChannel->isActive()); + } LLFloater::draw(); } @@ -342,16 +1128,22 @@ private: BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) { + LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + return FALSE; + } + S32 count = ids.count(); - if( isAddAllowed() && (count > 0) ) + if( isInviteAllowed() && (count > 0) ) { - llinfos << "LLFloaterIMPanel::inviteToSession() - adding participants" << llendl; + llinfos << "LLFloaterIMPanel::inviteToSession() - inviting participants" << llendl; - std::string url = - gAgent.getRegion()->getCapability("ChatSessionRequest"); + std::string url = region->getCapability("ChatSessionRequest"); LLSD data; + data["params"] = LLSD::emptyArray(); for (int i = 0; i < count; i++) { @@ -378,6 +1170,13 @@ BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) return TRUE; } +void LLFloaterIMPanel::addHistoryLine(const LLUUID& source, const std::string &utf8msg, const LLColor4& color, bool log_to_file) +{ + addHistoryLine(utf8msg, color, log_to_file); + mSpeakers->speakerChatted(source); + mSpeakers->setSpeakerTyping(source, FALSE); +} + void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file) { LLMultiFloater* hostp = getHost(); @@ -391,7 +1190,7 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4 // Now we're adding the actual line of text, so erase the // "Foo is typing..." text segment, and the optional timestamp // if it was present. JC - removeTypingIndicator(); + removeTypingIndicator(NULL); // Actually add the line LLString timestring; @@ -470,7 +1269,7 @@ BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_paren // but not shift-return or control-return if ( !gSavedSettings.getBOOL("PinTalkViewOpen") && !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT) ) { - gIMView->toggle(NULL); + gIMMgr->toggle(NULL); } } else if ( KEY_ESCAPE == key ) @@ -481,7 +1280,7 @@ BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_paren // Close talk panel with escape if( !gSavedSettings.getBOOL("PinTalkViewOpen") ) { - gIMView->toggle(NULL); + gIMMgr->toggle(NULL); } } } @@ -522,7 +1321,7 @@ BOOL LLFloaterIMPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop) { - BOOL rv = isAddAllowed(); + BOOL rv = isInviteAllowed(); if(rv && item && item->getCreatorUUID().notNull()) { if(drop) @@ -542,7 +1341,7 @@ BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop) BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop) { - BOOL rv = isAddAllowed(); + BOOL rv = isInviteAllowed(); if(rv && category) { LLInventoryModel::cat_array_t cats; @@ -571,11 +1370,11 @@ BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop) return rv; } -BOOL LLFloaterIMPanel::isAddAllowed() const +BOOL LLFloaterIMPanel::isInviteAllowed() const { - return ((IM_SESSION_CONFERENCE_START == mDialog) - || (IM_SESSION_INVITE) ); + return ( (IM_SESSION_CONFERENCE_START == mDialog) + || (IM_SESSION_INVITE == mDialog) ); } @@ -600,6 +1399,15 @@ void LLFloaterIMPanel::onClickProfile( void* userdata ) } // static +void LLFloaterIMPanel::onClickGroupInfo( void* userdata ) +{ + // Bring up the Profile window + LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; + + LLFloaterGroupInfo::showFromUUID(self->mSessionUUID); +} + +// static void LLFloaterIMPanel::onClickClose( void* userdata ) { LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; @@ -610,6 +1418,44 @@ void LLFloaterIMPanel::onClickClose( void* userdata ) } // static +void LLFloaterIMPanel::onClickStartCall(void* userdata) +{ + LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; + + self->mVoiceChannel->activate(); +} + +// static +void LLFloaterIMPanel::onClickEndCall(void* userdata) +{ + LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; + + self->getVoiceChannel()->deactivate(); +} + +// static +void LLFloaterIMPanel::onClickSend(void* userdata) +{ + LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata; + self->sendMsg(); +} + +// static +void LLFloaterIMPanel::onClickToggleActiveSpeakers(void* userdata) +{ + LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata; + + self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel")); +} + +// static +void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata) +{ + LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata; + self->sendMsg(); +} + +// static void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata ) { LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata; @@ -660,7 +1506,7 @@ void LLFloaterIMPanel::onClose(bool app_quitting) mSessionUUID); gAgent.sendReliableMessage(); } - gIMView->removeSession(mSessionUUID); + gIMMgr->removeSession(mSessionUUID); destroy(); } @@ -748,10 +1594,9 @@ void LLFloaterIMPanel::sendMsg() } history_echo += utf8_text; - BOOL other_was_typing = mOtherTyping; - addHistoryLine(history_echo); + addHistoryLine(gAgent.getID(), history_echo); if (other_was_typing) { @@ -762,6 +1607,8 @@ void LLFloaterIMPanel::sendMsg() } else { + //queue up the message to send once the session is + //initialized mQueuedMsgsForInit.append(utf8_text); } @@ -775,15 +1622,31 @@ void LLFloaterIMPanel::sendMsg() mSentTypingState = TRUE; } +void LLFloaterIMPanel::updateSpeakersList(LLSD speaker_updates) +{ + mSpeakers->processSpeakerListUpdate(speaker_updates); +} + +void LLFloaterIMPanel::setSpeakersListFromMap(LLSD speaker_map) +{ + mSpeakers->processSpeakerMap(speaker_map); +} + +void LLFloaterIMPanel::setSpeakersList(LLSD speaker_list) +{ + mSpeakers->processSpeakerList(speaker_list); +} + void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) { mSessionUUID = session_id; + mVoiceChannel->updateSessionID(session_id); mSessionInitialized = TRUE; //we assume the history editor hasn't moved at all since //we added the starting session message //so, we count how many characters to remove - S32 chars_to_remove = mHistoryEditor->getText().length() - + S32 chars_to_remove = mHistoryEditor->getText().length() - mSessionStartMsgPos; mHistoryEditor->removeTextFromEnd(chars_to_remove); @@ -793,13 +1656,18 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) iter != mQueuedMsgsForInit.endArray(); ++iter) { - deliver_message(iter->asString(), - mSessionUUID, - mOtherParticipantUUID, - mDialog); + deliver_message( + iter->asString(), + mSessionUUID, + mOtherParticipantUUID, + mDialog); } } +void LLFloaterIMPanel::requestAutoConnect() +{ + mAutoConnect = TRUE; +} void LLFloaterIMPanel::setTyping(BOOL typing) { @@ -816,6 +1684,8 @@ void LLFloaterIMPanel::setTyping(BOOL typing) // Will send typing state after a short delay. mSentTypingState = FALSE; } + + mSpeakers->setSpeakerTyping(gAgent.getID(), TRUE); } else { @@ -825,6 +1695,7 @@ void LLFloaterIMPanel::setTyping(BOOL typing) sendTypingState(FALSE); mSentTypingState = TRUE; } + mSpeakers->setSpeakerTyping(gAgent.getID(), FALSE); } mTyping = typing; @@ -853,7 +1724,6 @@ void LLFloaterIMPanel::sendTypingState(BOOL typing) gAgent.sendReliableMessage(); } - void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing) { if (typing) @@ -864,7 +1734,7 @@ void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing) else { // other user stopped typing - removeTypingIndicator(); + removeTypingIndicator(im_info); } } @@ -877,14 +1747,17 @@ void LLFloaterIMPanel::addTypingIndicator(const std::string &name) mTypingLineStartIndex = mHistoryEditor->getText().length(); LLUIString typing_start = sTypingStartString; typing_start.setArg("[NAME]", name); - addHistoryLine(typing_start, LLColor4::grey, false); + addHistoryLine(typing_start, gSavedSettings.getColor4("SystemChatColor"), false); mOtherTypingName = name; mOtherTyping = TRUE; } + // MBW -- XXX -- merge from release broke this (argument to this function changed from an LLIMInfo to a name) + // Richard will fix. +// mSpeakers->setSpeakerTyping(im_info->mFromID, TRUE); } -void LLFloaterIMPanel::removeTypingIndicator() +void LLFloaterIMPanel::removeTypingIndicator(const LLIMInfo* im_info) { if (mOtherTyping) { @@ -893,6 +1766,10 @@ void LLFloaterIMPanel::removeTypingIndicator() S32 chars_to_remove = mHistoryEditor->getText().length() - mTypingLineStartIndex; mHistoryEditor->removeTextFromEnd(chars_to_remove); + if (im_info) + { + mSpeakers->setSpeakerTyping(im_info->mFromID, FALSE); + } } } @@ -905,4 +1782,3 @@ void LLFloaterIMPanel::chatFromLogFile(LLString line, void* userdata) self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey); } - |