summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorMonroe Williams <monroe@lindenlab.com>2007-08-02 01:18:34 +0000
committerMonroe Williams <monroe@lindenlab.com>2007-08-02 01:18:34 +0000
commit7138fb673ac3df46b9cb5f23d0d74e70fdd2b6b3 (patch)
tree3c34a3a180b5275bd4166b0056765c5868f56447 /indra/newview
parentf6a10b3214d79df4e8f5768acaa68edbd2de5620 (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')
-rw-r--r--indra/newview/English.lproj/InfoPlist.strings4
-rw-r--r--indra/newview/Info-SecondLife.plist2
-rw-r--r--indra/newview/llagent.cpp60
-rw-r--r--indra/newview/llagent.h2
-rw-r--r--indra/newview/llaudiosourcevo.cpp2
-rw-r--r--indra/newview/llcallingcard.cpp1
-rw-r--r--indra/newview/llchatbar.cpp155
-rw-r--r--indra/newview/llchatbar.h21
-rw-r--r--indra/newview/llcolorswatch.cpp15
-rw-r--r--indra/newview/llcolorswatch.h2
-rw-r--r--indra/newview/lldebugmessagebox.cpp10
-rw-r--r--indra/newview/lldebugview.cpp14
-rw-r--r--indra/newview/llemote.h2
-rw-r--r--indra/newview/llfasttimerview.cpp3
-rw-r--r--indra/newview/llfirstuse.cpp13
-rw-r--r--indra/newview/llfirstuse.h1
-rw-r--r--indra/newview/llfloateranimpreview.cpp2
-rw-r--r--indra/newview/llfloateravatarpicker.cpp7
-rw-r--r--indra/newview/llfloaterchat.cpp213
-rw-r--r--indra/newview/llfloaterchat.h25
-rw-r--r--indra/newview/llfloaterchatterbox.cpp322
-rw-r--r--indra/newview/llfloaterchatterbox.h67
-rw-r--r--indra/newview/llfloaterfriends.cpp397
-rw-r--r--indra/newview/llfloaterfriends.h26
-rw-r--r--indra/newview/llfloatergroups.cpp477
-rw-r--r--indra/newview/llfloatergroups.h79
-rw-r--r--indra/newview/llfloaterland.cpp69
-rw-r--r--indra/newview/llfloaterland.h2
-rw-r--r--indra/newview/llfloaternamedesc.cpp4
-rw-r--r--indra/newview/llfloaterpostcard.cpp2
-rw-r--r--indra/newview/llfloaterpreference.cpp10
-rw-r--r--indra/newview/llfloaterpreference.h2
-rw-r--r--indra/newview/llfloaterregioninfo.cpp26
-rw-r--r--indra/newview/llfloaterscriptdebug.cpp2
-rw-r--r--indra/newview/llfloatersnapshot.cpp3
-rw-r--r--indra/newview/llfloatertools.cpp11
-rw-r--r--indra/newview/llfloatertools.h2
-rw-r--r--indra/newview/llgesturemgr.cpp63
-rw-r--r--indra/newview/llgesturemgr.h2
-rw-r--r--indra/newview/llhudobject.cpp4
-rw-r--r--indra/newview/llhudobject.h3
-rw-r--r--indra/newview/llhudtext.cpp3
-rw-r--r--indra/newview/llimpanel.cpp990
-rw-r--r--indra/newview/llimpanel.h150
-rw-r--r--indra/newview/llimview.cpp822
-rw-r--r--indra/newview/llimview.h57
-rw-r--r--indra/newview/llinventorybridge.cpp9
-rw-r--r--indra/newview/llinventorymodel.cpp9
-rw-r--r--indra/newview/llmutelist.cpp124
-rw-r--r--indra/newview/llmutelist.h32
-rw-r--r--indra/newview/llnetmap.cpp2
-rw-r--r--indra/newview/lloverlaybar.cpp457
-rw-r--r--indra/newview/lloverlaybar.h45
-rw-r--r--indra/newview/llpanelavatar.cpp9
-rw-r--r--indra/newview/llpanelgroup.cpp11
-rw-r--r--indra/newview/llpanellogin.cpp32
-rw-r--r--indra/newview/llpanelpermissions.cpp16
-rw-r--r--indra/newview/llpreview.cpp3
-rw-r--r--indra/newview/llpreviewscript.cpp5
-rw-r--r--indra/newview/llpreviewsound.cpp15
-rw-r--r--indra/newview/llpreviewtexture.cpp2
-rw-r--r--indra/newview/llstartup.cpp30
-rw-r--r--indra/newview/llstatusbar.cpp110
-rw-r--r--indra/newview/llstatusbar.h23
-rw-r--r--indra/newview/lltoolbar.cpp35
-rw-r--r--indra/newview/lltoolbar.h3
-rw-r--r--indra/newview/lltooldraganddrop.cpp2
-rw-r--r--indra/newview/llviewercontrol.h2
-rw-r--r--indra/newview/llviewermenu.cpp43
-rw-r--r--indra/newview/llviewermessage.cpp71
-rw-r--r--indra/newview/llviewerobject.cpp23
-rw-r--r--indra/newview/llviewerobject.h5
-rw-r--r--indra/newview/llviewerparcelmgr.cpp16
-rw-r--r--indra/newview/llviewerregion.cpp3
-rw-r--r--indra/newview/llviewertexteditor.cpp17
-rw-r--r--indra/newview/llviewerwindow.cpp69
-rw-r--r--indra/newview/llviewerwindow.h2
-rw-r--r--indra/newview/llvoavatar.cpp313
-rw-r--r--indra/newview/llvoavatar.h16
-rw-r--r--indra/newview/llvoiceclient.cpp4056
-rw-r--r--indra/newview/llvoiceclient.h503
-rw-r--r--indra/newview/llvoicevisualizer.cpp439
-rw-r--r--indra/newview/llvoicevisualizer.h119
-rw-r--r--indra/newview/llvovolume.cpp4
-rw-r--r--indra/newview/llworldmapview.cpp3
-rwxr-xr-xindra/newview/viewer_manifest.py185
86 files changed, 9040 insertions, 1972 deletions
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
index 5991f9150f..aa9b6c054a 100644
--- a/indra/newview/English.lproj/InfoPlist.strings
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -1,5 +1,5 @@
/* Localized versions of Info.plist keys */
CFBundleName = "Second Life";
-CFBundleShortVersionString = "Second Life version 1.18.1.1";
-CFBundleGetInfoString = "Second Life version 1.18.1.1, Copyright 2004-2007 Linden Research, Inc.";
+CFBundleShortVersionString = "Second Life version 1.18.1.2";
+CFBundleGetInfoString = "Second Life version 1.18.1.2, Copyright 2004-2007 Linden Research, Inc.";
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index 9bef82e0bc..91d1849688 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
- <string>1.18.1.1</string>
+ <string>1.18.1.2</string>
<key>CSResourcesFileMapped</key>
<true/>
</dict>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 45f0b448e2..7ed11c1154 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -41,6 +41,7 @@
#include "llface.h"
#include "llfirstuse.h"
#include "llfloater.h"
+#include "llfloateractivespeakers.h"
#include "llfloateravatarinfo.h"
#include "llfloaterbuildoptions.h"
#include "llfloaterchat.h"
@@ -97,6 +98,7 @@
#include "pipeline.h"
#include "roles_constants.h"
#include "viewer.h"
+#include "llvoiceclient.h"
// Ventrella
#include "llfollowcam.h"
@@ -326,7 +328,7 @@ LLAgent::LLAgent()
mEffectColor(0.f, 1.f, 1.f, 1.f),
mHaveHomePosition(FALSE),
mHomeRegionHandle( 0 ),
- mNearChatRadius(10.f),
+ mNearChatRadius(CHAT_NORMAL_RADIUS / 2.f),
mGodLevel( GOD_NOT ),
@@ -2824,7 +2826,7 @@ void LLAgent::endAnimationUpdateUI()
gMorphView->setVisible(FALSE);
}
- gIMView->setFloaterOpen( FALSE );
+ gIMMgr->setFloaterOpen( FALSE );
gConsole->setVisible( TRUE );
if (mAvatarObject)
@@ -3239,6 +3241,19 @@ void LLAgent::updateCamera()
setLookAt(LOOKAT_TARGET_FOCUS, NULL, mCameraPositionAgent);
}
+ // Send the camera position to the spatialized voice system.
+ if(gVoiceClient && getRegion())
+ {
+ LLMatrix3 rot;
+ rot.setRows(gCamera->getAtAxis(), gCamera->getLeftAxis (), gCamera->getUpAxis());
+
+ // MBW -- XXX -- Setting velocity to 0 for now. May figure it out later...
+ gVoiceClient->setCameraPosition(
+ getRegion()->getPosGlobalFromRegion(gCamera->getOrigin()),// position
+ LLVector3::zero, // velocity
+ rot); // rotation matrix
+ }
+
// update the travel distance stat
// this isn't directly related to the camera
// but this seemed like the best place to do this
@@ -3249,7 +3264,7 @@ void LLAgent::updateCamera()
mDistanceTraveled += delta.magVec();
}
mLastPositionGlobal = global_pos;
-
+
if (LLVOAvatar::sVisibleInFirstPerson && mAvatarObject.notNull() && !mAvatarObject->mIsSitting && cameraMouselook())
{
LLVector3 head_pos = mAvatarObject->mHeadp->getWorldPosition() +
@@ -4445,18 +4460,23 @@ void LLAgent::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate)
//-----------------------------------------------------------------------------
// heardChat()
//-----------------------------------------------------------------------------
-void LLAgent::heardChat(const LLChat& chat)
+void LLAgent::heardChat(const LLUUID& id)
{
- if (chat.mChatType == CHAT_TYPE_START
- || chat.mChatType == CHAT_TYPE_STOP)
+ // log text and voice chat to speaker mgr
+ // for keeping track of active speakers, etc.
+ gLocalSpeakerMgr->speakerChatted(id);
+
+ // don't respond to your own voice
+ if (id == getID()) return;
+
+ if (ll_rand(2) == 0)
{
- return;
- }
+ LLViewerObject *chatter = gObjectList.findObject(mLastChatterID);
+ setLookAt(LOOKAT_TARGET_AUTO_LISTEN, chatter, LLVector3::zero);
+ }
- mLastChatterID = chat.mFromID;
+ mLastChatterID = id;
mChatTimer.reset();
-
- mNearChatRadius = CHAT_NORMAL_RADIUS / 2.f;
}
//-----------------------------------------------------------------------------
@@ -5048,14 +5068,6 @@ void LLAgent::initOriginGlobal(const LLVector3d &origin_global)
void update_group_floaters(const LLUUID& group_id)
{
- // *HACK: added to do a live update of the groups floater if it is
- // open.
- LLFloaterGroups* fg = LLFloaterGroups::getInstance(gAgent.getID());
- if(fg)
- {
- fg->reset();
- }
-
LLFloaterGroupInfo::refreshGroup(group_id);
// update avatar info
@@ -5065,10 +5077,10 @@ void update_group_floaters(const LLUUID& group_id)
fa->resetGroupList();
}
- if (gIMView)
+ if (gIMMgr)
{
// update the talk view
- gIMView->refresh();
+ gIMMgr->refresh();
}
}
@@ -5159,6 +5171,7 @@ void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **)
if (need_floater_update)
{
update_group_floaters(group.mID);
+ gAgent.fireEvent(new LLEvent(&gAgent, "new group"), "");
}
}
@@ -5488,6 +5501,11 @@ bool LLAgent::teleportCore(bool is_local)
gAgent.setTeleportState( LLAgent::TELEPORT_START );
}
make_ui_sound("UISndTeleportOut");
+
+ // MBW -- Let the voice client know a teleport has begun so it can leave the existing channel.
+ // This was breaking the case of teleporting within a single sim. Backing it out for now.
+// gVoiceClient->leaveChannel();
+
return true;
}
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index c6d11b5ae5..438239e717 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -170,7 +170,7 @@ public:
void setObjectTracking(BOOL track) { mTrackFocusObject = track; }
// void setLookingAtAvatar(BOOL looking);
- void heardChat(const LLChat& chat);
+ void heardChat(const LLUUID& id);
void lookAtLastChat();
LLUUID getLastChatter() { return mLastChatterID; }
F32 getTypingTime() { return mTypingTimer.getElapsedTimeF32(); }
diff --git a/indra/newview/llaudiosourcevo.cpp b/indra/newview/llaudiosourcevo.cpp
index 86cc1e206e..9d83e5d80e 100644
--- a/indra/newview/llaudiosourcevo.cpp
+++ b/indra/newview/llaudiosourcevo.cpp
@@ -63,7 +63,7 @@ void LLAudioSourceVO::updateGain()
{
mute = TRUE;
}
- else if (gMuteListp->isMuted(mOwnerID))
+ else if (gMuteListp->isMuted(mOwnerID, LLMute::flagObjectSounds))
{
mute = TRUE;
}
diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
index e93ce8bdff..37bdbbfee5 100644
--- a/indra/newview/llcallingcard.cpp
+++ b/indra/newview/llcallingcard.cpp
@@ -274,6 +274,7 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds)
<< "]" << llendl;
}
}
+ notifyObservers();
return new_buddy_count;
}
diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
index 1a65903dcd..f5f0691dc6 100644
--- a/indra/newview/llchatbar.cpp
+++ b/indra/newview/llchatbar.cpp
@@ -27,6 +27,7 @@
#include "llgesturemgr.h"
#include "llkeyboard.h"
#include "lllineeditor.h"
+#include "llstatusbar.h"
#include "lltextbox.h"
#include "lluiconstants.h"
#include "llviewergesture.h" // for triggering gestures
@@ -50,15 +51,18 @@ const F32 AGENT_TYPING_TIMEOUT = 5.f; // seconds
LLChatBar *gChatBar = NULL;
-LLChatBarGestureObserver* LLChatBar::sObserver = NULL;
+// legacy calllback glue
+void toggleChatHistory(void* user_data);
class LLChatBarGestureObserver : public LLGestureManagerObserver
{
public:
- LLChatBarGestureObserver() {}
+ LLChatBarGestureObserver(LLChatBar* chat_barp) : mChatBar(chat_barp){}
virtual ~LLChatBarGestureObserver() {}
- virtual void changed() { gChatBar->refreshGestures(); }
+ virtual void changed() { mChatBar->refreshGestures(); }
+private:
+ LLChatBar* mChatBar;
};
@@ -66,12 +70,29 @@ public:
// Functions
//
-LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
+//inline constructor
+// for chat bars embedded in floaters, etc
+LLChatBar::LLChatBar(const std::string& name)
+: LLPanel(name, LLRect(), BORDER_NO),
+ mInputEditor(NULL),
+ mGestureLabelTimer(),
+ mLastSpecialChatChannel(0),
+ mIsBuilt(FALSE),
+ mDynamicLayout(FALSE),
+ mGestureCombo(NULL),
+ mObserver(NULL)
+{
+}
+
+LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
: LLPanel(name, rect, BORDER_NO),
mInputEditor(NULL),
mGestureLabelTimer(),
mLastSpecialChatChannel(0),
- mIsBuilt(FALSE)
+ mIsBuilt(FALSE),
+ mDynamicLayout(TRUE),
+ mGestureCombo(NULL),
+ mObserver(NULL)
{
setIsChrome(TRUE);
@@ -87,29 +108,6 @@ LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
// Start visible if we left the app while chatting.
setVisible( gSavedSettings.getBOOL("ChatVisible") );
- mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor");
- if (mInputEditor)
- {
- mInputEditor->setCallbackUserData(this);
- mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
- mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);
- mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );
- mInputEditor->setCommitOnFocusLost( FALSE );
- mInputEditor->setRevertOnEsc( FALSE );
- mInputEditor->setIgnoreTab(TRUE);
- mInputEditor->setPassDelete(TRUE);
- mInputEditor->setMaxTextLength(1023);
- mInputEditor->setEnableLineHistory(TRUE);
- }
-
- // Build the list of gestures
- refreshGestures();
-
- sObserver = new LLChatBarGestureObserver;
- gGestureManager.addObserver(sObserver);
-
- mIsBuilt = TRUE;
-
// Apply custom layout.
layout();
@@ -122,23 +120,43 @@ LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
LLChatBar::~LLChatBar()
{
- delete sObserver;
- sObserver = NULL;
+ delete mObserver;
+ mObserver = NULL;
// LLView destructor cleans up children
}
BOOL LLChatBar::postBuild()
{
- childSetAction("History", LLFloaterChat::toggle, this);
+ childSetAction("History", toggleChatHistory, this);
childSetAction("Say", onClickSay, this);
childSetAction("Shout", onClickShout, this);
- childSetCommitCallback("Gesture", onCommitGesture, this);
- LLButton * sayp = static_cast<LLButton*>(getChildByName("Say"));
+
+ // attempt to bind to an existing combo box named gesture
+ setGestureCombo(LLUICtrlFactory::getComboBoxByName(this, "Gesture"));
+
+ LLButton * sayp = static_cast<LLButton*>(getChildByName("Say", TRUE));
if(sayp)
{
setDefaultBtn(sayp);
}
+ mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor");
+ if (mInputEditor)
+ {
+ mInputEditor->setCallbackUserData(this);
+ mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
+ mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);
+ mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );
+ mInputEditor->setCommitOnFocusLost( FALSE );
+ mInputEditor->setRevertOnEsc( FALSE );
+ mInputEditor->setIgnoreTab(TRUE);
+ mInputEditor->setPassDelete(TRUE);
+
+ mInputEditor->setMaxTextLength(1023);
+ }
+
+ mIsBuilt = TRUE;
+
return TRUE;
}
@@ -186,7 +204,8 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
handled = TRUE;
}
}
- else if ( KEY_ESCAPE == key )
+ // only do this in main chatbar
+ else if ( KEY_ESCAPE == key && gChatBar == this)
{
stopChat();
@@ -199,6 +218,8 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
void LLChatBar::layout()
{
+ if (!mDynamicLayout) return;
+
S32 rect_width = mRect.getWidth();
S32 count = 9; // number of elements in LLToolBar
S32 pad = 4;
@@ -261,13 +282,16 @@ void LLChatBar::refresh()
// hide in mouselook, but keep previous visibility state
//BOOL mouselook = gAgent.cameraMouselook();
// call superclass setVisible so that we don't overwrite the saved setting
- LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible"));
+ if (mDynamicLayout)
+ {
+ LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible"));
+ }
// HACK: Leave the name of the gesture in place for a few seconds.
const F32 SHOW_GESTURE_NAME_TIME = 2.f;
if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
{
- LLCtrlListInterface* gestures = childGetListInterface("Gesture");
+ LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
if (gestures) gestures->selectFirstItem();
mGestureLabelTimer.stop();
}
@@ -277,6 +301,8 @@ void LLChatBar::refresh()
gAgent.stopTyping();
}
+ childSetValue("History", LLFloaterChat::instanceVisible(LLSD()));
+
childSetEnabled("Say", mInputEditor->getText().size() > 0);
childSetEnabled("Shout", mInputEditor->getText().size() > 0);
@@ -284,16 +310,18 @@ void LLChatBar::refresh()
void LLChatBar::refreshGestures()
{
- LLCtrlListInterface* gestures = childGetListInterface("Gesture");
- if (gestures)
+ LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
+ if (mGestureCombo && gestures)
{
//store current selection so we can maintain it
- LLString cur_gesture = childGetValue("Gesture").asString();
+ LLString cur_gesture = mGestureCombo->getValue().asString();
gestures->selectFirstItem();
- LLString label = childGetValue("Gesture").asString();
+ LLString label = mGestureCombo->getValue().asString();;
// clear
gestures->clearRows();
- // add gestures
+
+ // collect list of unique gestures
+ std::map <std::string, BOOL> unique;
LLGestureManager::item_map_t::iterator it;
for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
{
@@ -302,10 +330,18 @@ void LLChatBar::refreshGestures()
{
if (!gesture->mTrigger.empty())
{
- gestures->addSimpleElement(gesture->mTrigger);
+ unique[gesture->mTrigger] = TRUE;
}
}
}
+
+ // ad unique gestures
+ std::map <std::string, BOOL>::iterator it2;
+ for (it2 = unique.begin(); it2 != unique.end(); ++it2)
+ {
+ gestures->addSimpleElement((*it2).first);
+ }
+
gestures->sortByColumn(0, TRUE);
// Insert label after sorting
gestures->addSimpleElement(label, ADD_TOP);
@@ -362,6 +398,23 @@ LLString LLChatBar::getCurrentChat()
return mInputEditor ? mInputEditor->getText() : LLString::null;
}
+void LLChatBar::setGestureCombo(LLComboBox* combo)
+{
+ mGestureCombo = combo;
+ if (mGestureCombo)
+ {
+ mGestureCombo->setCommitCallback(onCommitGesture);
+ mGestureCombo->setCallbackUserData(this);
+
+ // now register observer since we have a place to put the results
+ mObserver = new LLChatBarGestureObserver(this);
+ gGestureManager.addObserver(mObserver);
+
+ // refresh list from current active gestures
+ refreshGestures();
+ }
+}
+
//-----------------------------------------------------------------------
// Internal functions
//-----------------------------------------------------------------------
@@ -453,13 +506,13 @@ void LLChatBar::sendChat( EChatType type )
sendChatFromViewer(utf8_revised_text, type, TRUE);
}
}
- childSetValue("Chat Editor", LLSD(LLString::null));
+ childSetValue("Chat Editor", LLString::null);
gAgent.stopTyping();
// If the user wants to stop chatting on hitting return, lose focus
// and go out of chat mode.
- if (gSavedSettings.getBOOL("CloseChatOnReturn"))
+ if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn"))
{
stopChat();
}
@@ -684,7 +737,7 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL
void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
{
LLChatBar* self = (LLChatBar*)data;
- LLCtrlListInterface* gestures = self->childGetListInterface("Gesture");
+ LLCtrlListInterface* gestures = self->mGestureCombo ? self->mGestureCombo->getListInterface() : NULL;
if (gestures)
{
S32 index = gestures->getFirstSelectedIndex();
@@ -708,6 +761,14 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
}
}
self->mGestureLabelTimer.start();
- // free focus back to chat bar
- self->childSetFocus("Gesture", FALSE);
+ if (self->mGestureCombo != NULL)
+ {
+ // free focus back to chat bar
+ self->mGestureCombo->setFocus(FALSE);
+ }
+}
+
+void toggleChatHistory(void* user_data)
+{
+ LLFloaterChat::toggleInstance(LLSD());
}
diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h
index 65724a1f45..33198f5fe6 100644
--- a/indra/newview/llchatbar.h
+++ b/indra/newview/llchatbar.h
@@ -13,23 +13,21 @@
#include "llframetimer.h"
#include "llchat.h"
-class LLButton;
-class LLComboBox;
class LLLineEditor;
class LLMessageSystem;
-class LLTextBox;
-class LLTextEditor;
class LLUICtrl;
class LLUUID;
class LLFrameTimer;
-class LLStatGraph;
class LLChatBarGestureObserver;
+class LLComboBox;
class LLChatBar
: public LLPanel
{
public:
- LLChatBar(const std::string& name, const LLRect& rect );
+ // constructor for inline chat-bars (e.g. hosted in chat history window)
+ LLChatBar(const std::string& name);
+ LLChatBar(const std::string& name, const LLRect& rect);
~LLChatBar();
virtual BOOL postBuild();
@@ -51,6 +49,10 @@ public:
BOOL inputEditorHasFocus();
LLString getCurrentChat();
+ // since chat bar logic is reused for chat history
+ // gesture combo box might not be a direct child
+ void setGestureCombo(LLComboBox* combo);
+
// Send a chat (after stripping /20foo channel chats).
// "Animate" means the nodding animation for regular text.
void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate);
@@ -61,7 +63,6 @@ public:
LLWString stripChannelNumber(const LLWString &mesg, S32* channel);
// callbacks
- static void onClickHistory( void* userdata );
static void onClickSay( void* userdata );
static void onClickShout( void* userdata );
@@ -89,8 +90,10 @@ protected:
S32 mLastSpecialChatChannel;
BOOL mIsBuilt;
-
- static LLChatBarGestureObserver* sObserver;
+ BOOL mDynamicLayout;
+ LLComboBox* mGestureCombo;
+
+ LLChatBarGestureObserver* mObserver;
};
extern LLChatBar *gChatBar;
diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp
index 41f8f1d714..7973e4d952 100644
--- a/indra/newview/llcolorswatch.cpp
+++ b/indra/newview/llcolorswatch.cpp
@@ -240,21 +240,6 @@ void LLColorSwatchCtrl::setEnabled( BOOL enabled )
}
-//////////////////////////////////////////////////////////////////////////////
-// called when parent filters down a visibility changed message
-void LLColorSwatchCtrl::onVisibilityChange ( BOOL curVisibilityIn )
-{
- // visibility changed - moved away to different tab for instance - cancel selection
- //if ( ! curVisibilityIn)
- //{
- // LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
- // if (pickerp)
- // {
- // pickerp->cancelSelection();
- // }
- //}
-}
-
void LLColorSwatchCtrl::setValue(const LLSD& value)
{
set(LLColor4(value), TRUE, TRUE);
diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h
index 999dce1296..4b44c5aef9 100644
--- a/indra/newview/llcolorswatch.h
+++ b/indra/newview/llcolorswatch.h
@@ -68,8 +68,6 @@ public:
virtual LLXMLNodePtr getXML(bool save_children = true) const;
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
- virtual void onVisibilityChange ( BOOL curVisibilityIn );
-
static void onColorChanged ( void* data, EColorPickOp pick_op = COLOR_CHANGE );
protected:
diff --git a/indra/newview/lldebugmessagebox.cpp b/indra/newview/lldebugmessagebox.cpp
index c62459f461..d3e7f08a95 100644
--- a/indra/newview/lldebugmessagebox.cpp
+++ b/indra/newview/lldebugmessagebox.cpp
@@ -33,25 +33,25 @@ LLDebugVarMessageBox::LLDebugVarMessageBox(const std::string& title, EDebugVarTy
switch(var_type)
{
case VAR_TYPE_F32:
- mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, *((F32*)var), -100.f, 100.f, 0.1f, NULL);
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, *((F32*)var), -100.f, 100.f, 0.1f, NULL);
mSlider1->setPrecision(3);
addChild(mSlider1);
mSlider2 = NULL;
mSlider3 = NULL;
break;
case VAR_TYPE_S32:
- mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, NULL);
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, NULL);
mSlider1->setPrecision(0);
addChild(mSlider1);
mSlider2 = NULL;
mSlider3 = NULL;
break;
case VAR_TYPE_VEC3:
- mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), "x: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, NULL);
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), "x: ", NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, NULL);
mSlider1->setPrecision(3);
- mSlider2 = new LLSliderCtrl("slider 2", LLRect(20,100,190,80), "y: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, NULL);
+ mSlider2 = new LLSliderCtrl("slider 2", LLRect(20,100,190,80), "y: ", NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, NULL);
mSlider2->setPrecision(3);
- mSlider3 = new LLSliderCtrl("slider 3", LLRect(20,70,190,50), "z: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, NULL);
+ mSlider3 = new LLSliderCtrl("slider 3", LLRect(20,70,190,50), "z: ", NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, NULL);
mSlider3->setPrecision(3);
addChild(mSlider1);
addChild(mSlider2);
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
index d20c431987..4e0cc94a26 100644
--- a/indra/newview/lldebugview.cpp
+++ b/indra/newview/lldebugview.cpp
@@ -17,7 +17,6 @@
#include "llconsole.h"
#include "lltextureview.h"
#include "llresmgr.h"
-#include "llaudiostatus.h"
#include "imageids.h"
#include "llvelocitybar.h"
#include "llviewerwindow.h"
@@ -89,19 +88,6 @@ LLDebugView::LLDebugView(const std::string& name, const LLRect &rect)
mStatViewp->setVisible(FALSE);
addChild(mStatViewp);
- //
- // Audio debugging stuff
- //
- const S32 AUDIO_STATUS_LEFT = rect.getWidth()/2-100;
- const S32 AUDIO_STATUS_WIDTH = 320;
- const S32 AUDIO_STATUS_TOP = (rect.getHeight()/2)+400;
- const S32 AUDIO_STATUS_HEIGHT = 320;
- r.setLeftTopAndSize( AUDIO_STATUS_LEFT, AUDIO_STATUS_TOP, AUDIO_STATUS_WIDTH, AUDIO_STATUS_HEIGHT );
- LLAudiostatus* gAudioStatus = new LLAudiostatus("AudioStatus", r);
- gAudioStatus->setFollowsTop();
- gAudioStatus->setFollowsRight();
- addChild(gAudioStatus);
-
const S32 VELOCITY_LEFT = 10; // 370;
const S32 VELOCITY_WIDTH = 500;
const S32 VELOCITY_TOP = 140;
diff --git a/indra/newview/llemote.h b/indra/newview/llemote.h
index c330d0bfc0..ede07b6db1 100644
--- a/indra/newview/llemote.h
+++ b/indra/newview/llemote.h
@@ -88,6 +88,8 @@ public:
// called when a motion is deactivated
virtual void onDeactivate();
+ virtual BOOL canDeprecate() { return FALSE; }
+
static BOOL getIndexFromName( const char* name, U32* index );
protected:
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index f5e6545369..7441d646ef 100644
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -911,8 +911,7 @@ void LLFastTimerView::draw()
//draw line graph history
{
LLGLSNoTexture no_texture;
- LLGLEnable scissor(GL_SCISSOR_TEST);
- LLUI::setScissorRegionLocal(graph_rect);
+ LLLocalClipRect clip(graph_rect);
//normalize based on last frame's maximum
static U64 last_max = 0;
diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp
index 9f05e59ac8..2982dba7b2 100644
--- a/indra/newview/llfirstuse.cpp
+++ b/indra/newview/llfirstuse.cpp
@@ -15,6 +15,7 @@
// viewer includes
#include "llnotify.h"
+#include "llfloatervoicewizard.h"
#include "llviewercontrol.h"
#include "llui.h"
#include "viewer.h"
@@ -228,3 +229,15 @@ void LLFirstUse::useSculptedPrim()
}
}
+
+// static
+void LLFirstUse::useVoice()
+{
+ if (gDisableVoice) return;
+ if (gSavedSettings.getWarning("FirstVoice"))
+ {
+ gSavedSettings.setWarning("FirstVoice", FALSE);
+
+ LLFloaterVoiceWizard::showInstance();
+ }
+}
diff --git a/indra/newview/llfirstuse.h b/indra/newview/llfirstuse.h
index 134699d1ec..977ff7ddeb 100644
--- a/indra/newview/llfirstuse.h
+++ b/indra/newview/llfirstuse.h
@@ -81,6 +81,7 @@ public:
static void useFlexible();
static void useDebugMenus();
static void useSculptedPrim();
+ static void useVoice();
protected:
static std::set<LLString> sConfigVariables;
diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp
index 7085407999..edbfa4244c 100644
--- a/indra/newview/llfloateranimpreview.cpp
+++ b/indra/newview/llfloateranimpreview.cpp
@@ -973,7 +973,7 @@ void LLFloaterAnimPreview::onBtnOK(void* userdata)
LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID);
}
- floaterp->onClose(false);
+ floaterp->close(false);
}
//-----------------------------------------------------------------------------
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
index b541b279d0..0ba0ab89b1 100644
--- a/indra/newview/llfloateravatarpicker.cpp
+++ b/indra/newview/llfloateravatarpicker.cpp
@@ -188,7 +188,12 @@ void LLFloaterAvatarPicker::onSelectionChange(const std::deque<LLFolderViewItem*
self->mAvatarIDs.clear();
self->mAvatarNames.clear();
- self->childSetEnabled("Select", FALSE);
+ // if we have calling cards, disable select button until
+ // the inventory picks a valid calling card
+ if (!items.empty())
+ {
+ self->childSetEnabled("Select", FALSE);
+ }
if (!self->mListNames)
{
diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp
index f4769cc18f..6e76db4270 100644
--- a/indra/newview/llfloaterchat.cpp
+++ b/indra/newview/llfloaterchat.cpp
@@ -14,6 +14,7 @@
#include "llviewerprecompiledheaders.h"
#include "llfloaterchat.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterscriptdebug.h"
#include "llchat.h"
@@ -29,11 +30,13 @@
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llconsole.h"
+#include "llfloaterchatterbox.h"
#include "llfloatermute.h"
#include "llkeyboard.h"
//#include "lllineeditor.h"
#include "llmutelist.h"
//#include "llresizehandle.h"
+#include "llchatbar.h"
#include "llstatusbar.h"
#include "llviewertexteditor.h"
#include "llviewergesture.h" // for triggering gestures
@@ -50,7 +53,6 @@
//
// Constants
//
-const char FLOATER_TITLE[] = "Chat History";
const F32 INSTANT_MSG_SIZE = 8.0f;
const F32 CHAT_MSG_SIZE = 8.0f;
const LLColor4 INSTANT_MSG_COLOR(1, 1, 1, 1);
@@ -60,25 +62,25 @@ const S32 MAX_CHATTER_COUNT = 16;
//
// Global statics
//
-LLFloaterChat* gFloaterChat = NULL;
-
LLColor4 get_text_color(const LLChat& chat);
//
// Member Functions
//
-LLFloaterChat::LLFloaterChat()
-: LLFloater("chat floater", "FloaterChatRect", FLOATER_TITLE,
- RESIZE_YES, 440, 100, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES)
+LLFloaterChat::LLFloaterChat(const LLSD& seed)
+: LLFloater("chat floater", "FloaterChatRect", "",
+ RESIZE_YES, 440, 100, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES),
+ mPanel(NULL)
{
-
- gUICtrlFactory->buildFloater(this,"floater_chat_history.xml");
+ mFactoryMap["chat_panel"] = LLCallbackMap(createChatPanel, NULL);
+ mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL);
+ // do not automatically open singleton floaters (as result of getInstance())
+ BOOL no_open = FALSE;
+ gUICtrlFactory->buildFloater(this,"floater_chat_history.xml",&getFactoryMap(),no_open);
- childSetAction("Mute resident",onClickMute,this);
- childSetAction("Chat", onClickChat, this);
- childSetCommitCallback("chatter combobox",onCommitUserSelect,this);
childSetCommitCallback("show mutes",onClickToggleShowMute,this); //show mutes
childSetVisible("Chat History Editor with mute",FALSE);
+ childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this);
setDefaultBtn("Chat");
}
@@ -92,33 +94,53 @@ void LLFloaterChat::setVisible(BOOL visible)
LLFloater::setVisible( visible );
gSavedSettings.setBOOL("ShowChatHistory", visible);
+}
- // Hide the chat overlay when our history is visible.
- gConsole->setVisible( !visible );
+void LLFloaterChat::draw()
+{
+ // enable say and shout only when text available
+
+ childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel"));
+
+ LLChatBar* chat_barp = (LLChatBar*)getChildByName("chat_panel", TRUE);
+ if (chat_barp)
+ {
+ chat_barp->refresh();
+ }
+
+ mPanel->refreshSpeakers();
+ LLFloater::draw();
}
+BOOL LLFloaterChat::postBuild()
+{
+ mPanel = (LLPanelActiveSpeakers*)LLUICtrlFactory::getPanelByName(this, "active_speakers_panel");
+
+ LLChatBar* chat_barp = (LLChatBar*)getChildByName("chat_panel", TRUE);
+ if (chat_barp)
+ {
+ chat_barp->setGestureCombo(LLUICtrlFactory::getComboBoxByName(this, "Gesture"));
+ }
+ return TRUE;
+}
// public virtual
void LLFloaterChat::onClose(bool app_quitting)
{
- LLFloater::setVisible( FALSE );
-
if (!app_quitting)
{
gSavedSettings.setBOOL("ShowChatHistory", FALSE);
}
-
- // Hide the chat overlay when our history is visible.
- gConsole->setVisible( TRUE );
+ setVisible(FALSE);
}
-
-// public
-void LLFloaterChat::show()
+void LLFloaterChat::onVisibilityChange(BOOL new_visibility)
{
- open(); /*Flawfinder: ignore*/
+ // Hide the chat overlay when our history is visible.
+ gConsole->setVisible( !new_visibility );
}
+
void add_timestamped_line(LLViewerTextEditor* edit, const LLString& line, const LLColor4& color)
{
bool prepend_newline = true;
@@ -142,7 +164,7 @@ void log_chat_text(const LLChat& chat)
}
// static
void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
-{
+{
if ( gSavedPerAccountSettings.getBOOL("LogChat") && log_to_file)
{
log_chat_text(chat);
@@ -165,10 +187,9 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
}
// could flash the chat button in the status bar here. JC
- if (!gFloaterChat) return;
-
- LLViewerTextEditor* history_editor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
- LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
+ LLFloaterChat* chat_floater = LLFloaterChat::getInstance(LLSD());
+ LLViewerTextEditor* history_editor = (LLViewerTextEditor*)chat_floater->getChildByName("Chat History Editor", TRUE);
+ LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)chat_floater->getChildByName("Chat History Editor with mute", TRUE);
history_editor->setParseHTML(TRUE);
history_editor_with_mute->setParseHTML(TRUE);
@@ -184,77 +205,24 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
LLColor4 muted_color = lerp(color, LLColor4::grey, 0.5f);
add_timestamped_line(history_editor_with_mute, chat.mText, color);
}
-
- if (!chat.mMuted
- && chat.mSourceType != CHAT_SOURCE_SYSTEM
- && chat.mFromID.notNull()
- && chat.mFromID != gAgent.getID())
+
+ // add objects as transient speakers that can be muted
+ if (chat.mSourceType == CHAT_SOURCE_OBJECT)
{
-
- LLComboBox* chatter_combo = LLUICtrlFactory::getComboBoxByName(gFloaterChat,"chatter combobox");
-
- if(!chatter_combo)
- {
- return;
- }
-
- if (!chatter_combo->setCurrentByID(chat.mFromID))
- {
- // if we have too many items...
- if (chatter_combo->getItemCount() >= MAX_CHATTER_COUNT)
- {
- chatter_combo->remove(0);
- }
-
- LLMute mute(chat.mFromID, chat.mFromName);
- if (chat.mSourceType == CHAT_SOURCE_OBJECT)
- {
- mute.mType = LLMute::OBJECT;
- }
- else if (chat.mSourceType == CHAT_SOURCE_AGENT)
- {
- mute.mType = LLMute::AGENT;
- }
- LLString item = mute.getDisplayName();
- chatter_combo->add(item, chat.mFromID);
- chatter_combo->setCurrentByIndex(chatter_combo->getItemCount() - 1);
- gFloaterChat->childSetEnabled("Mute resident",TRUE);
- }
+ chat_floater->mPanel->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT);
}
}
// static
void LLFloaterChat::setHistoryCursorAndScrollToEnd()
{
- if (gFloaterChat)
- {
- LLViewerTextEditor* history_editor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
- LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
-
- history_editor->setCursorAndScrollToEnd();
- history_editor_with_mute->setCursorAndScrollToEnd();
- }
-}
-
-
-// static
-void LLFloaterChat::toggle(void*)
-{
- if (gFloaterChat->getVisible())
- {
- gFloaterChat->close();
- }
- else
- {
- gFloaterChat->show();
- }
+ LLViewerTextEditor* history_editor = (LLViewerTextEditor*)LLFloaterChat::getInstance(LLSD())->getChildByName("Chat History Editor", TRUE);
+ LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)LLFloaterChat::getInstance(LLSD())->getChildByName("Chat History Editor with mute", TRUE);
+
+ history_editor->setCursorAndScrollToEnd();
+ history_editor_with_mute->setCursorAndScrollToEnd();
}
-// static
-BOOL LLFloaterChat::visible(void*)
-{
- return (gFloaterChat && gFloaterChat->getVisible());
-}
//static
void LLFloaterChat::onClickMute(void *data)
@@ -279,30 +247,6 @@ void LLFloaterChat::onClickMute(void *data)
}
//static
-void LLFloaterChat::onClickChat(void*)
-{
- // we need this function as a level of indirection because otherwise startChat would
- // cast the data pointer to a character string, and dump garbage in the chat
- LLChatBar::startChat(NULL);
-}
-
-//static
-void LLFloaterChat::onCommitUserSelect(LLUICtrl* caller, void* data)
-{
- LLFloaterChat* floater = (LLFloaterChat*)data;
- LLComboBox* combo = (LLComboBox*)caller;
-
- if (combo->getCurrentIndex() == -1)
- {
- floater->childSetEnabled("Mute resident",FALSE);
- }
- else
- {
- floater->childSetEnabled("Mute resident",TRUE);
- }
-}
-
-//static
void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
{
LLFloaterChat* floater = (LLFloaterChat*)data;
@@ -310,8 +254,8 @@ void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
//LLCheckBoxCtrl*
BOOL show_mute = LLUICtrlFactory::getCheckBoxByName(floater,"show mutes")->get();
- LLViewerTextEditor* history_editor = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor");
- LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor with mute");
+ LLViewerTextEditor* history_editor = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor", TRUE);
+ LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor with mute", TRUE);
if (!history_editor || !history_editor_with_mute)
return;
@@ -427,7 +371,7 @@ LLColor4 get_text_color(const LLChat& chat)
//static
void LLFloaterChat::loadHistory()
{
- LLLogChat::loadHistory("chat", &chatFromLogFile, (void *)gFloaterChat);
+ LLLogChat::loadHistory("chat", &chatFromLogFile, (void *)LLFloaterChat::getInstance(LLSD()));
}
//static
@@ -438,3 +382,40 @@ void LLFloaterChat::chatFromLogFile(LLString line, void* userdata)
chat.mText = line;
addChatHistory(chat, FALSE);
}
+
+//static
+void* LLFloaterChat::createSpeakersPanel(void* data)
+{
+ return new LLPanelActiveSpeakers(gLocalSpeakerMgr, TRUE);
+}
+
+//static
+void* LLFloaterChat::createChatPanel(void* data)
+{
+ LLChatBar* chatp = new LLChatBar("floating_chat_bar");
+ return chatp;
+}
+
+//static
+void LLFloaterChat::hideInstance(const LLSD& id)
+{
+ LLFloaterChat* floaterp = LLFloaterChat::getInstance(LLSD());
+ // don't do anything when hosted in the chatterbox
+ if(floaterp->getHost())
+ {
+ LLFloaterChatterBox::hideInstance(LLSD());
+ }
+ else
+ {
+ LLUISingleton<LLFloaterChat>::hideInstance(id);
+ }
+}
+
+// static
+void LLFloaterChat::onClickToggleActiveSpeakers(void* userdata)
+{
+ LLFloaterChat* self = (LLFloaterChat*)userdata;
+
+ self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel"));
+}
+
diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h
index bf93afb455..6da3b9bb1b 100644
--- a/indra/newview/llfloaterchat.h
+++ b/indra/newview/llfloaterchat.h
@@ -24,17 +24,20 @@ class LLViewerTextEditor;
class LLMessageSystem;
class LLUUID;
class LLCheckBoxCtrl;
+class LLPanelActiveSpeakers;
class LLFloaterChat
-: public LLFloater
+: public LLFloater, public LLUISingleton<LLFloaterChat>
{
public:
- LLFloaterChat();
+ LLFloaterChat(const LLSD& seed);
~LLFloaterChat();
- void show();
- virtual void onClose(bool app_quitting);
virtual void setVisible( BOOL b );
+ virtual void draw();
+ virtual BOOL postBuild();
+ virtual void onClose(bool app_quitting);
+ virtual void onVisibilityChange(BOOL cur_visibility);
static void setHistoryCursorAndScrollToEnd();
@@ -45,17 +48,17 @@ public:
// Add chat to history alone.
static void addChatHistory(const LLChat& chat, bool log_to_file = true);
- static void toggle(void*);
- static BOOL visible(void*);
-
static void onClickMute(void *data);
- static void onClickChat(void *);
- static void onCommitUserSelect(LLUICtrl* caller, void* data);
static void onClickToggleShowMute(LLUICtrl* caller, void *data);
+ static void onClickToggleActiveSpeakers(void* userdata);
static void chatFromLogFile(LLString line, void* userdata);
static void loadHistory();
-};
+ static void* createSpeakersPanel(void* data);
+ static void* createChatPanel(void* data);
+ static void hideInstance(const LLSD& id);
-extern LLFloaterChat* gFloaterChat;
+protected:
+ LLPanelActiveSpeakers* mPanel;
+};
#endif
diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp
new file mode 100644
index 0000000000..ab992bdb45
--- /dev/null
+++ b/indra/newview/llfloaterchatterbox.cpp
@@ -0,0 +1,322 @@
+/**
+ * @file llfloaterchatterbox.cpp
+ * @author Richard
+ * @date 2007-05-08
+ * @brief Implementation of the chatterbox integrated conversation ui
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterchatterbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llfloaterchat.h"
+#include "llfloaterfriends.h"
+#include "llfloatergroups.h"
+#include "llviewercontrol.h"
+#include "llimview.h"
+#include "llimpanel.h"
+
+//
+// LLFloaterMyFriends
+//
+
+LLFloaterMyFriends::LLFloaterMyFriends(const LLSD& seed)
+{
+ mFactoryMap["friends_panel"] = LLCallbackMap(LLFloaterMyFriends::createFriendsPanel, NULL);
+ mFactoryMap["groups_panel"] = LLCallbackMap(LLFloaterMyFriends::createGroupsPanel, NULL);
+ // do not automatically open singleton floaters (as result of getInstance())
+ BOOL no_open = FALSE;
+ gUICtrlFactory->buildFloater(this, "floater_my_friends.xml", &getFactoryMap(), no_open);
+}
+
+LLFloaterMyFriends::~LLFloaterMyFriends()
+{
+}
+
+BOOL LLFloaterMyFriends::postBuild()
+{
+ mTabs = LLUICtrlFactory::getTabContainerByName(this, "friends_and_groups");
+
+ return TRUE;
+}
+
+
+void LLFloaterMyFriends::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+}
+
+//static
+LLFloaterMyFriends* LLFloaterMyFriends::showInstance(const LLSD& id)
+{
+ LLFloaterMyFriends* floaterp = LLUIInstanceMgr<LLFloaterMyFriends>::showInstance(id);
+ // garbage values in id will be interpreted as 0, or the friends tab
+ floaterp->mTabs->selectTab(id);
+
+ return floaterp;
+}
+
+//static
+void LLFloaterMyFriends::hideInstance(const LLSD& id)
+{
+ if(instanceVisible(id))
+ {
+ LLFloaterChatterBox::hideInstance(LLSD());
+ }
+}
+
+// is the specified panel currently visible
+//static
+BOOL LLFloaterMyFriends::instanceVisible(const LLSD& id)
+{
+ // if singleton not created yet, trivially return false
+ if (!findInstance(id)) return FALSE;
+
+ LLFloaterMyFriends* floaterp = getInstance(id);
+ return floaterp->isInVisibleChain() && floaterp->mTabs->getCurrentPanelIndex() == id.asInteger();
+}
+
+//static
+void* LLFloaterMyFriends::createFriendsPanel(void* data)
+{
+ return new LLPanelFriends();
+}
+
+//static
+void* LLFloaterMyFriends::createGroupsPanel(void* data)
+{
+ return new LLPanelGroups();
+}
+
+//
+// LLFloaterChatterBox
+//
+LLFloaterChatterBox::LLFloaterChatterBox(const LLSD& seed) :
+ mActiveVoiceFloater(NULL)
+{
+ mAutoResize = FALSE;
+
+ gUICtrlFactory->buildFloater(this, "floater_chatterbox.xml", NULL, FALSE);
+ addFloater(LLFloaterMyFriends::getInstance(0), TRUE);
+ if (gSavedSettings.getBOOL("ChatHistoryTornOff"))
+ {
+ LLFloaterChat* floater_chat = LLFloaterChat::getInstance(LLSD());
+ // add then remove to set up relationship for re-attach
+ addFloater(floater_chat, FALSE);
+ removeFloater(floater_chat);
+ // reparent to floater view
+ gFloaterView->addChild(floater_chat);
+ }
+ else
+ {
+ addFloater(LLFloaterChat::getInstance(LLSD()), FALSE);
+ }
+ mTabContainer->lockTabs();
+}
+
+LLFloaterChatterBox::~LLFloaterChatterBox()
+{
+}
+
+BOOL LLFloaterChatterBox::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (getEnabled()
+ && mask == MASK_CONTROL)
+ {
+ if (key == 'W')
+ {
+ LLFloater* floater = getActiveFloater();
+ // is user closeable and is system closeable
+ if (floater && floater->canClose())
+ {
+ if (floater->isCloseable())
+ {
+ floater->close();
+ }
+ else
+ {
+ // close chatterbox window if frontmost tab is reserved, non-closeable tab
+ // such as contacts or near me
+ close();
+ }
+ }
+ return TRUE;
+ }
+ }
+
+ return LLMultiFloater::handleKeyHere(key, mask, called_from_parent);
+}
+
+void LLFloaterChatterBox::draw()
+{
+ // clear new im notifications when chatterbox is visible
+ if (!isMinimized())
+ {
+ gIMMgr->clearNewIMNotification();
+ }
+ LLFloater* current_active_floater = getCurrentVoiceFloater();
+ // set icon on tab for floater currently associated with active voice channel
+ if(mActiveVoiceFloater != current_active_floater)
+ {
+ // remove image from old floater's tab
+ if (mActiveVoiceFloater)
+ {
+ mTabContainer->setTabImage(mActiveVoiceFloater, "");
+ }
+ }
+
+ // update image on current active tab
+ if (current_active_floater)
+ {
+ LLColor4 icon_color = LLColor4::white;
+ LLVoiceChannel* channelp = LLVoiceChannel::getCurrentVoiceChannel();
+ if (channelp)
+ {
+ if (channelp->isActive())
+ {
+ icon_color = LLColor4::green;
+ }
+ else if (channelp->getState() == LLVoiceChannel::STATE_ERROR)
+ {
+ icon_color = LLColor4::red;
+ }
+ else // active, but not connected
+ {
+ icon_color = LLColor4::yellow;
+ }
+ }
+ mTabContainer->setTabImage(current_active_floater, "active_voice_tab.tga", icon_color);
+ }
+
+ mActiveVoiceFloater = current_active_floater;
+
+ LLFloater::draw();
+}
+
+void LLFloaterChatterBox::onOpen()
+{
+ gSavedSettings.setBOOL("ShowCommunicate", TRUE);
+}
+
+void LLFloaterChatterBox::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+ gSavedSettings.setBOOL("ShowCommunicate", FALSE);
+}
+
+void LLFloaterChatterBox::removeFloater(LLFloater* floaterp)
+{
+ if (floaterp->getName() == "chat floater")
+ {
+ // only my friends floater now locked
+ mTabContainer->lockTabs(1);
+ gSavedSettings.setBOOL("ChatHistoryTornOff", TRUE);
+ floaterp->setCanClose(TRUE);
+ }
+ LLMultiFloater::removeFloater(floaterp);
+}
+
+void LLFloaterChatterBox::addFloater(LLFloater* floaterp,
+ BOOL select_added_floater,
+ LLTabContainerCommon::eInsertionPoint insertion_point)
+{
+ // make sure my friends and chat history both locked when re-attaching chat history
+ if (floaterp->getName() == "chat floater")
+ {
+ // select my friends tab
+ mTabContainer->selectFirstTab();
+ // add chat history to the right of the my friends tab
+ //*TODO: respect select_added_floater so that we don't leave first tab selected
+ LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT);
+ // make sure first two tabs are now locked
+ mTabContainer->lockTabs(2);
+ gSavedSettings.setBOOL("ChatHistoryTornOff", FALSE);
+ floaterp->setCanClose(FALSE);
+ }
+ else
+ {
+ LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+ }
+
+ // make sure active voice icon shows up for new tab
+ if (floaterp == mActiveVoiceFloater)
+ {
+ mTabContainer->setTabImage(floaterp, "active_voice_tab.tga");
+ }
+}
+
+
+//static
+LLFloaterChatterBox* LLFloaterChatterBox::showInstance(const LLSD& seed)
+{
+ LLFloaterChatterBox* floater = LLUISingleton<LLFloaterChatterBox>::showInstance(seed);
+
+ // if TRUE, show tab for active voice channel, otherwise, just show last tab
+ if (seed.asBoolean())
+ {
+ LLFloater* floater_to_show = getCurrentVoiceFloater();
+ if (floater_to_show)
+ {
+ floater_to_show->open();
+ }
+ else
+ {
+ // just open chatterbox if there is no active voice window
+ LLUISingleton<LLFloaterChatterBox>::getInstance(seed)->open();
+ }
+ }
+
+ return floater;
+}
+
+//static
+BOOL LLFloaterChatterBox::instanceVisible(const LLSD &seed)
+{
+ if (seed.asBoolean())
+ {
+ LLFloater* floater_to_show = getCurrentVoiceFloater();
+ if (floater_to_show)
+ {
+ return floater_to_show->isInVisibleChain();
+ }
+ }
+
+ return LLUISingleton<LLFloaterChatterBox>::instanceVisible(seed);
+}
+
+//static
+LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater()
+{
+ if (!LLVoiceClient::voiceEnabled())
+ {
+ return NULL;
+ }
+ if (LLVoiceChannelProximal::getInstance() == LLVoiceChannel::getCurrentVoiceChannel())
+ {
+ // show near me tab if in proximal channel
+ return LLFloaterChat::getInstance(LLSD());
+ }
+ else
+ {
+ LLFloaterChatterBox* floater = LLFloaterChatterBox::getInstance(LLSD());
+ // iterator over all IM tabs (skip friends and near me)
+ for (S32 i = 0; i < floater->getFloaterCount(); i++)
+ {
+ LLPanel* panelp = floater->mTabContainer->getPanelByIndex(i);
+ if (panelp->getName() == "im_floater")
+ {
+ // only LLFloaterIMPanels are called "im_floater"
+ LLFloaterIMPanel* im_floaterp = (LLFloaterIMPanel*)panelp;
+ if (im_floaterp->getVoiceChannel() == LLVoiceChannel::getCurrentVoiceChannel())
+ {
+ return im_floaterp;
+ }
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/indra/newview/llfloaterchatterbox.h b/indra/newview/llfloaterchatterbox.h
new file mode 100644
index 0000000000..38cc7d6e4b
--- /dev/null
+++ b/indra/newview/llfloaterchatterbox.h
@@ -0,0 +1,67 @@
+/**
+ * @file llfloaterchatterbox.h
+ * @author Richard
+ * @date 2007-05-04
+ * @brief Integrated friends and group management/communication tool
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERCHATTERBOX_H
+#define LL_LLFLOATERCHATTERBOX_H
+
+#include "llfloater.h"
+#include "llstring.h"
+
+class LLTabContainerCommon;
+
+class LLFloaterMyFriends : public LLFloater, public LLUISingleton<LLFloaterMyFriends>
+{
+public:
+ LLFloaterMyFriends(const LLSD& seed);
+ virtual ~LLFloaterMyFriends();
+
+ virtual BOOL postBuild();
+
+ void onClose(bool app_quitting);
+
+ // override LLUISingleton behavior
+ static LLFloaterMyFriends* showInstance(const LLSD& id);
+ static void hideInstance(const LLSD& id);
+ static BOOL instanceVisible(const LLSD& id);
+
+ static void* createFriendsPanel(void* data);
+ static void* createGroupsPanel(void* data);
+
+protected:
+ LLTabContainerCommon* mTabs;
+};
+
+class LLFloaterChatterBox : public LLMultiFloater, public LLUISingleton<LLFloaterChatterBox>
+{
+public:
+ LLFloaterChatterBox(const LLSD& seed);
+ virtual ~LLFloaterChatterBox();
+
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ void draw();
+ /*virtual*/ void onOpen();
+ /*virtual*/ void onClose(bool app_quitting);
+
+ /*virtual*/ void removeFloater(LLFloater* floaterp);
+ /*virtual*/ void addFloater(LLFloater* floaterp,
+ BOOL select_added_floater,
+ LLTabContainerCommon::eInsertionPoint insertion_point = LLTabContainerCommon::END);
+
+ static LLFloaterChatterBox* showInstance(const LLSD& seed);
+ static BOOL instanceVisible(const LLSD& seed);
+
+ static LLFloater* getCurrentVoiceFloater();
+
+protected:
+ LLFloater* mActiveVoiceFloater;
+};
+
+
+#endif // LL_LLFLOATERCHATTERBOX_H
diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp
index 7e0a64c420..592cb4186c 100644
--- a/indra/newview/llfloaterfriends.cpp
+++ b/indra/newview/llfloaterfriends.cpp
@@ -43,20 +43,18 @@
class LLLocalFriendsObserver : public LLFriendObserver
{
public:
- LLLocalFriendsObserver(LLFloaterFriends* floater) : mFloater(floater) {}
+ LLLocalFriendsObserver(LLPanelFriends* floater) : mFloater(floater) {}
virtual ~LLLocalFriendsObserver() { mFloater = NULL; }
virtual void changed(U32 mask)
{
mFloater->updateFriends(mask);
}
protected:
- LLFloaterFriends* mFloater;
+ LLPanelFriends* mFloater;
};
-LLFloaterFriends* LLFloaterFriends::sInstance = NULL;
-
-LLFloaterFriends::LLFloaterFriends() :
- LLFloater("Friends"),
+LLPanelFriends::LLPanelFriends() :
+ LLPanel(),
LLEventTimer(1000000),
mObserver(NULL),
mMenuState(0),
@@ -64,81 +62,38 @@ LLFloaterFriends::LLFloaterFriends() :
mAllowRightsChange(TRUE),
mNumRightsChanged(0)
{
- mTimer.stop();
- sInstance = this;
+ mEventTimer.stop();
mObserver = new LLLocalFriendsObserver(this);
LLAvatarTracker::instance().addObserver(mObserver);
- gSavedSettings.setBOOL("ShowFriends", TRUE);
- // Builds and adds to gFloaterView
- gUICtrlFactory->buildFloater(this, "floater_friends.xml");
- refreshUI();
}
-LLFloaterFriends::~LLFloaterFriends()
+LLPanelFriends::~LLPanelFriends()
{
LLAvatarTracker::instance().removeObserver(mObserver);
delete mObserver;
- sInstance = NULL;
- gSavedSettings.setBOOL("ShowFriends", FALSE);
}
-void LLFloaterFriends::tick()
+void LLPanelFriends::tick()
{
- mTimer.stop();
+ mEventTimer.stop();
mPeriod = 1000000;
mAllowRightsChange = TRUE;
updateFriends(LLFriendObserver::ADD);
}
-// static
-void LLFloaterFriends::show(void*)
-{
- if(sInstance)
- {
- sInstance->open(); /*Flawfinder: ignore*/
- }
- else
- {
- LLFloaterFriends* self = new LLFloaterFriends;
- self->open(); /*Flawfinder: ignore*/
- }
-}
-
-
-// static
-BOOL LLFloaterFriends::visible(void*)
-{
- return sInstance && sInstance->getVisible();
-}
-
-
-// static
-void LLFloaterFriends::toggle(void*)
-{
- if (sInstance)
- {
- sInstance->close();
- }
- else
- {
- show();
- }
-}
-
-
-void LLFloaterFriends::updateFriends(U32 changed_mask)
+void LLPanelFriends::updateFriends(U32 changed_mask)
{
LLUUID selected_id;
- LLCtrlListInterface *friends_list = sInstance->childGetListInterface("friend_list");
+ LLCtrlListInterface *friends_list = childGetListInterface("friend_list");
if (!friends_list) return;
- LLCtrlScrollInterface *friends_scroll = sInstance->childGetScrollInterface("friend_list");
+ LLCtrlScrollInterface *friends_scroll = childGetScrollInterface("friend_list");
if (!friends_scroll) return;
// We kill the selection warning, otherwise we'll spam with warning popups
// if the maximum amount of friends are selected
mShowMaxSelectWarning = false;
- LLDynamicArray<LLUUID> selected_friends = sInstance->getSelectedIDs();
+ LLDynamicArray<LLUUID> selected_friends = getSelectedIDs();
if(changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
{
refreshNames();
@@ -149,8 +104,8 @@ void LLFloaterFriends::updateFriends(U32 changed_mask)
if(mNumRightsChanged > 0)
{
mPeriod = RIGHTS_CHANGE_TIMEOUT;
- mTimer.start();
- mTimer.reset();
+ mEventTimer.start();
+ mEventTimer.reset();
mAllowRightsChange = FALSE;
}
else
@@ -175,12 +130,12 @@ void LLFloaterFriends::updateFriends(U32 changed_mask)
}
// virtual
-BOOL LLFloaterFriends::postBuild()
+BOOL LLPanelFriends::postBuild()
{
-
mFriendsList = LLUICtrlFactory::getScrollListByName(this, "friend_list");
mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT);
mFriendsList->setMaxiumumSelectCallback(onMaximumSelect);
+ mFriendsList->setCommitOnSelectionChange(TRUE);
childSetCommitCallback("friend_list", onSelectName, this);
childSetDoubleClickCallback("friend_list", onClickIM);
@@ -195,13 +150,17 @@ BOOL LLFloaterFriends::postBuild()
childSetAction("pay_btn", onClickPay, this);
childSetAction("add_btn", onClickAddFriend, this);
childSetAction("remove_btn", onClickRemove, this);
- childSetAction("close_btn", onClickClose, this);
+
+ setDefaultBtn("im_btn");
+
+ updateFriends(LLFriendObserver::ADD);
+ refreshUI();
return TRUE;
}
-void LLFloaterFriends::addFriend(const std::string& name, const LLUUID& agent_id)
+void LLPanelFriends::addFriend(const std::string& name, const LLUUID& agent_id)
{
LLAvatarTracker& at = LLAvatarTracker::instance();
const LLRelationship* relationInfo = at.getBuddyInfo(agent_id);
@@ -255,17 +214,26 @@ void LLFloaterFriends::addFriend(const std::string& name, const LLUUID& agent_id
mFriendsList->addElement(element, ADD_BOTTOM);
}
-void LLFloaterFriends::refreshRightsChangeList(U8 state)
+void LLPanelFriends::refreshRightsChangeList()
{
LLDynamicArray<LLUUID> friends = getSelectedIDs();
- const LLRelationship* friend_status = NULL;
- if(friends.size() > 0) friend_status = LLAvatarTracker::instance().getBuddyInfo(friends[0]);
+ S32 num_selected = friends.size();
LLSD row;
- bool can_change_visibility = false;
- bool can_change_modify = false;
- bool can_change_online_multiple = true;
- bool can_change_map_multiple = true;
+ bool can_offer_teleport = num_selected >= 1;
+
+ // aggregate permissions over all selected friends
+ bool friends_see_online = true;
+ bool friends_see_on_map = true;
+ bool friends_modify_objects = true;
+
+ // do at least some of the friends selected have these rights?
+ bool some_friends_see_online = false;
+ bool some_friends_see_on_map = false;
+ bool some_friends_modify_objects = false;
+
+ bool selected_friends_online = true;
+
LLTextBox* processing_label = LLUICtrlFactory::getTextBoxByName(this, "process_rights_label");
if(!mAllowRightsChange)
@@ -273,7 +241,9 @@ void LLFloaterFriends::refreshRightsChangeList(U8 state)
if(processing_label)
{
processing_label->setVisible(true);
- state = 0;
+ // ignore selection for now
+ friends.clear();
+ num_selected = 0;
}
}
else
@@ -284,82 +254,111 @@ void LLFloaterFriends::refreshRightsChangeList(U8 state)
}
}
- if(state == 1)
- {
- if(friend_status && !friend_status->isOnline())
- {
- childSetEnabled("offer_teleport_btn", false);
- }
- can_change_visibility = true;
- can_change_modify = true;
- }
- else if (state == 2)
+ const LLRelationship* friend_status = NULL;
+ for(LLDynamicArray<LLUUID>::iterator itr = friends.begin(); itr != friends.end(); ++itr)
{
- can_change_visibility = true;
- can_change_modify = false;
- for(LLDynamicArray<LLUUID>::iterator itr = friends.begin(); itr != friends.end(); ++itr)
+ friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
+ if (friend_status)
{
- friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
+ bool can_see_online = friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS);
+ bool can_see_on_map = friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION);
+ bool can_modify_objects = friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);
+
+ // aggregate rights of this friend into total selection
+ friends_see_online &= can_see_online;
+ friends_see_on_map &= can_see_on_map;
+ friends_modify_objects &= can_modify_objects;
+
+ // can at least one of your selected friends do any of these?
+ some_friends_see_online |= can_see_online;
+ some_friends_see_on_map |= can_see_on_map;
+ some_friends_modify_objects |= can_modify_objects;
+
if(!friend_status->isOnline())
{
- childSetEnabled("offer_teleport_btn", false);
- }
- if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
- {
- can_change_online_multiple = false;
- can_change_map_multiple = false;
- }
- else if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))
- {
- can_change_map_multiple = false;
+ can_offer_teleport = false;
+ selected_friends_online = false;
}
}
-
+ else // missing buddy info, don't allow any operations
+ {
+ can_offer_teleport = false;
+
+ friends_see_online = false;
+ friends_see_on_map = false;
+ friends_modify_objects = false;
+
+ some_friends_see_online = false;
+ some_friends_see_on_map = false;
+ some_friends_modify_objects = false;
+ }
}
- LLCheckboxCtrl* check;
+ // seeing a friend on the map requires seeing online status as a prerequisite
+ friends_see_on_map &= friends_see_online;
+
mMenuState = 0;
- check = LLUICtrlFactory::getCheckBoxByName(this, "online_status_cb");
- check->setEnabled(can_change_visibility);
- check->set(FALSE);
- if(!mAllowRightsChange) check->setVisible(FALSE);
- else check->setVisible(TRUE);
- if(friend_status)
+ // make checkboxes visible after we have finished processing rights
+ childSetVisible("online_status_cb", mAllowRightsChange);
+ childSetVisible("map_status_cb", mAllowRightsChange);
+ childSetVisible("modify_status_cb", mAllowRightsChange);
+
+ if (num_selected == 0) // nothing selected
{
- check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_online_multiple);
- if(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) mMenuState |= LLRelationship::GRANT_ONLINE_STATUS;
+ childSetEnabled("im_btn", FALSE);
+ childSetEnabled("offer_teleport_btn", FALSE);
+
+ childSetEnabled("online_status_cb", FALSE);
+ childSetValue("online_status_cb", FALSE);
+ childSetTentative("online_status_cb", FALSE);
+
+ childSetEnabled("map_status_cb", FALSE);
+ childSetValue("map_status_cb", FALSE);
+ childSetTentative("map_status_cb", FALSE);
+
+ childSetEnabled("modify_status_cb", FALSE);
+ childSetValue("modify_status_cb", FALSE);
+ childSetTentative("modify_status_cb", FALSE);
}
-
- check = LLUICtrlFactory::getCheckBoxByName(this, "map_status_cb");
- check->setEnabled(false);
- check->set(FALSE);
- if(!mAllowRightsChange) check->setVisible(FALSE);
- else check->setVisible(TRUE);
- if(friend_status)
+ else // we have at least one friend selected...
{
- check->setEnabled(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_visibility && can_change_online_multiple);
- check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) && can_change_map_multiple);
- if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION)) mMenuState |= LLRelationship::GRANT_MAP_LOCATION;
- }
+ // only allow IMs to groups when everyone in the group is online
+ // to be consistent with context menus in inventory and because otherwise
+ // offline friends would be silently dropped from the session
+ childSetEnabled("im_btn", selected_friends_online || num_selected == 1);
- check = LLUICtrlFactory::getCheckBoxByName(this, "modify_status_cb");
- check->setEnabled(can_change_modify);
- check->set(FALSE);
- if(!mAllowRightsChange) check->setVisible(FALSE);
- else check->setVisible(TRUE);
- if(can_change_modify)
- {
- if(friend_status)
+ childSetEnabled("offer_teleport_btn", can_offer_teleport);
+
+ childSetEnabled("online_status_cb", TRUE);
+ childSetValue("online_status_cb", some_friends_see_online);
+ childSetTentative("online_status_cb", some_friends_see_online != friends_see_online);
+ if (friends_see_online)
+ {
+ mMenuState |= LLRelationship::GRANT_ONLINE_STATUS;
+ }
+
+ childSetEnabled("map_status_cb", TRUE);
+ childSetValue("map_status_cb", some_friends_see_on_map);
+ childSetTentative("map_status_cb", some_friends_see_on_map != friends_see_on_map);
+ if(friends_see_on_map)
+ {
+ mMenuState |= LLRelationship::GRANT_MAP_LOCATION;
+ }
+
+ // for now, don't allow modify rights change for multiple select
+ childSetEnabled("modify_status_cb", num_selected == 1);
+ childSetValue("modify_status_cb", some_friends_modify_objects);
+ childSetTentative("modify_status_cb", some_friends_modify_objects != friends_modify_objects);
+ if(friends_modify_objects)
{
- check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
- if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)) mMenuState |= LLRelationship::GRANT_MODIFY_OBJECTS;
+ mMenuState |= LLRelationship::GRANT_MODIFY_OBJECTS;
}
}
}
-void LLFloaterFriends::refreshNames()
+void LLPanelFriends::refreshNames()
{
LLDynamicArray<LLUUID> selected_ids = getSelectedIDs();
S32 pos = mFriendsList->getScrollPos();
@@ -389,7 +388,7 @@ void LLFloaterFriends::refreshNames()
}
-void LLFloaterFriends::refreshUI()
+void LLPanelFriends::refreshUI()
{
BOOL single_selected = FALSE;
BOOL multiple_selected = FALSE;
@@ -423,40 +422,34 @@ void LLFloaterFriends::refreshUI()
childSetEnabled("im_btn", single_selected);
childSetEnabled("friend_rights", single_selected);
- //Note: We reset this in refreshRightsChangeList since we already have to iterate
- //through all selected friends there
- childSetEnabled("offer_teleport_btn", single_selected);
-
- refreshRightsChangeList((single_selected + multiple_selected));
+ refreshRightsChangeList();
}
-// static
-LLDynamicArray<LLUUID> LLFloaterFriends::getSelectedIDs()
+LLDynamicArray<LLUUID> LLPanelFriends::getSelectedIDs()
{
LLUUID selected_id;
LLDynamicArray<LLUUID> friend_ids;
- if(sInstance)
+ std::vector<LLScrollListItem*> selected = mFriendsList->getAllSelected();
+ for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
{
- std::vector<LLScrollListItem*> selected = sInstance->mFriendsList->getAllSelected();
- for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
- {
- friend_ids.push_back((*itr)->getUUID());
- }
+ friend_ids.push_back((*itr)->getUUID());
}
return friend_ids;
}
// static
-void LLFloaterFriends::onSelectName(LLUICtrl* ctrl, void* user_data)
+void LLPanelFriends::onSelectName(LLUICtrl* ctrl, void* user_data)
{
- if(sInstance)
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ if(panelp)
{
- sInstance->refreshUI();
+ panelp->refreshUI();
}
}
//static
-void LLFloaterFriends::onMaximumSelect(void* user_data)
+void LLPanelFriends::onMaximumSelect(void* user_data)
{
LLString::format_map_t args;
args["[MAX_SELECT]"] = llformat("%d", MAX_FRIEND_SELECT);
@@ -464,10 +457,12 @@ void LLFloaterFriends::onMaximumSelect(void* user_data)
};
// static
-void LLFloaterFriends::onClickProfile(void* user_data)
+void LLPanelFriends::onClickProfile(void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickProfile()" << llendl;
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ //llinfos << "LLPanelFriends::onClickProfile()" << llendl;
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() > 0)
{
LLUUID agent_id = ids[0];
@@ -478,10 +473,12 @@ void LLFloaterFriends::onClickProfile(void* user_data)
}
// static
-void LLFloaterFriends::onClickIM(void* user_data)
+void LLPanelFriends::onClickIM(void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickIM()" << llendl;
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ //llinfos << "LLPanelFriends::onClickIM()" << llendl;
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() > 0)
{
if(ids.size() == 1)
@@ -494,8 +491,8 @@ void LLFloaterFriends::onClickIM(void* user_data)
{
char buffer[MAX_STRING]; /* Flawfinder: ignore */
snprintf(buffer, MAX_STRING, "%s %s", first, last); /* Flawfinder: ignore */
- gIMView->setFloaterOpen(TRUE);
- gIMView->addSession(
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession(
buffer,
IM_NOTHING_SPECIAL,
agent_id);
@@ -503,17 +500,18 @@ void LLFloaterFriends::onClickIM(void* user_data)
}
else
{
- gIMView->setFloaterOpen(TRUE);
- gIMView->addSession("Friends Conference",
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession("Friends Conference",
IM_SESSION_CONFERENCE_START,
ids[0],
ids);
}
+ make_ui_sound("UISndStartIM");
}
}
// static
-void LLFloaterFriends::requestFriendship(const LLUUID& target_id, const LLString& target_name)
+void LLPanelFriends::requestFriendship(const LLUUID& target_id, const LLString& target_name)
{
// HACK: folder id stored as "message"
LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
@@ -532,7 +530,7 @@ struct LLAddFriendData
};
// static
-void LLFloaterFriends::callbackAddFriend(S32 option, void* data)
+void LLPanelFriends::callbackAddFriend(S32 option, void* data)
{
LLAddFriendData* add = (LLAddFriendData*)data;
if (option == 0)
@@ -543,7 +541,7 @@ void LLFloaterFriends::callbackAddFriend(S32 option, void* data)
}
// static
-void LLFloaterFriends::onPickAvatar(const std::vector<std::string>& names,
+void LLPanelFriends::onPickAvatar(const std::vector<std::string>& names,
const std::vector<LLUUID>& ids,
void* )
{
@@ -553,7 +551,7 @@ void LLFloaterFriends::onPickAvatar(const std::vector<std::string>& names,
}
// static
-void LLFloaterFriends::requestFriendshipDialog(const LLUUID& id,
+void LLPanelFriends::requestFriendshipDialog(const LLUUID& id,
const std::string& name)
{
if(id == gAgentID)
@@ -573,16 +571,24 @@ void LLFloaterFriends::requestFriendshipDialog(const LLUUID& id,
}
// static
-void LLFloaterFriends::onClickAddFriend(void* user_data)
+void LLPanelFriends::onClickAddFriend(void* user_data)
{
- LLFloaterAvatarPicker::show(onPickAvatar, user_data, FALSE, TRUE);
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+ LLFloater* root_floater = gFloaterView->getParentFloater(panelp);
+ LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(onPickAvatar, user_data, FALSE, TRUE);
+ if (root_floater)
+ {
+ root_floater->addDependentFloater(picker);
+ }
}
// static
-void LLFloaterFriends::onClickRemove(void* user_data)
+void LLPanelFriends::onClickRemove(void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickRemove()" << llendl;
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ //llinfos << "LLPanelFriends::onClickRemove()" << llendl;
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
LLStringBase<char>::format_map_t args;
if(ids.size() > 0)
{
@@ -610,9 +616,11 @@ void LLFloaterFriends::onClickRemove(void* user_data)
}
// static
-void LLFloaterFriends::onClickOfferTeleport(void*)
+void LLPanelFriends::onClickOfferTeleport(void* user_data)
{
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() > 0)
{
handle_lure(ids);
@@ -620,43 +628,40 @@ void LLFloaterFriends::onClickOfferTeleport(void*)
}
// static
-void LLFloaterFriends::onClickPay(void*)
+void LLPanelFriends::onClickPay(void* user_data)
{
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() == 1)
{
handle_pay_by_id(ids[0]);
}
}
-// static
-void LLFloaterFriends::onClickClose(void* user_data)
+void LLPanelFriends::onClickOnlineStatus(LLUICtrl* ctrl, void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickClose()" << llendl;
- if(sInstance)
- {
- sInstance->onClose(false);
- }
-}
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
-void LLFloaterFriends::onClickOnlineStatus(LLUICtrl* ctrl, void* user_data)
-{
bool checked = ctrl->getValue();
- sInstance->updateMenuState(LLRelationship::GRANT_ONLINE_STATUS, checked);
- sInstance->applyRightsToFriends(LLRelationship::GRANT_ONLINE_STATUS, checked);
+ panelp->updateMenuState(LLRelationship::GRANT_ONLINE_STATUS, checked);
+ panelp->applyRightsToFriends(LLRelationship::GRANT_ONLINE_STATUS, checked);
}
-void LLFloaterFriends::onClickMapStatus(LLUICtrl* ctrl, void* user_data)
+void LLPanelFriends::onClickMapStatus(LLUICtrl* ctrl, void* user_data)
{
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
bool checked = ctrl->getValue();
- sInstance->updateMenuState(LLRelationship::GRANT_MAP_LOCATION, checked);
- sInstance->applyRightsToFriends(LLRelationship::GRANT_MAP_LOCATION, checked);
+ panelp->updateMenuState(LLRelationship::GRANT_MAP_LOCATION, checked);
+ panelp->applyRightsToFriends(LLRelationship::GRANT_MAP_LOCATION, checked);
}
-void LLFloaterFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
+void LLPanelFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
{
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
bool checked = ctrl->getValue();
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
LLStringBase<char>::format_map_t args;
if(ids.size() > 0)
{
@@ -670,33 +675,35 @@ void LLFloaterFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
args["[FIRST_NAME]"] = first;
args["[LAST_NAME]"] = last;
}
- if(checked) gViewerWindow->alertXml("GrantModifyRights", args, handleModifyRights, NULL);
- else gViewerWindow->alertXml("RevokeModifyRights", args, handleModifyRights, NULL);
+ if(checked) gViewerWindow->alertXml("GrantModifyRights", args, handleModifyRights, user_data);
+ else gViewerWindow->alertXml("RevokeModifyRights", args, handleModifyRights, user_data);
}
else return;
}
}
-void LLFloaterFriends::handleModifyRights(S32 option, void* user_data)
+void LLPanelFriends::handleModifyRights(S32 option, void* user_data)
{
- if(sInstance)
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ if(panelp)
{
if(!option)
{
- sInstance->updateMenuState(LLRelationship::GRANT_MODIFY_OBJECTS, !((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
- sInstance->applyRightsToFriends(LLRelationship::GRANT_MODIFY_OBJECTS, ((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ panelp->updateMenuState(LLRelationship::GRANT_MODIFY_OBJECTS, !((panelp->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ panelp->applyRightsToFriends(LLRelationship::GRANT_MODIFY_OBJECTS, ((panelp->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
}
- sInstance->refreshUI();
+ panelp->refreshUI();
}
}
-void LLFloaterFriends::updateMenuState(S32 flag, BOOL value)
+void LLPanelFriends::updateMenuState(S32 flag, BOOL value)
{
if(value) mMenuState |= flag;
else mMenuState &= ~flag;
}
-void LLFloaterFriends::applyRightsToFriends(S32 flag, BOOL value)
+void LLPanelFriends::applyRightsToFriends(S32 flag, BOOL value)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_GrantUserRights);
@@ -725,7 +732,7 @@ void LLFloaterFriends::applyRightsToFriends(S32 flag, BOOL value)
// static
-void LLFloaterFriends::handleRemove(S32 option, void* user_data)
+void LLPanelFriends::handleRemove(S32 option, void* user_data)
{
LLDynamicArray<LLUUID>* ids = static_cast<LLDynamicArray<LLUUID>*>(user_data);
for(LLDynamicArray<LLUUID>::iterator itr = ids->begin(); itr != ids->end(); ++itr)
diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h
index c6ef365517..6895e469e5 100644
--- a/indra/newview/llfloaterfriends.h
+++ b/indra/newview/llfloaterfriends.h
@@ -11,7 +11,7 @@
#ifndef LL_LLFLOATERFRIENDS_H
#define LL_LLFLOATERFRIENDS_H
-#include "llfloater.h"
+#include "llpanel.h"
#include "llstring.h"
#include "lluuid.h"
#include "lltimer.h"
@@ -20,24 +20,23 @@ class LLFriendObserver;
/**
- * @class LLFloaterFriends
+ * @class LLPanelFriends
* @brief An instance of this class is used for displaying your friends
* and gives you quick access to all agents which a user relationship.
*
* @sa LLFloater
*/
-class LLFloaterFriends : public LLFloater, public LLEventTimer
+class LLPanelFriends : public LLPanel, public LLEventTimer
{
public:
- virtual ~LLFloaterFriends();
+ LLPanelFriends();
+ virtual ~LLPanelFriends();
/**
* @brief This method either creates or brings to the front the
* current instantiation of this floater. There is only once since
* you can currently only look at your local friends.
*/
- static void show(void* ignored = NULL);
-
virtual void tick();
/**
@@ -48,11 +47,6 @@ public:
virtual BOOL postBuild();
- static BOOL visible(void* unused = NULL);
-
- // Toggles visibility of floater
- static void toggle(void* unused = NULL);
-
// Show a dialog explaining what friendship entails, then request
// friendship. JC
static void requestFriendshipDialog(const LLUUID& target_id,
@@ -75,19 +69,18 @@ private:
};
// protected members
- LLFloaterFriends();
void reloadNames();
void refreshNames();
void refreshUI();
- void refreshRightsChangeList(U8 state);
+ void refreshRightsChangeList();
void applyRightsToFriends(S32 flag, BOOL value);
void updateMenuState(S32 flag, BOOL value);
S32 getMenuState() { return mMenuState; }
void addFriend(const std::string& name, const LLUUID& agent_id);
// return LLUUID::null if nothing is selected
- static LLDynamicArray<LLUUID> getSelectedIDs();
+ LLDynamicArray<LLUUID> getSelectedIDs();
// callback methods
static void onSelectName(LLUICtrl* ctrl, void* user_data);
@@ -103,8 +96,6 @@ private:
static void onClickOfferTeleport(void* user_data);
static void onClickPay(void* user_data);
- static void onClickClose(void* user_data);
-
static void onClickOnlineStatus(LLUICtrl* ctrl, void* user_data);
static void onClickMapStatus(LLUICtrl* ctrl, void* user_data);
static void onClickModifyStatus(LLUICtrl* ctrl, void* user_data);
@@ -113,9 +104,6 @@ private:
static void handleModifyRights(S32 option, void* user_data);
private:
- // static data
- static LLFloaterFriends* sInstance;
-
// member data
LLFriendObserver* mObserver;
LLUUID mAddFriendID;
diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp
index 4535a11ec3..60cfa3b809 100644
--- a/indra/newview/llfloatergroups.cpp
+++ b/indra/newview/llfloatergroups.cpp
@@ -1,6 +1,6 @@
/**
* @file llfloatergroups.cpp
- * @brief LLFloaterGroups class implementation
+ * @brief LLPanelGroups class implementation
*
* Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
* $License$
@@ -30,115 +30,135 @@
#include "lltextbox.h"
#include "llvieweruictrlfactory.h"
#include "llviewerwindow.h"
-
-const LLRect FLOATER_RECT(0, 258, 280, 0);
-const char FLOATER_TITLE[] = "Groups";
+#include "llimview.h"
// static
-LLMap<const LLUUID, LLFloaterGroups*> LLFloaterGroups::sInstances;
+std::map<const LLUUID, LLFloaterGroupPicker*> LLFloaterGroupPicker::sInstances;
+// helper functions
+void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id);
///----------------------------------------------------------------------------
-/// Class LLFloaterGroups
+/// Class LLFloaterGroupPicker
///----------------------------------------------------------------------------
-//LLEventListener
-//virtual
-bool LLFloaterGroups::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+// static
+LLFloaterGroupPicker* LLFloaterGroupPicker::findInstance(const LLSD& seed)
{
- if (event->desc() == "new group")
+ instance_map_t::iterator found_it = sInstances.find(seed.asUUID());
+ if (found_it != sInstances.end())
{
- reset();
- return true;
+ return found_it->second;
}
-
- return LLView::handleEvent(event, userdata);
+ return NULL;
}
-// Call this with an agent id and AGENT_GROUPS for an agent's
-// groups, otherwise, call with an object id and SET_OBJECT_GROUP
-// when modifying an object.
// static
-LLFloaterGroups* LLFloaterGroups::show(const LLUUID& id, EGroupDialog type)
+LLFloaterGroupPicker* LLFloaterGroupPicker::createInstance(const LLSD &seed)
+{
+ LLFloaterGroupPicker* pickerp = new LLFloaterGroupPicker(seed);
+ gUICtrlFactory->buildFloater(pickerp, "floater_choose_group.xml");
+ return pickerp;
+}
+
+LLFloaterGroupPicker::LLFloaterGroupPicker(const LLSD& seed) :
+ mSelectCallback(NULL),
+ mCallbackUserdata(NULL)
+{
+ mID = seed.asUUID();
+ sInstances.insert(std::make_pair(mID, this));
+}
+
+LLFloaterGroupPicker::~LLFloaterGroupPicker()
+{
+ sInstances.erase(mID);
+}
+
+void LLFloaterGroupPicker::setSelectCallback(void (*callback)(LLUUID, void*),
+ void* userdata)
{
- LLFloaterGroups* instance = NULL;
- if(sInstances.checkData(id))
+ mSelectCallback = callback;
+ mCallbackUserdata = userdata;
+}
+
+BOOL LLFloaterGroupPicker::postBuild()
+{
+ init_group_list(LLUICtrlFactory::getScrollListByName(this, "group list"), gAgent.getGroupID());
+
+ childSetAction("OK", onBtnOK, this);
+
+ childSetAction("Cancel", onBtnCancel, this);
+
+ setDefaultBtn("OK");
+
+ childSetDoubleClickCallback("group list", onBtnOK);
+ childSetUserData("group list", this);
+
+ childEnable("OK");
+
+ return TRUE;
+}
+
+void LLFloaterGroupPicker::onBtnOK(void* userdata)
+{
+ LLFloaterGroupPicker* self = (LLFloaterGroupPicker*)userdata;
+ if(self) self->ok();
+}
+
+void LLFloaterGroupPicker::onBtnCancel(void* userdata)
+{
+ LLFloaterGroupPicker* self = (LLFloaterGroupPicker*)userdata;
+ if(self) self->close();
+}
+
+
+void LLFloaterGroupPicker::ok()
+{
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
{
- instance = sInstances.getData(id);
- if (instance->getType() != type)
- {
- // not the type we want ==> destroy it and rebuild below
- instance->destroy();
- instance = NULL;
- }
- else
- {
- // Move the existing view to the front
- instance->open(); /* Flawfinder: ignore */
- }
+ group_id = group_list->getCurrentID();
}
-
- if (!instance)
+ if(mSelectCallback)
{
- S32 left = 0;
- S32 top = 0;
- LLRect rect = FLOATER_RECT;
- rect.translate( left - rect.mLeft, top - rect.mTop );
- instance = new LLFloaterGroups("groups", rect, FLOATER_TITLE, id);
- if(instance)
- {
- sInstances.addData(id, instance);
- //instance->init(type);
- instance->mType = type;
- switch (type)
- {
- case AGENT_GROUPS:
- gUICtrlFactory->buildFloater(instance, "floater_groups.xml");
- break;
- case CHOOSE_ONE:
- gUICtrlFactory->buildFloater(instance, "floater_choose_group.xml");
- break;
- }
- instance->center();
- instance->open(); /*Flawfinder: ignore*/
- }
+ mSelectCallback(group_id, mCallbackUserdata);
}
- return instance;
+
+ close();
}
-// static
-LLFloaterGroups* LLFloaterGroups::getInstance(const LLUUID& id)
+///----------------------------------------------------------------------------
+/// Class LLPanelGroups
+///----------------------------------------------------------------------------
+
+//LLEventListener
+//virtual
+bool LLPanelGroups::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
- if(sInstances.checkData(id))
+ if (event->desc() == "new group")
{
- return sInstances.getData(id);
+ reset();
+ return true;
}
- return NULL;
+
+ return LLView::handleEvent(event, userdata);
}
// Default constructor
-LLFloaterGroups::LLFloaterGroups(const std::string& name,
- const LLRect& rect,
- const std::string& title,
- const LLUUID& id) :
- LLFloater(name, rect, title),
- mID(id),
- mType(AGENT_GROUPS),
- mOKCallback(NULL),
- mCallbackUserdata(NULL)
+LLPanelGroups::LLPanelGroups() :
+ LLPanel()
{
+ gAgent.addListener(this, "new group");
}
-// Destroys the object
-LLFloaterGroups::~LLFloaterGroups()
+LLPanelGroups::~LLPanelGroups()
{
- gFocusMgr.releaseFocusIfNeeded( this );
-
- sInstances.removeData(mID);
+ gAgent.removeListener(this);
}
// clear the group list, and get a fresh set of info.
-void LLFloaterGroups::reset()
+void LLPanelGroups::reset()
{
LLCtrlListInterface *group_list = childGetListInterface("group list");
if (group_list)
@@ -148,215 +168,126 @@ void LLFloaterGroups::reset()
childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
- initAgentGroups(gAgent.getGroupID());
+ init_group_list(LLUICtrlFactory::getScrollListByName(this, "group list"), gAgent.getGroupID());
enableButtons();
}
-void LLFloaterGroups::setOkCallback(void (*callback)(LLUUID, void*),
- void* userdata)
-{
- mOKCallback = callback;
- mCallbackUserdata = userdata;
-}
-
-BOOL LLFloaterGroups::postBuild()
+BOOL LLPanelGroups::postBuild()
{
childSetCommitCallback("group list", onGroupList, this);
- if(mType == AGENT_GROUPS)
- {
- childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
- childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
-
- initAgentGroups(gAgent.getGroupID());
-
- childSetAction("Activate", onBtnActivate, this);
+ childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+ childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
- childSetAction("Info", onBtnInfo, this);
+ init_group_list(LLUICtrlFactory::getScrollListByName(this, "group list"), gAgent.getGroupID());
- childSetAction("Leave", onBtnLeave, this);
+ childSetAction("Activate", onBtnActivate, this);
- childSetAction("Create", onBtnCreate, this);
+ childSetAction("Info", onBtnInfo, this);
- childSetAction("Search...", onBtnSearch, this);
+ childSetAction("IM", onBtnIM, this);
- childSetAction("Close", onBtnCancel, this);
+ childSetAction("Leave", onBtnLeave, this);
- setDefaultBtn("Info");
+ childSetAction("Create", onBtnCreate, this);
- childSetDoubleClickCallback("group list", onBtnInfo);
- childSetUserData("group list", this);
- }
- else
- {
- initAgentGroups(gAgent.getGroupID());
-
- childSetAction("OK", onBtnOK, this);
+ childSetAction("Search...", onBtnSearch, this);
- childSetAction("Cancel", onBtnCancel, this);
+ setDefaultBtn("IM");
- setDefaultBtn("OK");
+ childSetDoubleClickCallback("group list", onBtnIM);
+ childSetUserData("group list", this);
- childSetDoubleClickCallback("group list", onBtnOK);
- childSetUserData("group list", this);
- }
-
- enableButtons();
+ reset();
return TRUE;
}
-void LLFloaterGroups::initAgentGroups(const LLUUID& highlight_id)
+void LLPanelGroups::enableButtons()
{
- S32 count = gAgent.mGroups.count();
- LLUUID id;
LLCtrlListInterface *group_list = childGetListInterface("group list");
- if (!group_list) return;
-
- group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
-
- for(S32 i = 0; i < count; ++i)
+ LLUUID group_id;
+ if (group_list)
{
- id = gAgent.mGroups.get(i).mID;
- LLGroupData* group_datap = &gAgent.mGroups.get(i);
- LLString style = "NORMAL";
- if(highlight_id == id)
- {
- style = "BOLD";
- }
-
- LLSD element;
- element["id"] = id;
- element["columns"][0]["column"] = "name";
- element["columns"][0]["value"] = group_datap->mName;
- element["columns"][0]["font"] = "SANSSERIF";
- element["columns"][0]["font-style"] = style;
-
- group_list->addElement(element, ADD_SORTED);
+ group_id = group_list->getCurrentID();
}
+ if(group_id != gAgent.getGroupID())
{
- LLString style = "NORMAL";
- if (highlight_id.isNull())
- {
- style = "BOLD";
- }
- LLSD element;
- element["id"] = LLUUID::null;
- element["columns"][0]["column"] = "name";
- element["columns"][0]["value"] = "none";
- element["columns"][0]["font"] = "SANSSERIF";
- element["columns"][0]["font-style"] = style;
-
- group_list->addElement(element, ADD_TOP);
+ childEnable("Activate");
}
-
- group_list->selectByValue(highlight_id);
-
- childSetFocus("group list");
-}
-
-void LLFloaterGroups::enableButtons()
-{
- LLCtrlListInterface *group_list = childGetListInterface("group list");
- LLUUID group_id;
- if (group_list)
+ else
{
- group_id = group_list->getCurrentID();
+ childDisable("Activate");
}
- if(mType == AGENT_GROUPS)
+ if (group_id.notNull())
{
- if(group_id != gAgent.getGroupID())
- {
- childEnable("Activate");
- }
- else
- {
- childDisable("Activate");
- }
- if (group_id.notNull())
- {
- childEnable("Info");
- childEnable("Leave");
- }
- else
- {
- childDisable("Info");
- childDisable("Leave");
- }
- if(gAgent.mGroups.count() < MAX_AGENT_GROUPS)
- {
- childEnable("Create");
- }
- else
- {
- childDisable("Create");
- }
+ childEnable("Info");
+ childEnable("IM");
+ childEnable("Leave");
}
else
{
- childEnable("OK");
+ childDisable("Info");
+ childDisable("IM");
+ childDisable("Leave");
+ }
+ if(gAgent.mGroups.count() < MAX_AGENT_GROUPS)
+ {
+ childEnable("Create");
+ }
+ else
+ {
+ childDisable("Create");
}
}
-void LLFloaterGroups::onBtnCreate(void* userdata)
+void LLPanelGroups::onBtnCreate(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
if(self) self->create();
}
-void LLFloaterGroups::onBtnActivate(void* userdata)
+void LLPanelGroups::onBtnActivate(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
if(self) self->activate();
}
-void LLFloaterGroups::onBtnInfo(void* userdata)
+void LLPanelGroups::onBtnInfo(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
if(self) self->info();
}
-void LLFloaterGroups::onBtnLeave(void* userdata)
+void LLPanelGroups::onBtnIM(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->leave();
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->startIM();
}
-void LLFloaterGroups::onBtnSearch(void* userdata)
+void LLPanelGroups::onBtnLeave(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->search();
-}
-
-void LLFloaterGroups::onBtnOK(void* userdata)
-{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->ok();
-}
-
-void LLFloaterGroups::onBtnCancel(void* userdata)
-{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->close();
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->leave();
}
-void LLFloaterGroups::onGroupList(LLUICtrl* ctrl, void* userdata)
+void LLPanelGroups::onBtnSearch(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->highlightGroupList(ctrl);
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->search();
}
-void LLFloaterGroups::create()
+void LLPanelGroups::create()
{
- llinfos << "LLFloaterGroups::create" << llendl;
+ llinfos << "LLPanelGroups::create" << llendl;
LLFloaterGroupInfo::showCreateGroup(NULL);
}
-void LLFloaterGroups::activate()
+void LLPanelGroups::activate()
{
- llinfos << "LLFloaterGroups::activate" << llendl;
+ llinfos << "LLPanelGroups::activate" << llendl;
LLCtrlListInterface *group_list = childGetListInterface("group list");
LLUUID group_id;
if (group_list)
@@ -372,9 +303,9 @@ void LLFloaterGroups::activate()
gAgent.sendReliableMessage();
}
-void LLFloaterGroups::info()
+void LLPanelGroups::info()
{
- llinfos << "LLFloaterGroups::info" << llendl;
+ llinfos << "LLPanelGroups::info" << llendl;
LLCtrlListInterface *group_list = childGetListInterface("group list");
LLUUID group_id;
if (group_list && (group_id = group_list->getCurrentID()).notNull())
@@ -383,9 +314,36 @@ void LLFloaterGroups::info()
}
}
-void LLFloaterGroups::leave()
+void LLPanelGroups::startIM()
+{
+ //llinfos << "LLPanelFriends::onClickIM()" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+
+ if (group_list && (group_id = group_list->getCurrentID()).notNull())
+ {
+ LLGroupData group_data;
+ if (gAgent.getGroupData(group_id, group_data))
+ {
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession(
+ group_data.mName,
+ IM_SESSION_GROUP_START,
+ group_id);
+ make_ui_sound("UISndStartIM");
+ }
+ else
+ {
+ // this should never happen, as starting a group IM session
+ // relies on you belonging to the group and hence having the group data
+ make_ui_sound("UISndInvalidOp");
+ }
+ }
+}
+
+void LLPanelGroups::leave()
{
- llinfos << "LLFloaterGroups::leave" << llendl;
+ llinfos << "LLPanelGroups::leave" << llendl;
LLCtrlListInterface *group_list = childGetListInterface("group list");
LLUUID group_id;
if (group_list && (group_id = group_list->getCurrentID()).notNull())
@@ -407,13 +365,13 @@ void LLFloaterGroups::leave()
}
}
-void LLFloaterGroups::search()
+void LLPanelGroups::search()
{
LLFloaterDirectory::showGroups();
}
// static
-void LLFloaterGroups::callbackLeaveGroup(S32 option, void* userdata)
+void LLPanelGroups::callbackLeaveGroup(S32 option, void* userdata)
{
LLUUID* group_id = (LLUUID*)userdata;
if(option == 0 && group_id)
@@ -430,25 +388,58 @@ void LLFloaterGroups::callbackLeaveGroup(S32 option, void* userdata)
delete group_id;
}
-void LLFloaterGroups::ok()
+void LLPanelGroups::onGroupList(LLUICtrl* ctrl, void* userdata)
{
- llinfos << "LLFloaterGroups::ok" << llendl;
- LLCtrlListInterface *group_list = childGetListInterface("group list");
- LLUUID group_id;
- if (group_list)
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->enableButtons();
+}
+
+void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id)
+{
+ S32 count = gAgent.mGroups.count();
+ LLUUID id;
+ LLCtrlListInterface *group_list = ctrl->getListInterface();
+ if (!group_list) return;
+
+ group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ for(S32 i = 0; i < count; ++i)
{
- group_id = group_list->getCurrentID();
+ id = gAgent.mGroups.get(i).mID;
+ LLGroupData* group_datap = &gAgent.mGroups.get(i);
+ LLString style = "NORMAL";
+ if(highlight_id == id)
+ {
+ style = "BOLD";
+ }
+
+ LLSD element;
+ element["id"] = id;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = group_datap->mName;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_SORTED);
}
- if(mOKCallback)
+
+ // add "none" to list at top
{
- mOKCallback(group_id, mCallbackUserdata);
+ LLString style = "NORMAL";
+ if (highlight_id.isNull())
+ {
+ style = "BOLD";
+ }
+ LLSD element;
+ element["id"] = LLUUID::null;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = "none";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_TOP);
}
- close();
+ group_list->selectByValue(highlight_id);
}
-void LLFloaterGroups::highlightGroupList(LLUICtrl*)
-{
- llinfos << "LLFloaterGroups::highlightGroupList" << llendl;
- enableButtons();
-}
diff --git a/indra/newview/llfloatergroups.h b/indra/newview/llfloatergroups.h
index 6f73c97553..65bed95ea8 100644
--- a/indra/newview/llfloatergroups.h
+++ b/indra/newview/llfloatergroups.h
@@ -21,89 +21,80 @@
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "lluuid.h"
-#include "llmap.h"
-#include "llevent.h"
#include "llfloater.h"
+#include <map>
class LLUICtrl;
class LLTextBox;
class LLScrollListCtrl;
class LLButton;
+class LLFloaterGroupPicker;
-class LLFloaterGroups : public LLFloater
+class LLFloaterGroupPicker : public LLFloater, public LLUIInstanceMgr<LLFloaterGroupPicker>
{
+ friend class LLUIInstanceMgr<LLFloaterGroupPicker>;
public:
+ ~LLFloaterGroupPicker();
+ void setSelectCallback( void (*callback)(LLUUID, void*),
+ void* userdata);
+ BOOL postBuild();
+
+protected:
+ LLFloaterGroupPicker(const LLSD& seed);
+ void ok();
+ static LLFloaterGroupPicker* findInstance(const LLSD& seed);
+ static LLFloaterGroupPicker* createInstance(const LLSD& seed);
+ static void onBtnOK(void* userdata);
+ static void onBtnCancel(void* userdata);
+
+protected:
+ LLUUID mID;
+ void (*mSelectCallback)(LLUUID id, void* userdata);
+ void* mCallbackUserdata;
+
+ typedef std::map<const LLUUID, LLFloaterGroupPicker*> instance_map_t;
+ static instance_map_t sInstances;
+};
+
+class LLPanelGroups : public LLPanel
+{
+public:
+ LLPanelGroups();
+ virtual ~LLPanelGroups();
+
//LLEventListener
/*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
- enum EGroupDialog
- {
- AGENT_GROUPS,
- CHOOSE_ONE
- };
- // Call this with an agent id and AGENT_GROUPS for an agent's
- // groups, otherwise, call with an object id and SET_OBJECT_GROUP
- // when modifying an object.
- static LLFloaterGroups* show(const LLUUID& id, EGroupDialog type);
-
- // Return the instance requested if it already exists. Otherwise,
- // return NULL.
- static LLFloaterGroups* getInstance(const LLUUID& id);
-
// clear the group list, and get a fresh set of info.
void reset();
- void setOkCallback( void (*callback)(LLUUID, void*),
- void* userdata);
-
- EGroupDialog getType() const { return mType; }
-
protected:
// initialize based on the type
BOOL postBuild();
// highlight_id is a group id to highlight
- void initAgentGroups(const LLUUID& highlight_id);
void enableButtons();
+ static void onGroupList(LLUICtrl* ctrl, void* userdata);
static void onBtnCreate(void* userdata);
static void onBtnActivate(void* userdata);
static void onBtnInfo(void* userdata);
+ static void onBtnIM(void* userdata);
static void onBtnLeave(void* userdata);
static void onBtnSearch(void* userdata);
static void onBtnVote(void* userdata);
- static void onBtnOK(void* userdata);
- static void onBtnCancel(void* userdata);
- static void onGroupList(LLUICtrl* ctrl, void* userdata);
static void onDoubleClickGroup(void* userdata);
void create();
void activate();
void info();
+ void startIM();
void leave();
void search();
void callVote();
- void ok();
- void highlightGroupList(LLUICtrl*);
static void callbackLeaveGroup(S32 option, void* userdata);
-protected:
- LLUUID mID;
-
- EGroupDialog mType;
-
- void (*mOKCallback)(LLUUID id, void* userdata);
- void* mCallbackUserdata;
-
-protected:
- static LLMap<const LLUUID, LLFloaterGroups*> sInstances;
-
-public:
- // do not call these directly
- LLFloaterGroups(const std::string& name, const LLRect& rect, const std::string& title,
- const LLUUID& id);
- virtual ~LLFloaterGroups();
};
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 4b18c0c91a..421066688e 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -22,6 +22,7 @@
#include "llfloateravatarpicker.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
+#include "llradiogroup.h"
#include "llcombobox.h"
#include "llfloaterauction.h"
#include "llfloateravatarinfo.h"
@@ -90,6 +91,14 @@ static const char RAW_HTML[] = "Raw HTML";
static const BOOL BUY_GROUP_LAND = TRUE;
static const BOOL BUY_PERSONAL_LAND = FALSE;
+// Values for the parcel voice settings radio group
+enum
+{
+ kRadioVoiceChatEstate = 0,
+ kRadioVoiceChatPrivate = 1,
+ kRadioVoiceChatDisable = 2
+};
+
// Statics
LLFloaterLand* LLFloaterLand::sInstance = NULL;
LLParcelSelectionObserver* LLFloaterLand::sObserver = NULL;
@@ -808,9 +817,20 @@ void LLPanelLandGeneral::draw()
// static
void LLPanelLandGeneral::onClickSetGroup(void* userdata)
{
- LLFloaterGroups* fg;
- fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
- fg->setOkCallback( cbGroupID, userdata );
+ LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)userdata;
+ LLFloaterGroupPicker* fg;
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+
+ fg = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID()));
+ fg->setSelectCallback( cbGroupID, userdata );
+
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, fg);
+ fg->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(fg);
+ }
}
// static
@@ -2319,6 +2339,9 @@ BOOL LLPanelLandMedia::postBuild()
mCheckSoundLocal = LLUICtrlFactory::getCheckBoxByName(this, "check sound local");
childSetCommitCallback("check sound local", onCommitAny, this);
+ mRadioVoiceChat = LLUICtrlFactory::getRadioGroupByName(this, "parcel_voice_channel");
+ childSetCommitCallback("parcel_voice_channel", onCommitAny, this);
+
mMusicURLEdit = LLUICtrlFactory::getLineEditorByName(this, "music_url");
childSetCommitCallback("music_url", onCommitAny, this);
@@ -2362,6 +2385,9 @@ void LLPanelLandMedia::refresh()
mCheckSoundLocal->set(FALSE);
mCheckSoundLocal->setEnabled(FALSE);
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatEstate);
+ mRadioVoiceChat->setEnabled(FALSE);
+
mMusicURLEdit->setText("");
mMusicURLEdit->setEnabled(FALSE);
@@ -2389,6 +2415,20 @@ void LLPanelLandMedia::refresh()
mCheckSoundLocal->set( parcel->getSoundLocal() );
mCheckSoundLocal->setEnabled( can_change_media );
+ if(parcel->getVoiceEnabled())
+ {
+ if(parcel->getVoiceUseEstateChannel())
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatEstate);
+ else
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatPrivate);
+ }
+ else
+ {
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatDisable);
+ }
+
+ mRadioVoiceChat->setEnabled( can_change_media );
+
// don't display urls if you're not able to change it
// much requested change in forums so people can't 'steal' urls
// NOTE: bug#2009 means this is still vunerable - however, bug
@@ -2448,16 +2488,39 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl *ctrl, void *userdata)
// Extract data from UI
BOOL sound_local = self->mCheckSoundLocal->get();
+ int voice_setting = self->mRadioVoiceChat->getSelectedIndex();
std::string music_url = self->mMusicURLEdit->getText();
std::string media_url = self->mMediaURLEdit->getText();
U8 media_auto_scale = self->mMediaAutoScaleCheck->get();
LLUUID media_id = self->mMediaTextureCtrl->getImageAssetID();
+ BOOL voice_enabled;
+ BOOL voice_estate_chan;
+
+ switch(voice_setting)
+ {
+ default:
+ case kRadioVoiceChatEstate:
+ voice_enabled = TRUE;
+ voice_estate_chan = TRUE;
+ break;
+ case kRadioVoiceChatPrivate:
+ voice_enabled = TRUE;
+ voice_estate_chan = FALSE;
+ break;
+ case kRadioVoiceChatDisable:
+ voice_enabled = FALSE;
+ voice_estate_chan = FALSE;
+ break;
+ }
+
// Remove leading/trailing whitespace (common when copying/pasting)
LLString::trim(music_url);
LLString::trim(media_url);
// Push data into current parcel
+ parcel->setParcelFlag(PF_ALLOW_VOICE_CHAT, voice_enabled);
+ parcel->setParcelFlag(PF_USE_ESTATE_VOICE_CHAN, voice_estate_chan);
parcel->setParcelFlag(PF_SOUND_LOCAL, sound_local);
parcel->setMusicURL(music_url.c_str());
parcel->setMediaURL(media_url.c_str());
diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h
index 6c0fb7319f..ab7ff3921c 100644
--- a/indra/newview/llfloaterland.h
+++ b/indra/newview/llfloaterland.h
@@ -21,6 +21,7 @@ const F32 CACHE_REFRESH_TIME = 2.5f;
class LLTextBox;
class LLCheckBoxCtrl;
+class LLRadioGroup;
class LLComboBox;
class LLButton;
class LLNameListCtrl;
@@ -348,6 +349,7 @@ public:
protected:
LLCheckBoxCtrl* mCheckSoundLocal;
+ LLRadioGroup* mRadioVoiceChat;
LLLineEditor* mMusicURLEdit;
LLLineEditor* mMediaURLEdit;
LLTextureCtrl* mMediaTextureCtrl;
diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp
index 602a198a03..2553c36f66 100644
--- a/indra/newview/llfloaternamedesc.cpp
+++ b/indra/newview/llfloaternamedesc.cpp
@@ -193,7 +193,7 @@ void LLFloaterNameDesc::onBtnOK( void* userdata )
fp->childGetValue("name_form").asString(),
fp->childGetValue("description_form").asString(),
bitrate, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
- fp->onClose(false);
+ fp->close(false);
}
// static
@@ -203,5 +203,5 @@ void LLFloaterNameDesc::onBtnOK( void* userdata )
void LLFloaterNameDesc::onBtnCancel( void* userdata )
{
LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata;
- fp->onClose(false);
+ fp->close(false);
}
diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp
index 3eaff0101c..e44dfe7d85 100644
--- a/indra/newview/llfloaterpostcard.cpp
+++ b/indra/newview/llfloaterpostcard.cpp
@@ -203,7 +203,7 @@ void LLFloaterPostcard::onClickCancel(void* data)
{
LLFloaterPostcard *self = (LLFloaterPostcard *)data;
- self->onClose(false);
+ self->close(false);
}
}
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 8a1a0dcaaa..ef68e5bec2 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -34,6 +34,7 @@
#include "llpanelmsgs.h"
#include "llpanelweb.h"
#include "llprefschat.h"
+#include "llprefsvoice.h"
#include "llprefsim.h"
#include "llresizehandle.h"
#include "llresmgr.h"
@@ -124,6 +125,10 @@ LLPreferenceCore::LLPreferenceCore(LLTabContainerCommon* tab_container, LLButton
mTabContainer->addTabPanel(mPrefsChat->getPanel(), mPrefsChat->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
mPrefsChat->getPanel()->setDefaultBtn(default_btn);
+ mPrefsVoice = new LLPrefsVoice();
+ mTabContainer->addTabPanel(mPrefsVoice, mPrefsVoice->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mPrefsVoice->setDefaultBtn(default_btn);
+
mPrefsIM = new LLPrefsIM();
mTabContainer->addTabPanel(mPrefsIM->getPanel(), mPrefsIM->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
mPrefsIM->getPanel()->setDefaultBtn(default_btn);
@@ -205,8 +210,8 @@ void LLPreferenceCore::apply()
mDisplayPanel->apply();
mDisplayPanel2->apply();
mDisplayPanel3->apply();
- mAudioPanel->apply();
mPrefsChat->apply();
+ mPrefsVoice->apply();
mPrefsIM->apply();
mMsgPanel->apply();
#if LL_LIBXUL_ENABLED
@@ -225,6 +230,7 @@ void LLPreferenceCore::cancel()
mDisplayPanel3->cancel();
mAudioPanel->cancel();
mPrefsChat->cancel();
+ mPrefsVoice->cancel();
mPrefsIM->cancel();
mMsgPanel->cancel();
#if LL_LIBXUL_ENABLED
@@ -368,7 +374,7 @@ void LLFloaterPreference::onBtnOK( void* userdata )
if (fp->canClose())
{
fp->apply();
- fp->onClose(false);
+ fp->close(false);
gSavedSettings.saveToFile( gSettingsFileName, TRUE );
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index dcfa123137..1de960c640 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -29,6 +29,7 @@ class LLPanelNetwork;
class LLPanelWeb;
class LLMessageSystem;
class LLPrefsChat;
+class LLPrefsVoice;
class LLPrefsIM;
class LLPanelMsgs;
class LLScrollListCtrl;
@@ -63,6 +64,7 @@ private:
LLPanelAudioPrefs *mAudioPanel;
// LLPanelDebug *mDebugPanel;
LLPrefsChat *mPrefsChat;
+ LLPrefsVoice *mPrefsVoice;
LLPrefsIM *mPrefsIM;
LLPanelMsgs *mMsgPanel;
LLPanelWeb *mWebPanel;
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 1c287a187a..efbf8a7826 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -1410,11 +1410,21 @@ void LLPanelEstateInfo::addAllowedGroup(S32 option, void* user_data)
{
if (option != 0) return;
- LLFloaterGroups* widget;
- widget = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ LLPanelEstateInfo* panelp = (LLPanelEstateInfo*)user_data;
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+
+ LLFloaterGroupPicker* widget;
+ widget = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID()));
if (widget)
{
- widget->setOkCallback(addAllowedGroup2, user_data);
+ widget->setSelectCallback(addAllowedGroup2, user_data);
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, widget);
+ widget->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(widget);
+ }
}
}
@@ -1910,6 +1920,7 @@ BOOL LLPanelEstateInfo::postBuild()
initCtrl("deny_anonymous");
initCtrl("deny_identified");
initCtrl("deny_transacted");
+ initCtrl("voice_chat_check");
initHelpBtn("estate_manager_help", "HelpEstateEstateManager");
initHelpBtn("use_global_time_help", "HelpEstateUseGlobalTime");
@@ -1919,6 +1930,7 @@ BOOL LLPanelEstateInfo::postBuild()
initHelpBtn("allow_resident_help", "HelpEstateAllowResident");
initHelpBtn("allow_group_help", "HelpEstateAllowGroup");
initHelpBtn("ban_resident_help", "HelpEstateBanResident");
+ initHelpBtn("voice_chat_help", "HelpEstateVoiceChat");
// set up the use global time checkbox
childSetCommitCallback("use_global_time_check", onChangeUseGlobalTime, this);
@@ -2084,6 +2096,9 @@ void LLPanelEstateInfo::setEstateFlags(U32 flags)
{
childSetValue("externally_visible_check", LLSD(flags & REGION_FLAGS_EXTERNALLY_VISIBLE ? TRUE : FALSE) );
childSetValue("fixed_sun_check", LLSD(flags & REGION_FLAGS_SUN_FIXED ? TRUE : FALSE) );
+ childSetValue(
+ "voice_chat_check",
+ LLSD(flags & REGION_FLAGS_ALLOW_VOICE ? TRUE : FALSE));
childSetValue("allow_direct_teleport", LLSD(flags & REGION_FLAGS_ALLOW_DIRECT_TELEPORT ? TRUE : FALSE) );
childSetValue("deny_anonymous", LLSD(flags & REGION_FLAGS_DENY_ANONYMOUS ? TRUE : FALSE) );
childSetValue("deny_identified", LLSD(flags & REGION_FLAGS_DENY_IDENTIFIED ? TRUE : FALSE) );
@@ -2099,6 +2114,11 @@ U32 LLPanelEstateInfo::computeEstateFlags()
{
flags |= REGION_FLAGS_EXTERNALLY_VISIBLE;
}
+
+ if ( childGetValue("voice_chat_check").asBoolean() )
+ {
+ flags |= REGION_FLAGS_ALLOW_VOICE;
+ }
if (childGetValue("allow_direct_teleport").asBoolean())
{
diff --git a/indra/newview/llfloaterscriptdebug.cpp b/indra/newview/llfloaterscriptdebug.cpp
index 0ba75b71f6..4d2f012396 100644
--- a/indra/newview/llfloaterscriptdebug.cpp
+++ b/indra/newview/llfloaterscriptdebug.cpp
@@ -93,7 +93,7 @@ LLFloater* LLFloaterScriptDebug::addOutputWindow(const LLUUID &object_id)
LLFloater::setFloaterHost(NULL);
// Tabs sometimes overlap resize handle
- sInstance->moveResizeHandleToFront();
+ sInstance->moveResizeHandlesToFront();
return floaterp;
}
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 80e32f9c00..5feb8b2f0d 100644
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -361,8 +361,7 @@ void LLSnapshotLivePreview::draw()
F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
// draw "shine" effect
- LLGLEnable scissor_test(GL_SCISSOR_TEST);
- LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+ LLLocalClipRect clip(getLocalRect());
{
// draw diagonal stripe with gradient that passes over screen
S32 x1 = gViewerWindow->getWindowWidth() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
index b708a4261f..0da0958c9b 100644
--- a/indra/newview/llfloatertools.cpp
+++ b/indra/newview/llfloatertools.cpp
@@ -53,7 +53,6 @@
#include "llviewerparcelmgr.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
-#include "llvolumesliderctrl.h"
#include "viewer.h"
#include "llvieweruictrlfactory.h"
@@ -175,7 +174,6 @@ BOOL LLFloaterTools::postBuild()
childSetAction("button land",LLFloaterTools::setEditTool, (void*)gToolParcel);
mTextStatus = LLUICtrlFactory::getTextBoxByName(this,"text status");
mRadioZoom = LLUICtrlFactory::getCheckBoxByName(this,"radio zoom");
- mSliderZoom = LLViewerUICtrlFactory::getVolumeSliderByName(this,"slider zoom");
childSetCommitCallback("slider zoom",commit_slider_zoom,this);
mRadioOrbit = LLUICtrlFactory::getCheckBoxByName(this,"radio orbit");
childSetCommitCallback("radio orbit",commit_radio_orbit,this);
@@ -476,8 +474,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)
mRadioZoom ->setVisible( focus_visible );
mRadioOrbit ->setVisible( focus_visible );
mRadioPan ->setVisible( focus_visible );
- mSliderZoom ->setVisible( focus_visible );
-
+ childSetVisible("slider zoom", focus_visible);
+
mRadioZoom ->set( !gCameraBtnOrbit &&
!gCameraBtnPan &&
!(mask == MASK_ORBIT) &&
@@ -494,7 +492,7 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)
(mask == (MASK_PAN | MASK_ALT)) );
// multiply by correction factor because volume sliders go [0, 0.5]
- mSliderZoom ->setValue( gAgent.getCameraZoomFraction() * 0.5f);
+ childSetValue( "slider zoom", gAgent.getCameraZoomFraction() * 0.5f);
// Move buttons
BOOL move_visible = (tool == gToolGrab);
@@ -833,9 +831,8 @@ void commit_radio_pan(LLUICtrl *, void*)
void commit_slider_zoom(LLUICtrl *ctrl, void*)
{
- LLVolumeSliderCtrl* slider = (LLVolumeSliderCtrl*)ctrl;
// renormalize value, since max "volume" level is 0.5 for some reason
- F32 zoom_level = (F32)slider->getValue().asReal() * 2.f; // / 0.5f;
+ F32 zoom_level = (F32)ctrl->getValue().asReal() * 2.f; // / 0.5f;
gAgent.setCameraZoomFraction(zoom_level);
}
diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h
index 7c85444578..156f768459 100644
--- a/indra/newview/llfloatertools.h
+++ b/indra/newview/llfloatertools.h
@@ -24,7 +24,6 @@ class LLPanelContents;
class LLPanelFace;
class LLPanelLandInfo;
class LLComboBox;
-class LLVolumeSliderCtrl;
class LLParcelSelection;
class LLObjectSelection;
@@ -100,7 +99,6 @@ public:
LLCheckBoxCtrl *mRadioOrbit;
LLCheckBoxCtrl *mRadioZoom;
LLCheckBoxCtrl *mRadioPan;
- LLVolumeSliderCtrl *mSliderZoom;
// Move buttons
LLCheckBoxCtrl *mRadioMove;
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index be1c26381b..3c51d7dcf1 100644
--- a/indra/newview/llgesturemgr.cpp
+++ b/indra/newview/llgesturemgr.cpp
@@ -83,7 +83,7 @@ void LLGestureManager::activateGesture(const LLUUID& item_id)
mDeactivateSimilarNames.clear();
const BOOL inform_server = TRUE;
- const BOOL deactivate_similar = TRUE;
+ const BOOL deactivate_similar = FALSE;
activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar);
}
@@ -403,7 +403,7 @@ void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new
LLLoadInfo* info = new LLLoadInfo;
info->mItemID = item_id;
info->mInformServer = TRUE;
- info->mDeactivateSimilar = TRUE;
+ info->mDeactivateSimilar = FALSE;
const BOOL high_priority = TRUE;
gAssetStorage->getAssetData(asset_id,
@@ -474,6 +474,8 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
LLString cur_token_lower = cur_token;
LLString::toLower(cur_token_lower);
+ // collect gestures that match
+ std::vector <LLMultiGesture *> matching;
item_map_t::iterator it;
for (it = mActive.begin(); it != mActive.end(); ++it)
{
@@ -481,16 +483,32 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
// Gesture asset data might not have arrived yet
if (!gesture) continue;
-
+
if (!stricmp(gesture->mTrigger.c_str(), cur_token_lower.c_str()))
{
+ matching.push_back(gesture);
+ }
+
+ gesture = NULL;
+ }
+
+
+ if (matching.size() > 0)
+ {
+ // choose one at random
+ {
+ S32 random = ll_rand(matching.size());
+
+ gesture = matching[random];
+
playGesture(gesture);
if (!gesture->mReplaceText.empty())
{
if( !first_token )
{
- revised_string->append( " " );
+ if (revised_string)
+ revised_string->append( " " );
}
// Don't muck with the user's capitalization if we don't have to.
@@ -499,30 +517,34 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
LLString::toLower(output_lower);
if( cur_token_lower == output_lower )
{
- revised_string->append( cur_token );
+ if (revised_string)
+ revised_string->append( cur_token );
}
else
{
- revised_string->append( output );
+ if (revised_string)
+ revised_string->append( output );
}
}
found_gestures = TRUE;
- break;
}
- gesture = NULL;
}
}
-
- if( !gesture )
+
+ if(!gesture)
{
+ // This token doesn't match a gesture. Pass it through to the output.
if( !first_token )
{
- revised_string->append( " " );
+ if (revised_string)
+ revised_string->append( " " );
}
- revised_string->append( cur_token );
+ if (revised_string)
+ revised_string->append( cur_token );
}
first_token = FALSE;
+ gesture = NULL;
}
return found_gestures;
}
@@ -530,7 +552,10 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
BOOL LLGestureManager::triggerGesture(KEY key, MASK mask)
{
+ std::vector <LLMultiGesture *> matching;
item_map_t::iterator it;
+
+ // collect matching gestures
for (it = mActive.begin(); it != mActive.end(); ++it)
{
LLMultiGesture* gesture = (*it).second;
@@ -541,10 +566,20 @@ BOOL LLGestureManager::triggerGesture(KEY key, MASK mask)
if (gesture->mKey == key
&& gesture->mMask == mask)
{
- playGesture(gesture);
- return TRUE;
+ matching.push_back(gesture);
}
}
+
+ // choose one and play it
+ if (matching.size() > 0)
+ {
+ U32 random = ll_rand(matching.size());
+
+ LLMultiGesture* gesture = matching[random];
+
+ playGesture(gesture);
+ return TRUE;
+ }
return FALSE;
}
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
index b79dc0687f..c68ca4f265 100644
--- a/indra/newview/llgesturemgr.h
+++ b/indra/newview/llgesturemgr.h
@@ -85,7 +85,7 @@ public:
BOOL triggerGesture(KEY key, MASK mask);
// Trigger all gestures referenced as substrings in this string
- BOOL triggerAndReviseString(const std::string &str, std::string *revised_string);
+ BOOL triggerAndReviseString(const std::string &str, std::string *revised_string = NULL);
// Does some gesture have this key bound?
BOOL isKeyBound(KEY key, MASK mask);
diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp
index 1c8c45a078..0bcbbbb140 100644
--- a/indra/newview/llhudobject.cpp
+++ b/indra/newview/llhudobject.cpp
@@ -22,6 +22,7 @@
#include "llhudeffectlookat.h"
//Ventrella
+#include "llvoicevisualizer.h"
#include "llanimalcontrols.h"
#include "lllocalanimationobject.h"
#include "llcape.h"
@@ -205,6 +206,9 @@ LLHUDObject *LLHUDObject::addHUDObject(const U8 type)
case LL_HUD_EFFECT_LOOKAT:
hud_objectp = new LLHUDEffectLookAt(type);
break;
+ case LL_HUD_EFFECT_VOICE_VISUALIZER:
+ hud_objectp = new LLVoiceVisualizer(type);
+ break;
case LL_HUD_EFFECT_POINTAT:
hud_objectp = new LLHUDEffectPointAt(type);
break;
diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h
index 9bd14796d5..3b5589c05b 100644
--- a/indra/newview/llhudobject.h
+++ b/indra/newview/llhudobject.h
@@ -68,7 +68,8 @@ public:
LL_HUD_EFFECT_SPIRAL,
LL_HUD_EFFECT_EDIT,
LL_HUD_EFFECT_LOOKAT,
- LL_HUD_EFFECT_POINTAT
+ LL_HUD_EFFECT_POINTAT,
+ LL_HUD_EFFECT_VOICE_VISUALIZER // Ventrella
};
protected:
static void sortObjects();
diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp
index aba3401d3c..70065c48d6 100644
--- a/indra/newview/llhudtext.cpp
+++ b/indra/newview/llhudtext.cpp
@@ -839,7 +839,8 @@ void LLHUDText::updateAll()
}
if (src_textp->mSoftScreenRect.rectInRect(&dst_textp->mSoftScreenRect))
{
- LLRectf intersect_rect = src_textp->mSoftScreenRect & dst_textp->mSoftScreenRect;
+ LLRectf intersect_rect = src_textp->mSoftScreenRect;
+ intersect_rect.intersectWith(dst_textp->mSoftScreenRect);
intersect_rect.stretch(-BUFFER_SIZE * 0.5f);
F32 src_center_x = src_textp->mSoftScreenRect.getCenterX();
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);
}
-
diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h
index cb8042e010..dbe09fb396 100644
--- a/indra/newview/llimpanel.h
+++ b/indra/newview/llimpanel.h
@@ -13,15 +13,122 @@
#include "lluuid.h"
#include "lldarray.h"
#include "llinstantmessage.h"
+#include "llvoiceclient.h"
class LLLineEditor;
class LLViewerTextEditor;
class LLInventoryItem;
class LLInventoryCategory;
+class LLIMSpeakerMgr;
+class LLPanelActiveSpeakers;
+
+class LLVoiceChannel : public LLVoiceClientStatusObserver
+{
+public:
+ typedef enum e_voice_channel_state
+ {
+ STATE_NO_CHANNEL_INFO,
+ STATE_ERROR,
+ STATE_HUNG_UP,
+ STATE_READY,
+ STATE_CALL_STARTED,
+ STATE_RINGING,
+ STATE_CONNECTED
+ } EState;
+
+ LLVoiceChannel(const LLUUID& session_id, const LLString& session_name);
+ virtual ~LLVoiceChannel();
+
+ void setChannelInfo(const LLString& uri, const LLString& credentials);
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+
+ virtual void handleStatusChange(EStatusType status);
+ virtual void handleError(EStatusType status);
+ virtual void deactivate();
+ virtual void activate();
+ virtual void getChannelInfo();
+ virtual BOOL isActive();
+ virtual BOOL callStarted();
+ EState getState() { return mState; }
+
+ void updateSessionID(const LLUUID& new_session_id);
+
+ static LLVoiceChannel* getChannelByID(const LLUUID& session_id);
+ static LLVoiceChannel* getChannelByURI(LLString uri);
+ static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; }
+ static void initClass();
+
+protected:
+ void setState(EState state);
+ void setURI(LLString uri);
+
+ LLString mURI;
+ LLString mCredentials;
+ LLUUID mSessionID;
+ EState mState;
+ LLString mSessionName;
+ LLString::format_map_t mNotifyArgs;
+ BOOL mIgnoreNextSessionLeave;
+ LLViewHandle mLoginNotificationHandle;
+
+ typedef std::map<LLUUID, LLVoiceChannel*> voice_channel_map_t;
+ static voice_channel_map_t sVoiceChannelMap;
+
+ typedef std::map<LLString, LLVoiceChannel*> voice_channel_map_uri_t;
+ static voice_channel_map_uri_t sVoiceChannelURIMap;
+
+ static LLVoiceChannel* sCurrentVoiceChannel;
+};
+
+class LLVoiceChannelGroup : public LLVoiceChannel
+{
+public:
+ LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name);
+ virtual ~LLVoiceChannelGroup();
+
+ /*virtual*/ void handleError(EStatusType status);
+ /*virtual*/ void activate();
+ /*virtual*/ void deactivate();
+ /*virtual*/ void getChannelInfo();
+};
+
+class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoiceChannelProximal>
+{
+public:
+ LLVoiceChannelProximal();
+ virtual ~LLVoiceChannelProximal();
+
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+ /*virtual*/ void handleStatusChange(EStatusType status);
+ /*virtual*/ void handleError(EStatusType status);
+ /*virtual*/ BOOL isActive();
+ /*virtual*/ void activate();
+ /*virtual*/ void deactivate();
+
+};
+
+class LLVoiceChannelP2P : public LLVoiceChannelGroup
+{
+public:
+ LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id);
+ virtual ~LLVoiceChannelP2P();
+
+ /*virtual*/ void handleStatusChange(EStatusType status);
+ /*virtual*/ void handleError(EStatusType status);
+ /*virtual*/ void activate();
+ /*virtual*/ void getChannelInfo();
+
+ void setSessionHandle(const LLString& handle);
+
+private:
+ LLString mSessionHandle;
+ LLUUID mOtherUserID;
+};
class LLFloaterIMPanel : public LLFloater
{
public:
+
// The session id is the id of the session this is for. The target
// refers to the user (or group) that where this session serves as
// the default. For example, if you open a session though a
@@ -40,7 +147,7 @@ public:
const LLUUID& target_id,
const LLDynamicArray<LLUUID>& ids,
EInstantMessage dialog);
-
+ virtual ~LLFloaterIMPanel();
/*virtual*/ BOOL postBuild();
@@ -53,6 +160,10 @@ public:
// Return TRUE if successful, otherwise FALSE.
BOOL inviteToSession(const LLDynamicArray<LLUUID>& agent_ids);
+ void addHistoryLine(const LLUUID& source,
+ const std::string &utf8msg,
+ const LLColor4& color = LLColor4::white,
+ bool log_to_file = true);
void addHistoryLine(const std::string &utf8msg,
const LLColor4& color = LLColor4::white,
bool log_to_file = true);
@@ -71,15 +182,32 @@ public:
static void onInputEditorFocusReceived( LLUICtrl* caller, void* userdata );
static void onInputEditorFocusLost(LLUICtrl* caller, void* userdata);
static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata);
+ static void onCommitChat(LLUICtrl* caller, void* userdata);
static void onTabClick( void* userdata );
- static void onClickProfile( void* userdata ); // Profile button pressed
+ static void onClickProfile( void* userdata );
+ static void onClickGroupInfo( void* userdata );
static void onClickClose( void* userdata );
+ static void onClickStartCall( void* userdata );
+ static void onClickEndCall( void* userdata );
+ static void onClickSend( void* userdata );
+ static void onClickToggleActiveSpeakers( void* userdata );
+ static void* createSpeakersPanel(void* data);
+
+ //callbacks for P2P muting and volume control
+ static void onClickMuteVoice(LLUICtrl* source, void* user_data);
+ static void onVolumeChange(LLUICtrl* source, void* user_data);
const LLUUID& getSessionID() const { return mSessionUUID; }
const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; }
+ void updateSpeakersList(LLSD speaker_updates);
+ void setSpeakersListFromMap(LLSD speaker_list);
+ void setSpeakersList(LLSD speaker_list);
+ LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; }
EInstantMessage getDialogType() const { return mDialog; }
+ void requestAutoConnect();
+
void sessionInitReplyReceived(const LLUUID& im_session_id);
// Handle other participant in the session typing.
@@ -98,7 +226,7 @@ private:
BOOL dropCategory(LLInventoryCategory* category, BOOL drop);
// test if local agent can add agents.
- BOOL isAddAllowed() const;
+ BOOL isInviteAllowed() const;
// Called whenever the user starts or stops typing.
// Sends the typing state to the other user if necessary.
@@ -108,7 +236,7 @@ private:
void addTypingIndicator(const std::string &name);
// Remove the "User is typing..." indicator.
- void removeTypingIndicator();
+ void removeTypingIndicator(const LLIMInfo* im_info);
void sendTypingState(BOOL typing);
@@ -125,7 +253,8 @@ private:
// 911 ==> Gaurdian_Angel_Group_ID ^ gAgent.getID()
LLUUID mSessionUUID;
- BOOL mSessionInitRequested;
+ LLVoiceChannel* mVoiceChannel;
+
BOOL mSessionInitialized;
LLSD mQueuedMsgsForInit;
@@ -150,10 +279,17 @@ private:
// Where does the "User is typing..." line start?
S32 mTypingLineStartIndex;
- //Where does the "Starting session..." line start?
+ // Where does the "Starting session..." line start?
S32 mSessionStartMsgPos;
-
+
BOOL mSentTypingState;
+
+ BOOL mShowSpeakersOnConnect;
+
+ BOOL mAutoConnect;
+
+ LLIMSpeakerMgr* mSpeakers;
+ LLPanelActiveSpeakers* mSpeakerPanel;
// Optimization: Don't send "User is typing..." until the
// user has actually been typing for a little while. Prevents
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 5a6cbb045d..d8643ab9c1 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -1,5 +1,5 @@
/**
- * @file llimview.cpp
+ * @file LLIMMgr.cpp
* @brief Container for Instant Messaging
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
@@ -12,9 +12,9 @@
#include "llfontgl.h"
#include "llrect.h"
-#include "lldbstrings.h"
#include "llerror.h"
#include "llbutton.h"
+#include "llhttpclient.h"
#include "llsdutil.h"
#include "llstring.h"
#include "linked_lists.h"
@@ -26,8 +26,8 @@
#include "llviewerwindow.h"
#include "llresmgr.h"
#include "llfloaterchat.h"
+#include "llfloaterchatterbox.h"
#include "llfloaternewim.h"
-#include "llhttpclient.h"
#include "llhttpnode.h"
#include "llimpanel.h"
#include "llresizebar.h"
@@ -42,40 +42,48 @@
#include "llcallingcard.h"
#include "lltoolbar.h"
#include "llviewermessage.h"
+#include "llnotify.h"
#include "llviewerregion.h"
+#include "llfirstuse.h"
+
const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START;
const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL;
//
// Globals
//
-LLIMView* gIMView = NULL;
+LLIMMgr* gIMMgr = NULL;
//
// Statics
//
+//*FIXME: make these all either UIStrings or Strings
static LLString sOnlyUserMessage;
-static LLString sOfflineMessage;
+static LLUIString sOfflineMessage;
static std::map<std::string,LLString> sEventStringsMap;
static std::map<std::string,LLString> sErrorStringsMap;
static std::map<std::string,LLString> sForceCloseSessionMap;
+static LLUIString sInviteMessage;
//
// Helper Functions
//
// 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);
-}
+//static BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b )
+//{
+// return (LLString::compareDict( a->mName, b->mName ) < 0);
+//}
// the other_participant_id is either an agent_id, a group_id, or an inventory
// folder item_id (collection of calling cards)
-static LLUUID compute_session_id(EInstantMessage dialog,
- const LLUUID& other_participant_id)
+
+// static
+LLUUID LLIMMgr::computeSessionID(
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id)
{
LLUUID session_id;
if (IM_SESSION_GROUP_START == dialog)
@@ -87,6 +95,11 @@ static LLUUID compute_session_id(EInstantMessage 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();
@@ -121,87 +134,25 @@ LLFloaterIM::LLFloaterIM()
BOOL LLFloaterIM::postBuild()
{
- requires("only_user_message", WIDGET_TYPE_TEXT_BOX);
- requires("offline_message", WIDGET_TYPE_TEXT_BOX);
- requires("generic_request_error", WIDGET_TYPE_TEXT_BOX);
- requires("insufficient_perms_error", WIDGET_TYPE_TEXT_BOX);
- requires("generic_request_error", WIDGET_TYPE_TEXT_BOX);
- requires("add_session_event", WIDGET_TYPE_TEXT_BOX);
- requires("message_session_event", WIDGET_TYPE_TEXT_BOX);
- requires("removed_from_group", WIDGET_TYPE_TEXT_BOX);
-
- if (checkRequirements())
- {
- sOnlyUserMessage = childGetText("only_user_message");
- sOfflineMessage = childGetText("offline_message");
-
- sErrorStringsMap["generic"] =
- childGetText("generic_request_error");
- sErrorStringsMap["unverified"] =
- childGetText("insufficient_perms_error");
- sErrorStringsMap["no_user_911"] =
- childGetText("user_no_help");
+ sOnlyUserMessage = getFormattedUIString("only_user_message");
+ sOfflineMessage = getUIString("offline_message");
- sEventStringsMap["add"] = childGetText("add_session_event");
- sEventStringsMap["message"] =
- childGetText("message_session_event");
+ sErrorStringsMap["generic"] =
+ getFormattedUIString("generic_request_error");
+ sErrorStringsMap["unverified"] =
+ getFormattedUIString("insufficient_perms_error");
+ sErrorStringsMap["no_user_911"] =
+ getFormattedUIString("user_no_help");
- sForceCloseSessionMap["removed"] =
- childGetText("removed_from_group");
+ sEventStringsMap["add"] = childGetText("add_session_event");
+ sEventStringsMap["message"] =
+ getFormattedUIString("message_session_event");
- return TRUE;
- }
- return FALSE;
-}
-
-//// virtual
-//BOOL LLFloaterIM::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
-//{
-// BOOL handled = FALSE;
-// if (getEnabled()
-// && mask == (MASK_CONTROL|MASK_SHIFT))
-// {
-// if (key == 'W')
-// {
-// LLFloater* floater = getActiveFloater();
-// if (floater)
-// {
-// if (mTabContainer->getTabCount() == 1)
-// {
-// // trying to close last tab, close
-// // entire window.
-// close();
-// handled = TRUE;
-// }
-// }
-// }
-// }
-// return handled || LLMultiFloater::handleKeyHere(key, mask, called_from_parent);
-//}
+ sForceCloseSessionMap["removed"] =
+ getFormattedUIString("removed_from_group");
-void LLFloaterIM::onClose(bool app_quitting)
-{
- if (!app_quitting)
- {
- gSavedSettings.setBOOL("ShowIM", FALSE);
- }
- setVisible(FALSE);
-}
-
-//virtual
-void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
-{
-/*
- Code removed via patch from VWR-1626
-
- // this code is needed to fix the bug where new IMs received will resize the IM floater.
- // SL-29075, SL-24556, and others
- LLRect parent_rect = getRect();
- S32 dheight = LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT;
- LLRect rect(0, parent_rect.getHeight()-dheight, parent_rect.getWidth(), 0);
- floaterp->reshape(rect.getWidth(), rect.getHeight(), TRUE);
-*/
- LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+ sInviteMessage = getUIString("invite_message");
+ return TRUE;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -213,7 +164,7 @@ void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLT
class LLIMViewFriendObserver : public LLFriendObserver
{
public:
- LLIMViewFriendObserver(LLIMView* tv) : mTV(tv) {}
+ LLIMViewFriendObserver(LLIMMgr* tv) : mTV(tv) {}
virtual ~LLIMViewFriendObserver() {}
virtual void changed(U32 mask)
{
@@ -223,7 +174,30 @@ public:
}
}
protected:
- LLIMView* mTV;
+ LLIMMgr* mTV;
+};
+
+
+class LLIMMgr::LLIMSessionInvite
+{
+public:
+ LLIMSessionInvite(const LLUUID& session_id, const LLString& session_name, const LLUUID& caller_id,const LLString& caller_name, EInstantMessage type, const LLString& session_handle, const LLString& notify_box) :
+ mSessionID(session_id),
+ mSessionName(session_name),
+ mCallerID(caller_id),
+ mCallerName(caller_name),
+ mType(type),
+ mSessionHandle(session_handle),
+ mNotifyBox(notify_box)
+ {};
+
+ LLUUID mSessionID;
+ LLString mSessionName;
+ LLUUID mCallerID;
+ LLString mCallerName;
+ EInstantMessage mType;
+ LLString mSessionHandle;
+ LLString mNotifyBox;
};
@@ -234,7 +208,7 @@ protected:
// This is a helper function to determine what kind of im session
// should be used for the given agent.
// static
-EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id)
+EInstantMessage LLIMMgr::defaultIMTypeForAgent(const LLUUID& agent_id)
{
EInstantMessage type = IM_NOTHING_SPECIAL;
if(is_agent_friend(agent_id))
@@ -248,20 +222,20 @@ EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id)
}
// static
-//void LLIMView::onPinButton(void*)
+//void LLIMMgr::onPinButton(void*)
//{
// BOOL state = gSavedSettings.getBOOL( "PinTalkViewOpen" );
// gSavedSettings.setBOOL( "PinTalkViewOpen", !state );
//}
// static
-void LLIMView::toggle(void*)
+void LLIMMgr::toggle(void*)
{
static BOOL return_to_mouselook = FALSE;
// Hide the button and show the floater or vice versa.
- llassert( gIMView );
- BOOL old_state = gIMView->getFloaterOpen();
+ 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 )
@@ -292,53 +266,39 @@ void LLIMView::toggle(void*)
return_to_mouselook = FALSE;
}
- gIMView->setFloaterOpen( new_state );
+ gIMMgr->setFloaterOpen( new_state );
}
//
// Member Functions
//
-LLIMView::LLIMView(const std::string& name, const LLRect& rect) :
- LLView(name, rect, FALSE),
+LLIMMgr::LLIMMgr() :
mFriendObserver(NULL),
mIMReceived(FALSE)
{
- gIMView = this;
mFriendObserver = new LLIMViewFriendObserver(this);
LLAvatarTracker::instance().addObserver(mFriendObserver);
- mTalkFloater = new LLFloaterIM();
+ //*HACK: use floater to initialize string constants from xml file
+ // then delete it right away
+ LLFloaterIM* dummy_floater = new LLFloaterIM();
+ delete dummy_floater;
- // New IM Panel
- mNewIMFloater = new LLFloaterNewIM();
- mTalkFloater->addFloater(mNewIMFloater, TRUE);
-
- // Tabs sometimes overlap resize handle
- mTalkFloater->moveResizeHandleToFront();
+ mPendingVoiceInvitations = LLSD::emptyMap();
}
-LLIMView::~LLIMView()
+LLIMMgr::~LLIMMgr()
{
LLAvatarTracker::instance().removeObserver(mFriendObserver);
delete mFriendObserver;
// Children all cleaned up by default view destructor.
}
-EWidgetType LLIMView::getWidgetType() const
-{
- return WIDGET_TYPE_TALK_VIEW;
-}
-
-LLString LLIMView::getWidgetTag() const
-{
- return LL_TALK_VIEW_TAG;
-}
-
// Add a message to a session.
-void LLIMView::addMessage(
+void LLIMMgr::addMessage(
const LLUUID& session_id,
- const LLUUID& other_participant_id,
+ const LLUUID& target_id,
const char* from,
const char* msg,
const char* session_name,
@@ -347,11 +307,30 @@ void LLIMView::addMessage(
const LLUUID& region_id,
const LLVector3& position)
{
+ LLUUID other_participant_id = target_id;
+ bool is_from_system = target_id.isNull();
+
+ // don't process muted IMs
+ if (gMuteListp->isMuted(
+ other_participant_id,
+ LLMute::flagTextChat) && !gMuteListp->isLinden(from))
+ {
+ return;
+ }
+
+ //not sure why...but if it is from ourselves we set the target_id
+ //to be NULL
+ if( other_participant_id == gAgent.getID() )
+ {
+ other_participant_id = LLUUID::null;
+ }
+
LLFloaterIMPanel* floater;
LLUUID new_session_id = session_id;
if (new_session_id.isNull())
{
- new_session_id = compute_session_id(dialog, other_participant_id);
+ //no session ID...compute new one
+ new_session_id = computeSessionID(dialog, other_participant_id);
}
floater = findFloaterBySession(new_session_id);
if (!floater)
@@ -363,20 +342,10 @@ void LLIMView::addMessage(
<< " by participant " << other_participant_id << llendl;
}
}
- if(floater)
- {
- floater->addHistoryLine(msg);
- }
- else
- {
- //if we have recently requsted to be dropped from a session
- //but are still receiving messages from the session, don't make
- //a new floater
- if ( mSessionsDropRequested.has(session_id.asString()) )
- {
- return ;
- }
+ // create IM window as necessary
+ if(!floater)
+ {
const char* name = from;
if(session_name && (strlen(session_name)>1))
{
@@ -384,7 +353,12 @@ void LLIMView::addMessage(
}
- floater = createFloater(new_session_id, other_participant_id, name, dialog, FALSE);
+ 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
@@ -404,27 +378,41 @@ void LLIMView::addMessage(
//<< "*** region_id: " << region_id << std::endl
//<< "*** position: " << position << std::endl;
- floater->addHistoryLine(bonus_info.str());
+ floater->addHistoryLine(bonus_info.str(), gSavedSettings.getColor4("SystemChatColor"));
}
- floater->addHistoryLine(msg);
make_ui_sound("UISndNewIncomingIMSession");
}
- if( !mTalkFloater->getVisible() && !floater->getVisible())
+ // now add message to floater
+ if ( is_from_system ) // chat came from system
+ {
+ floater->addHistoryLine(
+ other_participant_id,
+ msg,
+ gSavedSettings.getColor4("SystemChatColor"));
+ }
+ else
+ {
+ floater->addHistoryLine(other_participant_id, msg);
+ }
+
+ LLFloaterChatterBox* chat_floater = LLFloaterChatterBox::getInstance(LLSD());
+
+ 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 = mTalkFloater->getActiveFloater();
+ LLFloater* previouslyActiveFloater = chat_floater->getActiveFloater();
// select the newly added floater (or the floater with the new line added to it).
// it should be there.
- mTalkFloater->selectFloater(floater);
+ chat_floater->selectFloater(floater);
//there was a previously unseen IM, make that old tab flashing
//it is assumed that the most recently unseen IM tab is the one current selected/active
if ( previouslyActiveFloater && getIMReceived() )
{
- mTalkFloater->setFloaterFlashing(previouslyActiveFloater, TRUE);
+ chat_floater->setFloaterFlashing(previouslyActiveFloater, TRUE);
}
//notify of a new IM
@@ -432,37 +420,87 @@ void LLIMView::addMessage(
}
}
-void LLIMView::notifyNewIM()
+void LLIMMgr::addSystemMessage(const LLUUID& session_id, const LLString& message_name, const LLString::format_map_t& args)
+{
+ LLUIString message;
+
+ // null session id means near me (chat history)
+ if (session_id.isNull())
+ {
+ LLFloaterChat* floaterp = LLFloaterChat::getInstance();
+
+ message = floaterp->getUIString(message_name);
+ message.setArgList(args);
+
+ LLChat chat(message);
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
+ LLFloaterChat::getInstance()->addChatHistory(chat);
+ }
+ else // going to IM session
+ {
+ LLFloaterIMPanel* floaterp = findFloaterBySession(session_id);
+ if (floaterp)
+ {
+ message = floaterp->getUIString(message_name);
+ message.setArgList(args);
+
+ gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString().c_str());
+ }
+ }
+}
+
+void LLIMMgr::notifyNewIM()
{
- if(!gIMView->getFloaterOpen())
+ if(!gIMMgr->getFloaterOpen())
{
mIMReceived = TRUE;
}
}
-BOOL LLIMView::getIMReceived() const
+void LLIMMgr::clearNewIMNotification()
+{
+ mIMReceived = FALSE;
+}
+
+BOOL LLIMMgr::getIMReceived() const
{
return mIMReceived;
}
// This method returns TRUE if the local viewer has a session
// currently open keyed to the uuid.
-BOOL LLIMView::isIMSessionOpen(const LLUUID& uuid)
+BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid)
{
LLFloaterIMPanel* floater = findFloaterBySession(uuid);
if(floater) return TRUE;
return FALSE;
}
+LLUUID LLIMMgr::addP2PSession(const std::string& name,
+ const LLUUID& other_participant_id,
+ const LLString& voice_session_handle)
+{
+ LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id);
+
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(floater)
+ {
+ LLVoiceChannelP2P* voice_channelp = (LLVoiceChannelP2P*)floater->getVoiceChannel();
+ voice_channelp->setSessionHandle(voice_session_handle);
+ }
+
+ return session_id;
+}
+
// 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 LLIMView::addSession(const std::string& name,
+LLUUID LLIMMgr::addSession(const std::string& name,
EInstantMessage dialog,
const LLUUID& other_participant_id)
{
- LLUUID session_id = compute_session_id(dialog, other_participant_id);
+ LLUUID session_id = computeSessionID(dialog, other_participant_id);
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
if(!floater)
@@ -478,7 +516,7 @@ LLUUID LLIMView::addSession(const std::string& name,
TRUE);
noteOfflineUsers(floater, ids);
- mTalkFloater->showFloater(floater);
+ LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater);
}
else
{
@@ -491,7 +529,7 @@ LLUUID LLIMView::addSession(const std::string& name,
// 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 LLIMView::addSession(const std::string& name,
+LLUUID LLIMMgr::addSession(const std::string& name,
EInstantMessage dialog,
const LLUUID& other_participant_id,
const LLDynamicArray<LLUUID>& ids)
@@ -501,7 +539,7 @@ LLUUID LLIMView::addSession(const std::string& name,
return LLUUID::null;
}
- LLUUID session_id = compute_session_id(dialog,
+ LLUUID session_id = computeSessionID(dialog,
other_participant_id);
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
@@ -520,7 +558,7 @@ LLUUID LLIMView::addSession(const std::string& name,
noteOfflineUsers(floater, ids);
}
- mTalkFloater->showFloater(floater);
+ LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater);
//mTabContainer->selectTabPanel(panel);
floater->setInputFocus(TRUE);
return floater->getSessionID();
@@ -528,134 +566,272 @@ LLUUID LLIMView::addSession(const std::string& name,
// This removes the panel referenced by the uuid, and then restores
// internal consistency. The internal pointer is not deleted.
-void LLIMView::removeSession(const LLUUID& session_id)
+void LLIMMgr::removeSession(const LLUUID& session_id)
{
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
if(floater)
{
mFloaters.erase(floater->getHandle());
- mTalkFloater->removeFloater(floater);
+ LLFloaterChatterBox::getInstance(LLSD())->removeFloater(floater);
//mTabContainer->removeTabPanel(floater);
- if(session_id.notNull()
- && (floater->getDialogType() != IM_NOTHING_SPECIAL))
- {
- mSessionsDropRequested[session_id.asString()] = LLSD();
- }
}
}
-void LLIMView::refresh()
+void LLIMMgr::inviteToSession(
+ const LLUUID& session_id,
+ const LLString& session_name,
+ const LLUUID& caller_id,
+ const LLString& caller_name,
+ EInstantMessage type,
+ const LLString& session_handle)
{
- S32 old_scroll_pos = mNewIMFloater->getScrollPos();
- mNewIMFloater->clearAllTargets();
+ //ignore voice invites from voice-muted residents
+ if (gMuteListp->isMuted(caller_id))
+ {
+ return;
+ }
- // build a list of groups.
- LLLinkedList<LLGroupData> group_list( group_dictionary_sort );
+ LLString notify_box_type;
- LLGroupData* group;
- S32 count = gAgent.mGroups.count();
- S32 i;
- // read/sort groups on the first pass.
- for(i = 0; i < count; ++i)
+ BOOL ad_hoc_invite = FALSE;
+ if(type == IM_SESSION_P2P_INVITE)
{
- group = &(gAgent.mGroups.get(i));
- group_list.addDataSorted( group );
+ notify_box_type = "VoiceInviteP2P";
}
-
- // add groups to the floater on the second pass.
- for(group = group_list.getFirstData();
- group;
- group = group_list.getNextData())
+ else if (gAgent.isInGroup(session_id))
{
- mNewIMFloater->addGroup(group->mID, (void*)(&GROUP_DIALOG), TRUE, FALSE);
+ notify_box_type = "VoiceInviteGroup";
+ }
+ else
+ {
+ notify_box_type = "VoiceInviteAdHoc";
+ ad_hoc_invite = TRUE;
}
- // build a set of buddies in the current buddy list.
- LLCollectAllBuddies collector;
- LLAvatarTracker::instance().applyFunctor(collector);
- LLCollectAllBuddies::buddy_map_t::iterator it;
- LLCollectAllBuddies::buddy_map_t::iterator end;
- it = collector.mOnline.begin();
- end = collector.mOnline.end();
- for( ; it != end; ++it)
+ LLIMSessionInvite* invite = new LLIMSessionInvite(
+ session_id,
+ session_name,
+ caller_id,
+ caller_name,
+ type,
+ session_handle,
+ notify_box_type);
+
+ LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
+ if (channelp && channelp->callStarted())
{
- mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), TRUE);
+ // you have already started a call to the other user, so just accept the invite
+ inviteUserResponse(0, invite);
+ return;
}
- it = collector.mOffline.begin();
- end = collector.mOffline.end();
- for( ; it != end; ++it)
+
+ if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite)
{
- mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), FALSE);
+ // is the inviter a friend?
+ if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)
+ {
+ // 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;
+ }
+ }
}
- mNewIMFloater->setScrollPos( old_scroll_pos );
+ if ( !mPendingVoiceInvitations.has(session_id.asString()) )
+ {
+ if (caller_name.empty())
+ {
+ gCacheName->getName(caller_id, onInviteNameLookup, invite);
+ }
+ else
+ {
+ LLString::format_map_t args;
+ args["[NAME]"] = caller_name;
+ args["[GROUP]"] = session_name;
+
+ LLNotifyBox::showXml(notify_box_type,
+ args,
+ inviteUserResponse,
+ (void*)invite);
+
+ }
+ mPendingVoiceInvitations[session_id.asString()] = LLSD();
+ }
}
-// JC - This used to set console visibility. It doesn't any more.
-void LLIMView::setFloaterOpen(BOOL set_open)
+//static
+void LLIMMgr::onInviteNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* userdata)
{
- gSavedSettings.setBOOL("ShowIM", set_open);
+ LLIMSessionInvite* invite = (LLIMSessionInvite*)userdata;
- //RN "visible" and "open" are considered synonomous for now
- if (set_open)
+ invite->mCallerName = llformat("%s %s", first, last);
+ invite->mSessionName = invite->mCallerName;
+
+ LLString::format_map_t args;
+ args["[NAME]"] = invite->mCallerName;
+
+ LLNotifyBox::showXml(invite->mNotifyBox,
+ args,
+ inviteUserResponse,
+ (void*)invite);
+}
+
+class LLViewerChatterBoxInvitationAcceptResponder :
+ public LLHTTPClient::Responder
+{
+public:
+ LLViewerChatterBoxInvitationAcceptResponder(
+ const LLUUID& session_id,
+ bool is_voice_invitation)
{
- mTalkFloater->open(); /*Flawfinder: ignore*/
+ mSessionID = session_id;
+ mIsVoiceInvitiation = is_voice_invitation;
}
- else
+
+ void result(const LLSD& content)
{
- mTalkFloater->close();
+ if ( gIMMgr)
+ {
+ LLFloaterIMPanel* floaterp =
+ gIMMgr->findFloaterBySession(mSessionID);
+
+ if (floaterp)
+ {
+ floaterp->setSpeakersList(content["agents"]);
+
+ if ( mIsVoiceInvitiation )
+ {
+ floaterp->requestAutoConnect();
+ LLFloaterIMPanel::onClickStartCall(floaterp);
+ }
+ }
+
+ if ( mIsVoiceInvitiation )
+ {
+ gIMMgr->clearPendingVoiceInviation(mSessionID);
+ }
+ }
}
- if( set_open )
+ void error(U32 statusNum, const std::string& reason)
{
- // notifyNewIM();
-
- // We're showing the IM, so mark view as non-pending
- mIMReceived = FALSE;
+ //throw something back to the viewer here?
+ if ( gIMMgr && mIsVoiceInvitiation )
+ {
+ gIMMgr->clearPendingVoiceInviation(mSessionID);
+ }
}
-}
+private:
+ LLUUID mSessionID;
+ bool mIsVoiceInvitiation;
+};
-BOOL LLIMView::getFloaterOpen()
+//static
+void LLIMMgr::inviteUserResponse(S32 option, void* user_data)
{
- return mTalkFloater->getVisible();
-}
-
-void LLIMView::pruneSessions()
-{
- if(mNewIMFloater)
+ LLIMSessionInvite* invitep = (LLIMSessionInvite*)user_data;
+
+ switch(option)
{
- BOOL removed = TRUE;
- LLFloaterIMPanel* floater = NULL;
- while(removed)
+ case 0: // accept
+ {
+ if (invitep->mType == IM_SESSION_P2P_INVITE)
+ {
+ // 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)
+ {
+ im_floater->requestAutoConnect();
+ LLFloaterIMPanel::onClickStartCall(im_floater);
+ }
+
+ gIMMgr->clearPendingVoiceInviation(invitep->mSessionID);
+ }
+ else
+ {
+ gIMMgr->addSession(
+ invitep->mSessionName,
+ invitep->mType,
+ invitep->mSessionID);
+
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ LLSD data;
+ data["method"] = "accept invitation";
+ data["session-id"] = invitep->mSessionID;
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLViewerChatterBoxInvitationAcceptResponder(
+ invitep->mSessionID,
+ true));
+ }
+ }
+ 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))
+ {
+ LLMute mute(invitep->mCallerID, invitep->mCallerName, LLMute::AGENT);
+ gMuteListp->add(mute);
+ }
+ }
+ /* FALLTHROUGH */
+
+ case 1: // ignore
{
- removed = FALSE;
- std::set<LLViewHandle>::iterator handle_it;
- for(handle_it = mFloaters.begin();
- handle_it != mFloaters.end();
- ++handle_it)
+ if (invitep->mType == IM_SESSION_P2P_INVITE)
{
- floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
- if(floater && !mNewIMFloater->isUUIDAvailable(floater->getOtherParticipantID()))
+ if(gVoiceClient)
{
- // remove this floater
- removed = TRUE;
- mFloaters.erase(handle_it++);
- floater->close();
- break;
+ gVoiceClient->declineInvite(invitep->mSessionHandle);
}
}
}
+ break;
}
+
+ delete invitep;
}
+void LLIMMgr::refresh()
+{
+}
-void LLIMView::disconnectAllSessions()
+void LLIMMgr::setFloaterOpen(BOOL set_open)
{
- if(mNewIMFloater)
+ if (set_open)
{
- mNewIMFloater->setEnabled(FALSE);
+ LLFloaterChatterBox::showInstance(LLSD());
}
+ else
+ {
+ LLFloaterChatterBox::hideInstance(LLSD());
+ }
+}
+
+
+BOOL LLIMMgr::getFloaterOpen()
+{
+ return LLFloaterChatterBox::instanceVisible(LLSD());
+}
+
+void LLIMMgr::disconnectAllSessions()
+{
LLFloaterIMPanel* floater = NULL;
std::set<LLViewHandle>::iterator handle_it;
for(handle_it = mFloaters.begin();
@@ -670,7 +846,7 @@ void LLIMView::disconnectAllSessions()
if (floater)
{
floater->setEnabled(FALSE);
- floater->onClose(TRUE);
+ floater->close(TRUE);
}
}
}
@@ -679,7 +855,7 @@ void LLIMView::disconnectAllSessions()
// 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* LLIMView::findFloaterBySession(const LLUUID& session_id)
+LLFloaterIMPanel* LLIMMgr::findFloaterBySession(const LLUUID& session_id)
{
LLFloaterIMPanel* rv = NULL;
std::set<LLViewHandle>::iterator handle_it;
@@ -698,17 +874,25 @@ LLFloaterIMPanel* LLIMView::findFloaterBySession(const LLUUID& session_id)
}
-BOOL LLIMView::hasSession(const LLUUID& session_id)
+BOOL LLIMMgr::hasSession(const LLUUID& session_id)
{
return (findFloaterBySession(session_id) != NULL);
}
+void LLIMMgr::clearPendingVoiceInviation(const LLUUID& session_id)
+{
+ if ( mPendingVoiceInvitations.has(session_id.asString()) )
+ {
+ mPendingVoiceInvitations.erase(session_id.asString());
+ }
+}
+
// 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* LLIMView::createFloater(
+LLFloaterIMPanel* LLIMMgr::createFloater(
const LLUUID& session_id,
const LLUUID& other_participant_id,
const std::string& session_label,
@@ -720,7 +904,7 @@ LLFloaterIMPanel* LLIMView::createFloater(
llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
}
- llinfos << "LLIMView::createFloater: from " << other_participant_id
+ llinfos << "LLIMMgr::createFloater: from " << other_participant_id
<< " in session " << session_id << llendl;
LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
LLRect(),
@@ -729,12 +913,12 @@ LLFloaterIMPanel* LLIMView::createFloater(
other_participant_id,
dialog);
LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
- mTalkFloater->addFloater(floater, FALSE, i_pt);
+ LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt);
mFloaters.insert(floater->getHandle());
return floater;
}
-LLFloaterIMPanel* LLIMView::createFloater(
+LLFloaterIMPanel* LLIMMgr::createFloater(
const LLUUID& session_id,
const LLUUID& other_participant_id,
const std::string& session_label,
@@ -747,7 +931,7 @@ LLFloaterIMPanel* LLIMView::createFloater(
llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
}
- llinfos << "LLIMView::createFloater: from " << other_participant_id
+ llinfos << "LLIMMgr::createFloater: from " << other_participant_id
<< " in session " << session_id << llendl;
LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
LLRect(),
@@ -757,18 +941,18 @@ LLFloaterIMPanel* LLIMView::createFloater(
ids,
dialog);
LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
- mTalkFloater->addFloater(floater, FALSE, i_pt);
+ LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt);
mFloaters.insert(floater->getHandle());
return floater;
}
-void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater,
+void LLIMMgr::noteOfflineUsers(LLFloaterIMPanel* floater,
const LLDynamicArray<LLUUID>& ids)
{
S32 count = ids.count();
if(count == 0)
{
- floater->addHistoryLine(sOnlyUserMessage);
+ floater->addHistoryLine(sOnlyUserMessage, gSavedSettings.getColor4("SystemChatColor"));
}
else
{
@@ -785,25 +969,25 @@ void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater,
LLUIString offline = sOfflineMessage;
offline.setArg("[FIRST]", first);
offline.setArg("[LAST]", last);
- floater->addHistoryLine(offline);
+ floater->addHistoryLine(offline, gSavedSettings.getColor4("SystemChatColor"));
}
}
}
}
-void LLIMView::processIMTypingStart(const LLIMInfo* im_info)
+void LLIMMgr::processIMTypingStart(const LLIMInfo* im_info)
{
processIMTypingCore(im_info, TRUE);
}
-void LLIMView::processIMTypingStop(const LLIMInfo* im_info)
+void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info)
{
processIMTypingCore(im_info, FALSE);
}
-void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
+void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
{
- LLUUID session_id = compute_session_id(im_info->mIMType, im_info->mFromID);
+ LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID);
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
if (floater)
{
@@ -811,8 +995,9 @@ void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
}
}
-void LLIMView::updateFloaterSessionID(const LLUUID& old_session_id,
- const LLUUID& new_session_id)
+void LLIMMgr::updateFloaterSessionID(
+ const LLUUID& old_session_id,
+ const LLUUID& new_session_id)
{
LLFloaterIMPanel* floater = findFloaterBySession(old_session_id);
if (floater)
@@ -821,9 +1006,9 @@ void LLIMView::updateFloaterSessionID(const LLUUID& old_session_id,
}
}
-void LLIMView::onDropRequestReplyReceived(const LLUUID& session_id)
-{
- mSessionsDropRequested.erase(session_id.asString());
+LLFloaterChatterBox* LLIMMgr::getFloater()
+{
+ return LLFloaterChatterBox::getInstance(LLSD());
}
void onConfirmForceCloseError(S32 option, void* data)
@@ -831,25 +1016,24 @@ void onConfirmForceCloseError(S32 option, void* data)
//only 1 option really
LLFloaterIMPanel* floater = ((LLFloaterIMPanel*) data);
- if ( floater ) floater->onClose(FALSE);
+ if ( floater ) floater->close(FALSE);
}
-class LLViewerIMSessionStartReply : public LLHTTPNode
+class LLViewerChatterBoxSessionStartReply : public LLHTTPNode
{
public:
virtual void describe(Description& desc) const
{
- desc.shortInfo("Used for receiving a reply to a request to initialize an IM session");
+ desc.shortInfo("Used for receiving a reply to a request to initialize an ChatterBox session");
desc.postAPI();
desc.input(
"{\"client_session_id\": UUID, \"session_id\": UUID, \"success\" boolean, \"reason\": string");
desc.source(__FILE__, __LINE__);
}
- virtual void post(
- ResponsePtr response,
- const LLSD& context,
- const LLSD& input) const
+ virtual void post(ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
{
LLSD body;
LLUUID temp_session_id;
@@ -863,16 +1047,21 @@ public:
if ( success )
{
session_id = body["session_id"].asUUID();
- gIMView->updateFloaterSessionID(
+ gIMMgr->updateFloaterSessionID(
temp_session_id,
session_id);
+ LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id);
+ if (floaterp)
+ {
+ floaterp->setSpeakersList(body["agents"]);
+ }
}
else
{
//throw an error dialog and close the temp session's
//floater
LLFloaterIMPanel* floater =
- gIMView->findFloaterBySession(temp_session_id);
+ gIMMgr->findFloaterBySession(temp_session_id);
if (floater)
{
LLString::format_map_t args;
@@ -880,22 +1069,22 @@ public:
sErrorStringsMap[body["error"].asString()];
args["[RECIPIENT]"] = floater->getTitle();
- gViewerWindow->alertXml(
- "IMSessionStartError",
- args,
- onConfirmForceCloseError,
- floater);
+ gViewerWindow->alertXml("ChatterBoxSessionStartError",
+ args,
+ onConfirmForceCloseError,
+ floater);
+
}
}
}
};
-class LLViewerIMSessionEventReply : public LLHTTPNode
+class LLViewerChatterBoxSessionEventReply : public LLHTTPNode
{
public:
virtual void describe(Description& desc) const
{
- desc.shortInfo("Used for receiving a reply to a IM session event");
+ desc.shortInfo("Used for receiving a reply to a ChatterBox session event");
desc.postAPI();
desc.input(
"{\"event\": string, \"reason\": string, \"success\": boolean, \"session_id\": UUID");
@@ -917,7 +1106,7 @@ public:
{
//throw an error dialog
LLFloaterIMPanel* floater =
- gIMView->findFloaterBySession(session_id);
+ gIMMgr->findFloaterBySession(session_id);
if (floater)
{
LLString::format_map_t args;
@@ -927,14 +1116,14 @@ public:
sEventStringsMap[body["event"].asString()];
args["[RECIPIENT]"] = floater->getTitle();
- gViewerWindow->alertXml("IMSessionEventError",
+ gViewerWindow->alertXml("ChatterBoxSessionEventError",
args);
}
}
}
};
-class LLViewerForceCloseIMSession: public LLHTTPNode
+class LLViewerForceCloseChatterBoxSession: public LLHTTPNode
{
public:
virtual void post(ResponsePtr response,
@@ -948,7 +1137,7 @@ public:
reason = input["body"]["reason"].asString();
LLFloaterIMPanel* floater =
- gIMView ->findFloaterBySession(session_id);
+ gIMMgr ->findFloaterBySession(session_id);
if ( floater )
{
@@ -957,7 +1146,7 @@ public:
args["[NAME]"] = floater->getTitle();
args["[REASON]"] = sForceCloseSessionMap[reason];
- gViewerWindow->alertXml("ForceCloseIMSession",
+ gViewerWindow->alertXml("ForceCloseChatterBoxSession",
args,
onConfirmForceCloseError,
floater);
@@ -965,28 +1154,6 @@ public:
}
};
-class LLViewerIMSessionDropReply : public LLHTTPNode
-{
-public:
- virtual void post(ResponsePtr response,
- const LLSD& context,
- const LLSD& input) const
- {
- LLUUID session_id;
- bool success;
-
- success = input["body"]["success"].asBoolean();
- session_id = input["body"]["session_id"].asUUID();
-
- if ( !success )
- {
- //throw an error alert?
- }
-
- gIMView->onDropRequestReplyReceived(session_id);
- }
-};
-
class LLViewerChatterBoxSessionAgentListUpdates : public LLHTTPNode
{
public:
@@ -995,29 +1162,38 @@ public:
const LLSD& context,
const LLSD& input) const
{
+ LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID());
+ if (floaterp)
+ {
+ floaterp->updateSpeakersList(input["body"]["updates"]);
+ }
}
};
class LLViewerChatterBoxInvitation : public LLHTTPNode
{
public:
+
virtual void post(
- ResponsePtr responder,
+ ResponsePtr response,
const LLSD& context,
const LLSD& input) const
{
if ( input["body"].has("instantmessage") )
{
+ LLString capability = input["body"]["capabilities"]["call"].asString();
+
LLSD message_params =
input["body"]["instantmessage"]["message_params"];
+ //do something here to have the IM invite behave
+ //just like a normal IM
//this is just replicated code from process_improved_im
//and should really go in it's own function -jwolk
if (gNoRender)
{
return;
}
-
char buffer[DB_IM_MSG_BUF_SIZE * 2]; /* Flawfinder: ignore */
LLChat chat;
@@ -1032,7 +1208,11 @@ public:
(time_t) message_params["timestamp"].asInteger();
BOOL is_busy = gAgent.getBusy();
- BOOL is_muted = gMuteListp->isMuted(from_id, name);
+ BOOL is_muted = gMuteListp->isMuted(
+ from_id,
+ name.c_str(),
+ LLMute::flagTextChat);
+
BOOL is_linden = gMuteListp->isLinden(
name.c_str());
char separator_string[3]=": "; /* Flawfinder: ignore */
@@ -1049,7 +1229,8 @@ public:
chat.mMuted = is_muted && !is_linden;
chat.mFromID = from_id;
chat.mFromName = name;
- if (!is_linden && is_busy)
+
+ if (!is_linden && (is_busy || is_muted))
{
return;
}
@@ -1077,10 +1258,9 @@ public:
BOOL is_this_agent = FALSE;
if(from_id == gAgentID)
{
- from_id = LLUUID::null;
is_this_agent = TRUE;
}
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name.c_str(),
@@ -1102,11 +1282,7 @@ public:
chat.mText = buffer;
LLFloaterChat::addChat(chat, TRUE, is_this_agent);
- //if we succesfully accepted the invitation
- //send a message back down
-
- //TODO - When availble, have this response just be part
- //of an automatic response system
+ //K now we want to accept the invitation
std::string url = gAgent.getRegion()->getCapability(
"ChatSessionRequest");
@@ -1118,28 +1294,46 @@ public:
LLHTTPClient::post(
url,
data,
- NULL);
+ new LLViewerChatterBoxInvitationAcceptResponder(
+ input["body"]["session_id"],
+ false));
}
} //end if invitation has instant message
+ else if ( input["body"].has("voice") )
+ {
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if(!LLVoiceClient::voiceEnabled())
+ {
+ // Don't display voice invites unless the user has voice enabled.
+ return;
+ }
+
+ gIMMgr->inviteToSession(
+ input["body"]["session_id"].asUUID(),
+ input["body"]["session_name"].asString(),
+ input["body"]["from_id"].asUUID(),
+ input["body"]["from_name"].asString(),
+ IM_SESSION_INVITE);
+ }
}
};
-LLHTTPRegistration<LLViewerIMSessionStartReply>
- gHTTPRegistrationMessageImsessionstartreply(
+LLHTTPRegistration<LLViewerChatterBoxSessionStartReply>
+ gHTTPRegistrationMessageChatterboxsessionstartreply(
"/message/ChatterBoxSessionStartReply");
-LLHTTPRegistration<LLViewerIMSessionEventReply>
- gHTTPRegistrationMessageImsessioneventreply(
+LLHTTPRegistration<LLViewerChatterBoxSessionEventReply>
+ gHTTPRegistrationMessageChatterboxsessioneventreply(
"/message/ChatterBoxSessionEventReply");
-LLHTTPRegistration<LLViewerForceCloseIMSession>
- gHTTPRegistrationMessageForceCloseImSession(
+LLHTTPRegistration<LLViewerForceCloseChatterBoxSession>
+ gHTTPRegistrationMessageForceclosechatterboxsession(
"/message/ForceCloseChatterBoxSession");
-LLHTTPRegistration<LLViewerIMSessionDropReply>
- gHTTPRegistrationMessageImSessionDropReply(
- "/message/ChatterBoxSessionLeaveReply");
-
LLHTTPRegistration<LLViewerChatterBoxSessionAgentListUpdates>
gHTTPRegistrationMessageChatterboxsessionagentlistupdates(
"/message/ChatterBoxSessionAgentListUpdates");
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index aac6fd63ce..f8a36107d6 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -1,5 +1,5 @@
/**
- * @file llimview.h
+ * @file LLIMMgr.h
* @brief Container for Instant Messaging
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
@@ -13,31 +13,32 @@
#include "llinstantmessage.h"
#include "lluuid.h"
-class LLFloaterNewIM;
+class LLFloaterChatterBox;
class LLUUID;
class LLFloaterIMPanel;
class LLFriendObserver;
class LLFloaterIM;
-class LLIMView : public LLView
+class LLIMMgr : public LLSingleton<LLIMMgr>
{
public:
- LLIMView(const std::string& name, const LLRect& rect);
- ~LLIMView();
-
- virtual EWidgetType getWidgetType() const;
- virtual LLString getWidgetTag() const;
+ LLIMMgr();
+ virtual ~LLIMMgr();
// Add a message to a session. The session can keyed to sesion id
// or agent id.
- void addMessage(const LLUUID& session_id, const LLUUID& target_id,
- const char* from, const char* msg,
+ void addMessage(const LLUUID& session_id,
+ const LLUUID& target_id,
+ const char* from,
+ const char* msg,
const char* session_name = NULL,
EInstantMessage dialog = IM_NOTHING_SPECIAL,
U32 parent_estate_id = 0,
const LLUUID& region_id = LLUUID::null,
const LLVector3& position = LLVector3::zero);
+ void addSystemMessage(const LLUUID& session_id, const LLString& message_name, const LLString::format_map_t& args);
+
// This method returns TRUE if the local viewer has a session
// currently open keyed to the uuid. The uuid can be keyed by
// either session id or agent id.
@@ -62,11 +63,23 @@ public:
const LLUUID& other_participant_id,
const LLDynamicArray<LLUUID>& ids);
+ // Creates a P2P session with the requisite handle for responding to voice calls
+ LLUUID addP2PSession(const std::string& name,
+ const LLUUID& other_participant_id,
+ const LLString& voice_session_handle);
+
// This removes the panel referenced by the uuid, and then
// restores internal consistency. The internal pointer is not
// deleted.
void removeSession(const LLUUID& session_id);
+ void inviteToSession(const LLUUID& session_id,
+ const LLString& session_name,
+ const LLUUID& caller,
+ const LLString& caller_name,
+ EInstantMessage type,
+ const LLString& session_handle = LLString::null);
+
//Updates a given session's session IDs. Does not open,
//create or do anything new. If the old session doesn't
//exist, then nothing happens.
@@ -80,6 +93,7 @@ public:
void refresh();
void notifyNewIM();
+ void clearNewIMNotification();
// IM received that you haven't seen yet
BOOL getIMReceived() const;
@@ -87,10 +101,7 @@ public:
void setFloaterOpen(BOOL open); /*Flawfinder: ignore*/
BOOL getFloaterOpen();
- LLFloaterIM * getFloater() { return mTalkFloater; }
-
- // close any sessions which are not available in the newimpanel.
- void pruneSessions();
+ LLFloaterChatterBox* getFloater();
// This method is used to go through all active sessions and
// disable all of them. This method is usally called when you are
@@ -111,9 +122,13 @@ public:
// is no matching panel.
LLFloaterIMPanel* findFloaterBySession(const LLUUID& session_id);
- void onDropRequestReplyReceived(const LLUUID& session_id);
+ static LLUUID computeSessionID(EInstantMessage dialog, const LLUUID& other_participant_id);
+ void clearPendingVoiceInviation(const LLUUID& session_id);
+
private:
+ class LLIMSessionInvite;
+
// create a panel and update internal representation for
// consistency. Returns the pointer, caller (the class instance
// since it is a private method) is not responsible for deleting
@@ -139,9 +154,8 @@ private:
void processIMTypingCore(const LLIMInfo* im_info, BOOL typing);
-public:
- LLFloaterIM* mTalkFloater;
- LLFloaterNewIM* mNewIMFloater;
+ static void inviteUserResponse(S32 option, void* user_data);
+ static void onInviteNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* userdata);
private:
std::set<LLViewHandle> mFloaters;
@@ -150,7 +164,7 @@ private:
// An IM has been received that you haven't seen yet.
BOOL mIMReceived;
- LLSD mSessionsDropRequested;
+ LLSD mPendingVoiceInvitations;
};
@@ -158,13 +172,10 @@ class LLFloaterIM : public LLMultiFloater
{
public:
LLFloaterIM();
- ///*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
/*virtual*/ BOOL postBuild();
- /*virtual*/ void onClose(bool app_quitting);
- /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainerCommon::END);
};
// Globals
-extern LLIMView *gIMView;
+extern LLIMMgr *gIMMgr;
#endif // LL_LLIMView_H
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 4a1d496d6f..8a08bd2a4b 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -2573,8 +2573,8 @@ void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel*
if (item && (item->getCreatorUUID() != gAgent.getID()) &&
(!item->getCreatorUUID().isNull()))
{
- gIMView->setFloaterOpen(TRUE);
- gIMView->addSession(item->getName(), IM_NOTHING_SPECIAL, item->getCreatorUUID());
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession(item->getName(), IM_NOTHING_SPECIAL, item->getCreatorUUID());
}
}
else if ("lure" == action)
@@ -2651,7 +2651,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
BOOL good_card = (item
&& (LLUUID::null != item->getCreatorUUID())
&& (item->getCreatorUUID() != gAgent.getID()));
-
+ BOOL user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()));
items.push_back("Send Instant Message");
items.push_back("Offer Teleport...");
items.push_back("Conference Chat");
@@ -2659,6 +2659,9 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if (!good_card)
{
disabled_items.push_back("Send Instant Message");
+ }
+ if (!good_card || !user_online)
+ {
disabled_items.push_back("Offer Teleport...");
disabled_items.push_back("Conference Chat");
}
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index f7aa4d4251..793571e111 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1466,11 +1466,18 @@ bool LLInventoryModel::loadSkeleton(
{
LLViewerInventoryCategory* cat = categories[i];
cat_set_t::iterator cit = temp_cats.find(cat);
+ if (cit == temp_cats.end())
+ {
+ continue; // cache corruption?? not sure why this happens -SJB
+ }
LLViewerInventoryCategory* tcat = *cit;
// we can safely ignore anything loaded from file, but
// not sent down in the skeleton.
- if(cit == not_cached) continue;
+ if(cit == not_cached)
+ {
+ continue;
+ }
if(cat->getVersion() != tcat->getVersion())
{
// if the cached version does not match the server version,
diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp
index 8b07dfbfeb..62c7d40a90 100644
--- a/indra/newview/llmutelist.cpp
+++ b/indra/newview/llmutelist.cpp
@@ -169,11 +169,11 @@ BOOL LLMuteList::isLinden(const LLString& name) const
}
-BOOL LLMuteList::add(const LLMute& mute)
+BOOL LLMuteList::add(const LLMute& mute, U32 flags)
{
- // Can't mute Lindens
+ // Can't mute text from Lindens
if ((mute.mType == LLMute::AGENT || mute.mType == LLMute::BY_NAME)
- && isLinden(mute.mName))
+ && isLinden(mute.mName) && (flags & LLMute::flagTextChat || flags == 0))
{
gViewerWindow->alertXml("MuteLinden");
return FALSE;
@@ -218,25 +218,59 @@ BOOL LLMuteList::add(const LLMute& mute)
}
else
{
- std::pair<mute_set_t::iterator, bool> result = mMutes.insert(mute);
- if (result.second)
+ // Need a local (non-const) copy to set up flags properly.
+ LLMute localmute = mute;
+
+ // If an entry for the same entity is already in the list, remove it, saving flags as necessary.
+ mute_set_t::iterator it = mMutes.find(localmute);
+ if (it != mMutes.end())
{
- llinfos << "Muting " << mute.mName << " id " << mute.mID << llendl;
- updateAdd(mute);
- notifyObservers();
- //Kill all particle systems owned by muted task
- if(mute.mType == LLMute::AGENT || mute.mType == LLMute::OBJECT)
- {
- gWorldPointer->mPartSim.cleanMutedParticles(mute.mID);
- }
+ // This mute is already in the list. Save the existing entry's flags if that's warranted.
+ localmute.mFlags = it->mFlags;
+
+ mMutes.erase(it);
+ // Don't need to call notifyObservers() here, since it will happen after the entry has been re-added below.
+ }
+ else
+ {
+ // There was no entry in the list previously. Fake things up by making it look like the previous entry had all properties unmuted.
+ localmute.mFlags = LLMute::flagAll;
+ }
- return TRUE;
+ if(flags)
+ {
+ // The user passed some combination of flags. Make sure those flag bits are turned off (i.e. those properties will be muted).
+ localmute.mFlags &= (~flags);
}
else
{
- return FALSE;
+ // The user passed 0. Make sure all flag bits are turned off (i.e. all properties will be muted).
+ localmute.mFlags = 0;
+ }
+
+ // (re)add the mute entry.
+ {
+ std::pair<mute_set_t::iterator, bool> result = mMutes.insert(localmute);
+ if (result.second)
+ {
+ llinfos << "Muting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
+ updateAdd(mute);
+ notifyObservers();
+ if(!(localmute.mFlags & LLMute::flagParticles))
+ {
+ //Kill all particle systems owned by muted task
+ if(localmute.mType == LLMute::AGENT || localmute.mType == LLMute::OBJECT)
+ {
+ gWorldPointer->mPartSim.cleanMutedParticles(localmute.mID);
+ }
+ }
+ return TRUE;
+ }
}
}
+
+ // If we were going to return success, we'd have done it by now.
+ return FALSE;
}
void LLMuteList::updateAdd(const LLMute& mute)
@@ -251,14 +285,14 @@ void LLMuteList::updateAdd(const LLMute& mute)
msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
msg->addStringFast(_PREHASH_MuteName, mute.mName);
msg->addS32("MuteType", mute.mType);
- msg->addU32("MuteFlags", 0x0); // future
+ msg->addU32("MuteFlags", mute.mFlags);
gAgent.sendReliableMessage();
mIsLoaded = TRUE;
}
-BOOL LLMuteList::remove(const LLMute& mute)
+BOOL LLMuteList::remove(const LLMute& mute, U32 flags)
{
BOOL found = FALSE;
@@ -266,8 +300,46 @@ BOOL LLMuteList::remove(const LLMute& mute)
mute_set_t::iterator it = mMutes.find(mute);
if (it != mMutes.end())
{
- updateRemove(*it);
+ LLMute localmute = *it;
+ bool remove = true;
+ if(flags)
+ {
+ // If the user passed mute flags, we may only want to turn some flags on.
+ localmute.mFlags |= flags;
+
+ if(localmute.mFlags == LLMute::flagAll)
+ {
+ // Every currently available mute property has been masked out.
+ // Remove the mute entry entirely.
+ }
+ else
+ {
+ // Only some of the properties are masked out. Update the entry.
+ remove = false;
+ }
+ }
+ else
+ {
+ // The caller didn't pass any flags -- just remove the mute entry entirely.
+ }
+
+ // Always remove the entry from the set -- it will be re-added with new flags if necessary.
mMutes.erase(it);
+
+ if(remove)
+ {
+ // The entry was actually removed. Notify the server.
+ updateRemove(localmute);
+ llinfos << "Unmuting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
+ }
+ else
+ {
+ // Flags were updated, the mute entry needs to be retransmitted to the server and re-added to the list.
+ mMutes.insert(localmute);
+ updateAdd(localmute);
+ llinfos << "Updating mute entry " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
+ }
+
// Must be after erase.
notifyObservers();
found = TRUE;
@@ -361,7 +433,7 @@ BOOL LLMuteList::loadFromFile(const LLString& filename)
buffer, " %d %254s %254[^|]| %u\n", &type, id_buffer, name_buffer,
&flags);
LLUUID id = LLUUID(id_buffer);
- LLMute mute(id, name_buffer, (LLMute::EType)type);
+ LLMute mute(id, name_buffer, (LLMute::EType)type, flags);
if (mute.mID.isNull()
|| mute.mType == LLMute::BY_NAME)
{
@@ -410,19 +482,27 @@ BOOL LLMuteList::saveToFile(const LLString& filename)
{
it->mID.toString(id_string);
const LLString& name = it->mName;
- fprintf(fp, "%d %s %s|\n", (S32)it->mType, id_string, name.c_str());
+ fprintf(fp, "%d %s %s|%u\n", (S32)it->mType, id_string, name.c_str(), it->mFlags);
}
fclose(fp);
return TRUE;
}
-BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name) const
+BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name, U32 flags) const
{
// don't need name or type for lookup
LLMute mute(id);
mute_set_t::const_iterator mute_it = mMutes.find(mute);
- if (mute_it != mMutes.end()) return TRUE;
+ if (mute_it != mMutes.end())
+ {
+ // If any of the flags the caller passed are set, this item isn't considered muted for this caller.
+ if(flags & mute_it->mFlags)
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
// empty names can't be legacy-muted
if (name.empty()) return FALSE;
diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h
index 7f9b1b3bf6..f035e4c2b1 100644
--- a/indra/newview/llmutelist.h
+++ b/indra/newview/llmutelist.h
@@ -23,8 +23,22 @@ public:
// Legacy mutes are BY_NAME and have null UUID.
enum EType { BY_NAME = 0, AGENT = 1, OBJECT = 2, GROUP = 3, COUNT = 4 };
- LLMute(const LLUUID& id, const LLString& name = "", EType type = BY_NAME)
- : mID(id), mName(name), mType(type) { }
+ // Bits in the mute flags. For backwards compatibility (since any mute list entries that were created before the flags existed
+ // will have a flags field of 0), some of the flags are "inverted".
+ // Note that it's possible, through flags, to completely disable an entry in the mute list. The code should detect this case
+ // and remove the mute list entry instead.
+ enum
+ {
+ flagTextChat = 0x00000001, // If set, don't mute user's text chat
+ flagVoiceChat = 0x00000002, // If set, don't mute user's voice chat
+ flagParticles = 0x00000004, // If set, don't mute user's particles
+ flagObjectSounds = 0x00000008, // If set, mute user's object sounds
+
+ flagAll = 0x0000000F // Mask of all currently defined flags
+ };
+
+ LLMute(const LLUUID& id, const LLString& name = "", EType type = BY_NAME, U32 flags = 0)
+ : mID(id), mName(name), mType(type),mFlags(flags) { }
// Returns name + suffix based on type
// For example: "James Tester (resident)"
@@ -39,6 +53,7 @@ public:
LLUUID mID; // agent or object id
LLString mName; // agent or object name
EType mType; // needed for UI display of existing mutes
+ U32 mFlags; // flags pertaining to this mute entry
};
class LLMuteList
@@ -50,14 +65,17 @@ public:
void addObserver(LLMuteListObserver* observer);
void removeObserver(LLMuteListObserver* observer);
- // Add either a normal or a BY_NAME mute.
- BOOL add(const LLMute& mute);
+ // Add either a normal or a BY_NAME mute, for any or all properties.
+ BOOL add(const LLMute& mute, U32 flags = 0);
- // Remove both normal and legacy mutes.
- BOOL remove(const LLMute& mute);
+ // Remove both normal and legacy mutes, for any or all properties.
+ BOOL remove(const LLMute& mute, U32 flags = 0);
// Name is required to test against legacy text-only mutes.
- BOOL isMuted(const LLUUID& id, const LLString& name = LLString::null) const;
+ BOOL isMuted(const LLUUID& id, const LLString& name = LLString::null, U32 flags = 0) const;
+
+ // Alternate (convenience) form for places we don't need to pass the name, but do need flags
+ BOOL isMuted(const LLUUID& id, U32 flags) const { return isMuted(id, LLString::null, flags); };
BOOL isLinden(const LLString& name) const;
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 4fde8988f5..754c096296 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -226,7 +226,7 @@ void LLNetMap::draw()
{
LLGLSNoTexture no_texture;
- LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+ LLLocalClipRect clip(getLocalRect());
glMatrixMode(GL_MODELVIEW);
diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp
index 8e36297c93..0847864be0 100644
--- a/indra/newview/lloverlaybar.cpp
+++ b/indra/newview/lloverlaybar.cpp
@@ -14,24 +14,26 @@
#include "lloverlaybar.h"
#include "audioengine.h"
-#include "llparcel.h"
-
#include "llagent.h"
#include "llbutton.h"
-#include "llviewercontrol.h"
+#include "llfocusmgr.h"
#include "llimview.h"
-#include "lltextbox.h"
-#include "llvoavatar.h"
#include "llmediaengine.h"
-#include "viewer.h"
+#include "llpanelaudiovolume.h"
+#include "llparcel.h"
+#include "lltextbox.h"
#include "llui.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
#include "llviewermenu.h" // handle_reset_view()
#include "llviewerparcelmgr.h"
-#include "llwebbrowserctrl.h"
#include "llvieweruictrlfactory.h"
-#include "llviewerimagelist.h"
#include "llviewerwindow.h"
-#include "llfocusmgr.h"
+#include "llvoiceclient.h"
+#include "llvoavatar.h"
+#include "llvoiceremotectrl.h"
+#include "llwebbrowserctrl.h"
+#include "viewer.h"
//
// Globals
@@ -47,38 +49,54 @@ extern S32 MENU_BAR_HEIGHT;
//static
-void* LLOverlayBar::createMediaRemote(void* userdata)
+void* LLOverlayBar::createMasterRemote(void* userdata)
{
-
- LLOverlayBar *self = (LLOverlayBar*)userdata;
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+ self->mMasterRemote = new LLMediaRemoteCtrl ( "master_volume",
+ "volume",
+ LLRect(),
+ "panel_master_volume.xml");
+ return self->mMasterRemote;
+}
-
+void* LLOverlayBar::createMediaRemote(void* userdata)
+{
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
self->mMediaRemote = new LLMediaRemoteCtrl ( "media_remote",
- "media",
- LLRect(),
- "panel_media_remote.xml");
+ "media",
+ LLRect(),
+ "panel_media_remote.xml");
return self->mMediaRemote;
}
-
-
void* LLOverlayBar::createMusicRemote(void* userdata)
{
-
LLOverlayBar *self = (LLOverlayBar*)userdata;
-
self->mMusicRemote = new LLMediaRemoteCtrl ( "music_remote",
- "music",
- LLRect(),
- "panel_music_remote.xml" );
+ "music",
+ LLRect(),
+ "panel_music_remote.xml" );
return self->mMusicRemote;
}
+void* LLOverlayBar::createVoiceRemote(void* userdata)
+{
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+ self->mVoiceRemote = new LLVoiceRemoteCtrl("voice_remote");
+ return self->mVoiceRemote;
+}
+
LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
-: LLPanel(name, rect, FALSE) // not bordered
+ : LLPanel(name, rect, FALSE), // not bordered
+ mMasterRemote(NULL),
+ mMusicRemote(NULL),
+ mMediaRemote(NULL),
+ mVoiceRemote(NULL),
+ mMediaState(STOPPED),
+ mMusicState(STOPPED)
{
setMouseOpaque(FALSE);
setIsChrome(TRUE);
@@ -86,8 +104,10 @@ LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
isBuilt = FALSE;
LLCallbackMap::map_t factory_map;
+ factory_map["master_volume"] = LLCallbackMap(LLOverlayBar::createMasterRemote, this);
factory_map["media_remote"] = LLCallbackMap(LLOverlayBar::createMediaRemote, this);
factory_map["music_remote"] = LLCallbackMap(LLOverlayBar::createMusicRemote, this);
+ factory_map["voice_remote"] = LLCallbackMap(LLOverlayBar::createVoiceRemote, this);
gUICtrlFactory->buildPanel(this, "panel_overlaybar.xml", &factory_map);
@@ -97,34 +117,17 @@ LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
childSetAction("Mouselook",onClickMouselook,this);
childSetAction("Stand Up",onClickStandUp,this);
- mMusicRemote->addObserver ( this );
-
- if ( gAudiop )
- {
- //HACK / NOT HACK
- //maintenance patch - bhear obsoletes this, do not merge (poppy)
- F32 audioLevelMusic = gSavedSettings.getF32 ( "AudioLevelMusic" );
- mMusicRemote->setVolume ( audioLevelMusic );
- gAudiop->setInternetStreamGain ( audioLevelMusic );
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
- };
-
mIsFocusRoot = TRUE;
-
- mMediaRemote->addObserver ( this );
- mMediaRemote->setVolume ( gSavedSettings.getF32 ( "MediaAudioVolume" ) );
-
isBuilt = true;
+ // make overlay bar conform to window size
+ setRect(rect);
layoutButtons();
}
LLOverlayBar::~LLOverlayBar()
{
// LLView destructor cleans up children
-
- mMusicRemote->remObserver ( this );
- mMediaRemote->remObserver ( this );
}
EWidgetType LLOverlayBar::getWidgetType() const
@@ -148,20 +151,28 @@ void LLOverlayBar::reshape(S32 width, S32 height, BOOL called_from_parent)
}
}
-
void LLOverlayBar::layoutButtons()
{
S32 width = mRect.getWidth();
- if (width > 800) width = 800;
+ if (width > 1024) width = 1024;
S32 count = getChildCount();
const S32 PAD = gSavedSettings.getS32("StatusBarPad");
- F32 segment_width = (F32)(width) / (F32)count;
+ const S32 num_media_controls = 3;
+ S32 media_remote_width = mMediaRemote ? mMediaRemote->getRect().getWidth() : 0;
+ S32 music_remote_width = mMusicRemote ? mMusicRemote->getRect().getWidth() : 0;
+ S32 voice_remote_width = mVoiceRemote ? mVoiceRemote->getRect().getWidth() : 0;
+ S32 master_remote_width = mMasterRemote ? mMasterRemote->getRect().getWidth() : 0;
- S32 btn_width = lltrunc(segment_width - PAD);
+ // total reserved width for all media remotes
+ const S32 ENDPAD = 20;
+ S32 remote_total_width = media_remote_width + PAD + music_remote_width + PAD + voice_remote_width + PAD + master_remote_width + ENDPAD;
- S32 remote_width = mMusicRemote->getRect().getWidth();
+ // calculate button widths
+ F32 segment_width = (F32)(width - remote_total_width) / (F32)(count - num_media_controls);
+
+ S32 btn_width = lltrunc(segment_width - PAD);
// Evenly space all views
LLRect r;
@@ -171,22 +182,47 @@ void LLOverlayBar::layoutButtons()
{
LLView *view = *child_iter;
r = view->getRect();
- r.mLeft = (width) - llround((i+1)*segment_width);
+ r.mLeft = (width) - llround(remote_total_width + (i-num_media_controls+1)*segment_width);
r.mRight = r.mLeft + btn_width;
view->setRect(r);
i++;
}
// Fix up remotes to have constant width because they can't shrink
- r = mMusicRemote->getRect();
- r.mRight = r.mLeft + remote_width;
- mMusicRemote->setRect(r);
-
- r = mMediaRemote->getRect();
- r.mLeft = mMusicRemote->getRect().mRight + PAD;
- r.mRight = r.mLeft + remote_width;
- mMediaRemote->setRect(r);
-
+ S32 right = mRect.getWidth() - remote_total_width - PAD;
+ if (mMediaRemote)
+ {
+ r = mMediaRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + media_remote_width;
+ r.mRight = right;
+ mMediaRemote->setRect(r);
+ }
+ if (mMusicRemote)
+ {
+ r = mMusicRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + music_remote_width;
+ r.mRight = right;
+ mMusicRemote->setRect(r);
+ }
+ if (mVoiceRemote)
+ {
+ r = mVoiceRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + voice_remote_width;
+ r.mRight = right;
+ mVoiceRemote->setRect(r);
+ }
+ if (mMasterRemote)
+ {
+ r = mMasterRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + master_remote_width;
+ r.mRight = right;
+ mMasterRemote->setRect(r);
+ }
+
updateRect();
}
@@ -266,7 +302,7 @@ void LLOverlayBar::draw()
// Per-frame updates of visibility
void LLOverlayBar::refresh()
{
- BOOL im_received = gIMView->getIMReceived();
+ BOOL im_received = gIMMgr->getIMReceived();
childSetVisible("IM Received", im_received);
childSetEnabled("IM Received", im_received);
@@ -297,10 +333,10 @@ void LLOverlayBar::refresh()
}
- if ( gAudiop )
+ if ( mMusicRemote && gAudiop )
{
LLParcel* parcel = gParcelMgr->getAgentParcel();
- if (!parcel
+ if (!parcel
|| !parcel->getMusicURL()
|| !parcel->getMusicURL()[0]
|| !gSavedSettings.getBOOL("AudioStreamingMusic"))
@@ -316,50 +352,29 @@ void LLOverlayBar::refresh()
}
// if there is a url and a texture and media is enabled and available and media streaming is on... (phew!)
- if ( LLMediaEngine::getInstance () &&
- LLMediaEngine::getInstance ()->getUrl ().length () &&
- LLMediaEngine::getInstance ()->getImageUUID ().notNull () &&
- LLMediaEngine::getInstance ()->isEnabled () &&
- LLMediaEngine::getInstance ()->isAvailable () &&
- gSavedSettings.getBOOL ( "AudioStreamingVideo" ) )
+ if ( mMediaRemote )
{
- // display remote control
- mMediaRemote->setVisible ( TRUE );
- mMediaRemote->setEnabled ( TRUE );
-
- if ( LLMediaEngine::getInstance ()->getMediaRenderer () )
+ if (LLMediaEngine::getInstance () &&
+ LLMediaEngine::getInstance ()->getUrl ().length () &&
+ LLMediaEngine::getInstance ()->getImageUUID ().notNull () &&
+ LLMediaEngine::getInstance ()->isEnabled () &&
+ LLMediaEngine::getInstance ()->isAvailable () &&
+ gSavedSettings.getBOOL ( "AudioStreamingVideo" ) )
{
- if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPlaying () ||
- LLMediaEngine::getInstance ()->getMediaRenderer ()->isLooping () )
- {
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
- }
- else
- if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPaused () )
- {
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
- }
- else
- {
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
- };
- };
+ // display remote control
+ mMediaRemote->setVisible ( TRUE );
+ mMediaRemote->setEnabled ( TRUE );
+ }
+ else
+ {
+ mMediaRemote->setVisible ( FALSE );
+ mMediaRemote->setEnabled ( FALSE );
+ }
}
- else
+ if (mVoiceRemote)
{
- mMediaRemote->setVisible ( FALSE );
- mMediaRemote->setEnabled ( FALSE );
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
- };
-
- BOOL any_button = (childIsVisible("IM Received")
- || childIsVisible("Set Not Busy")
- || childIsVisible("Release Keys")
- || childIsVisible("Mouselook")
- || childIsVisible("Stand Up")
- || mMusicRemote->getVisible()
- || mMediaRemote->getVisible() );
-
+ mVoiceRemote->setVisible(LLVoiceClient::voiceEnabled());
+ }
// turn off the whole bar in mouselook
if (gAgent.cameraMouselook())
@@ -368,8 +383,8 @@ void LLOverlayBar::refresh()
}
else
{
- setVisible(any_button);
- };
+ setVisible(TRUE);
+ }
}
//-----------------------------------------------------------------------
@@ -379,7 +394,7 @@ void LLOverlayBar::refresh()
// static
void LLOverlayBar::onClickIMReceived(void*)
{
- gIMView->setFloaterOpen(TRUE);
+ gIMMgr->setFloaterOpen(TRUE);
}
@@ -415,134 +430,162 @@ void LLOverlayBar::onClickStandUp(void*)
}
////////////////////////////////////////////////////////////////////////////////
-//
-//
-void
-LLOverlayBar::
-onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
-{
- LLUICtrl* control = eventIn.getControl ();
- F32 value = eventIn.getValue ();
+// static media helpers
+// *TODO: Move this into an audio manager abstraction
- if ( control == mMusicRemote )
+//static
+void LLOverlayBar::mediaPlay(void*)
+{
+ if (!gOverlayBar)
{
- if (gAudiop)
- {
- gAudiop->setInternetStreamGain ( value );
- };
- gSavedSettings.setF32 ( "AudioLevelMusic", value );
+ return;
}
- else
- if ( control == mMediaRemote )
+ gOverlayBar->mMediaState = PLAYING; // desired state
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
{
- LLMediaEngine::getInstance ()->setVolume ( value );
- gSavedSettings.setF32 ( "MediaAudioVolume", value );
-
- };
+ LLString path("");
+ LLMediaEngine::getInstance()->convertImageAndLoadUrl( true, false, path );
+ }
}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-//
-void
-LLOverlayBar::
-onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+//static
+void LLOverlayBar::mediaPause(void*)
{
- LLUICtrl* control = eventIn.getControl ();
-
- if ( control == mMusicRemote )
+ if (!gOverlayBar)
{
- if ( gAudiop )
- {
- gAudiop->stopInternetStream ();
- };
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ return;
}
- else
- if ( control == mMediaRemote )
+ gOverlayBar->mMediaState = PAUSED; // desired state
+ LLMediaEngine::getInstance()->pause();
+}
+//static
+void LLOverlayBar::mediaStop(void*)
+{
+ if (!gOverlayBar)
{
- LLMediaEngine::getInstance ()->stop ();
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
- };
+ return;
+ }
+ gOverlayBar->mMediaState = STOPPED; // desired state
+ LLMediaEngine::getInstance()->stop();
}
-////////////////////////////////////////////////////////////////////////////////
-//
-//
-void LLOverlayBar::onPlayButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+//static
+void LLOverlayBar::musicPlay(void*)
{
- LLUICtrl* control = eventIn.getControl ();
-
- LLParcel* parcel = gParcelMgr->getAgentParcel();
- if ( control == mMusicRemote )
+ if (!gOverlayBar)
{
- if (gAudiop)
+ return;
+ }
+ gOverlayBar->mMusicState = PLAYING; // desired state
+ if (gAudiop)
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if ( parcel )
{
- if ( parcel )
+ // this doesn't work properly when crossing parcel boundaries - even when the
+ // stream is stopped, it doesn't return the right thing - commenting out for now.
+// if ( gAudiop->isInternetStreamPlaying() == 0 )
{
- // this doesn't work properly when crossing parcel boundaries - even when the
- // stream is stopped, it doesn't return the right thing - commenting out for now.
- //if ( gAudiop->isInternetStreamPlaying() == 0 )
- //{
- const char* music_url = parcel->getMusicURL();
-
- gAudiop->startInternetStream(music_url);
-
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
- //}
+ gAudiop->startInternetStream(parcel->getMusicURL());
}
- };
-
- // CP: this is the old way of doing things (click play each time on a parcel to start stream)
- //if (gAudiop)
- //{
- // if (gAudiop->isInternetStreamPlaying() > 0)
- // {
- // gAudiop->pauseInternetStream ( 0 );
- // }
- // else
- // {
- // if (parcel)
- // {
- // const char* music_url = parcel->getMusicURL();
- // gAudiop->startInternetStream(music_url);
- // }
- // }
- //};
- //mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
}
- else
- if ( control == mMediaRemote )
+}
+//static
+void LLOverlayBar::musicPause(void*)
+{
+ if (!gOverlayBar)
{
- LLParcel* parcel = gParcelMgr->getAgentParcel();
- if (parcel)
+ return;
+ }
+ gOverlayBar->mMusicState = PAUSED; // desired state
+ if (gAudiop)
+ {
+ gAudiop->pauseInternetStream(1);
+ }
+}
+//static
+void LLOverlayBar::musicStop(void*)
+{
+ if (!gOverlayBar)
+ {
+ return;
+ }
+ gOverlayBar->mMusicState = STOPPED; // desired state
+ if (gAudiop)
+ {
+ gAudiop->stopInternetStream();
+ }
+}
+
+//static
+void LLOverlayBar::enableMusicButtons(LLPanel* panel)
+{
+ BOOL play_enabled = FALSE;
+ BOOL play_visible = TRUE;
+ BOOL pause_visible = FALSE;
+ BOOL stop_enabled = FALSE;
+ if ( gAudiop && gOverlayBar && gSavedSettings.getBOOL("AudioStreamingMusic"))
+ {
+ play_enabled = TRUE;
+ S32 is_playing = gAudiop->isInternetStreamPlaying();
+ if (is_playing == 1)
{
- LLString path( "" );
- LLMediaEngine::getInstance ()->convertImageAndLoadUrl( true, false, path );
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
+ play_visible = FALSE;
+ pause_visible = TRUE;
+ stop_enabled = TRUE;
}
- };
+ else if (is_playing == 2)
+ {
+ play_visible = TRUE;
+ pause_visible = FALSE;
+ stop_enabled = TRUE;
+ }
+ }
+ panel->childSetEnabled("music_play", play_enabled);
+ panel->childSetEnabled("music_pause", play_enabled);
+ panel->childSetVisible("music_play", play_visible);
+ panel->childSetVisible("music_pause", pause_visible);
+ panel->childSetEnabled("music_stop", stop_enabled);
}
-////////////////////////////////////////////////////////////////////////////////
-//
-//
-void LLOverlayBar::onPauseButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+//static
+void LLOverlayBar::enableMediaButtons(LLPanel* panel)
{
- LLUICtrl* control = eventIn.getControl ();
+ // Media
+ BOOL play_enabled = FALSE;
+ BOOL play_visible = TRUE;
+ BOOL pause_visible = FALSE;
+ BOOL stop_enabled = FALSE;
- if ( control == mMusicRemote )
+ if ( LLMediaEngine::getInstance() && gOverlayBar && gSavedSettings.getBOOL("AudioStreamingVideo") )
{
- if (gAudiop)
+ play_enabled = TRUE;
+ if (LLMediaEngine::getInstance()->getMediaRenderer())
{
- gAudiop->pauseInternetStream ( 1 );
- };
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ if ( LLMediaEngine::getInstance()->getMediaRenderer()->isPlaying() ||
+ LLMediaEngine::getInstance()->getMediaRenderer()->isLooping() )
+ {
+ play_visible = FALSE;
+ pause_visible = TRUE;
+ stop_enabled = TRUE;
+ }
+ else if ( LLMediaEngine::getInstance()->getMediaRenderer()->isPaused() )
+ {
+ play_visible = TRUE;
+ pause_visible = FALSE;
+ stop_enabled = TRUE;
+ }
+ }
}
- else
- if ( control == mMediaRemote )
- {
- LLMediaEngine::getInstance ()->pause ();
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
- };
+ panel->childSetEnabled("media_play", play_enabled);
+ panel->childSetEnabled("media_pause", play_enabled);
+ panel->childSetVisible("media_play", play_visible);
+ panel->childSetVisible("media_pause", pause_visible);
+ panel->childSetEnabled("media_stop", stop_enabled);
+}
+
+void LLOverlayBar::toggleAudioVolumeFloater(void* user_data)
+{
+ LLFloaterAudioVolume::toggleInstance(LLSD());
}
diff --git a/indra/newview/lloverlaybar.h b/indra/newview/lloverlaybar.h
index 78f544df57..d7fff6ba1e 100644
--- a/indra/newview/lloverlaybar.h
+++ b/indra/newview/lloverlaybar.h
@@ -25,11 +25,10 @@ class LLUUID;
class LLFrameTimer;
class LLStatGraph;
class LLSlider;
-class LLVolumeSliderCtrl;
+class LLVoiceRemoteCtrl;
class LLOverlayBar
-: public LLPanel,
- public LLMediaRemoteCtrlObserver
+: public LLPanel
{
public:
LLOverlayBar(const std::string& name, const LLRect& rect );
@@ -38,14 +37,16 @@ public:
virtual EWidgetType getWidgetType() const;
virtual LLString getWidgetTag() const;
- virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
-
- void refresh();
+ /*virtual*/ void refresh();
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
void layoutButtons();
- /*virtual*/ void draw();
-
+ // helpers for returning desired state
+ BOOL mediaPlaying() { return mMediaState == PLAYING; }
+ BOOL musicPlaying() { return mMusicState == PLAYING; }
+
static void onClickIMReceived(void* data);
static void onClickSetNotBusy(void* data);
static void onClickReleaseKeys(void* data);
@@ -53,23 +54,35 @@ public:
static void onClickStandUp(void* data);
static void onClickResetView(void* data);
- // observer overrides
- void onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
- void onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
- void onPlayButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
- void onPauseButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
-
- LLMediaRemoteCtrl* getMusicRemoteControl () { return mMusicRemote; };
+ //static media helper functions
+ static void mediaPlay(void*);
+ static void mediaPause(void*);
+ static void mediaStop(void*);
+
+ static void musicPlay(void*);
+ static void musicPause(void*);
+ static void musicStop(void*);
-protected:
+ static void toggleAudioVolumeFloater(void*);
+
+ static void enableMediaButtons(LLPanel* panel);
+ static void enableMusicButtons(LLPanel* panel);
+protected:
+ static void* createMasterRemote(void* userdata);
static void* createMusicRemote(void* userdata);
static void* createMediaRemote(void* userdata);
+ static void* createVoiceRemote(void* userdata);
protected:
+ LLMediaRemoteCtrl* mMasterRemote;
LLMediaRemoteCtrl* mMusicRemote;
LLMediaRemoteCtrl* mMediaRemote;
+ LLVoiceRemoteCtrl* mVoiceRemote;
BOOL isBuilt;
+ enum { STOPPED=0, PLAYING=1, PAUSED=2 };
+ BOOL mMediaState;
+ BOOL mMusicState;
};
extern LLOverlayBar* gOverlayBar;
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index 2567684fa9..ab2e30014d 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -1398,6 +1398,7 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
setOnlineStatus(online_status);
BOOL own_avatar = (mAvatarID == gAgent.getID() );
+ BOOL avatar_is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL;
mPanelSecondLife->enableControls(own_avatar && mAllowEdit);
mPanelWeb->enableControls(own_avatar && mAllowEdit);
@@ -1515,7 +1516,7 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
childSetToolTip("Show on Map",childGetValue("ShowOnMapFriendOnline").asString());
}
childSetVisible("Add Friend...", true);
- childSetEnabled("Add Friend...", true);
+ childSetEnabled("Add Friend...", !avatar_is_friend);
childSetVisible("Pay...",TRUE);
childSetEnabled("Pay...",FALSE);
}
@@ -1589,12 +1590,12 @@ void LLPanelAvatar::resetGroupList()
void LLPanelAvatar::onClickIM(void* userdata)
{
LLPanelAvatar* self = (LLPanelAvatar*) userdata;
- gIMView->setFloaterOpen(TRUE);
+ gIMMgr->setFloaterOpen(TRUE);
std::string name;
LLNameEditor* nameedit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
if (nameedit) name = nameedit->getText();
- gIMView->addSession(name, IM_NOTHING_SPECIAL, self->mAvatarID);
+ gIMMgr->addSession(name, IM_NOTHING_SPECIAL, self->mAvatarID);
}
@@ -1624,7 +1625,7 @@ void LLPanelAvatar::onClickAddFriend(void* userdata)
LLNameEditor* name_edit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
if (name_edit)
{
- LLFloaterFriends::requestFriendshipDialog(self->getAvatarID(),
+ LLPanelFriends::requestFriendshipDialog(self->getAvatarID(),
name_edit->getText());
}
}
diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp
index 23f7a4aba6..7a288a1730 100644
--- a/indra/newview/llpanelgroup.cpp
+++ b/indra/newview/llpanelgroup.cpp
@@ -105,16 +105,7 @@ void LLPanelGroupTab::handleClickHelp()
LLAlertDialog* dialogp = gViewerWindow->alertXml("GenericAlert", args);
if (dialogp)
{
- LLView* viewp = this;
- LLFloater* root_floater = NULL;
- while(viewp)
- {
- if(viewp->getWidgetType() == WIDGET_TYPE_FLOATER)
- {
- root_floater = (LLFloater*)viewp;
- }
- viewp = viewp->getParent();
- }
+ LLFloater* root_floater = gFloaterView->getParentFloater(this);;
if (root_floater)
{
root_floater->addDependentFloater(dialogp);
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index c3d4551032..e32a64eb8e 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -24,6 +24,7 @@
#include "llcombobox.h"
#include "llviewercontrol.h"
#include "llfloaterabout.h"
+#include "llfloatertest.h"
#include "llfloaterpreference.h"
#include "llfocusmgr.h"
#include "lllineeditor.h"
@@ -404,23 +405,30 @@ BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
return TRUE;
}
- #if LL_LIBXUL_ENABLED
+ if (('T' == key) && (MASK_CONTROL == mask))
+ {
+ new LLFloaterSimple("floater_test.xml");
+ return TRUE;
+ }
+
+#if LL_LIBXUL_ENABLED
if ( KEY_F1 == key )
{
llinfos << "Spawning HTML help window" << llendl;
gViewerHtmlHelp.show();
return TRUE;
- };
- #if ! LL_RELEASE_FOR_DOWNLOAD
- if ( KEY_F2 == key )
- {
- llinfos << "Spawning floater TOS window" << llendl;
- LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,"");
- tos_dialog->startModal();
- return TRUE;
- };
- #endif
- #endif
+ }
+
+# if !LL_RELEASE_FOR_DOWNLOAD
+ if ( KEY_F2 == key )
+ {
+ llinfos << "Spawning floater TOS window" << llendl;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,"");
+ tos_dialog->startModal();
+ return TRUE;
+ }
+# endif
+#endif
if (!called_from_parent)
{
diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp
index da7fc16cef..d0061e281e 100644
--- a/indra/newview/llpanelpermissions.cpp
+++ b/indra/newview/llpanelpermissions.cpp
@@ -787,14 +787,24 @@ void LLPanelPermissions::onClickOwner(void *data)
void LLPanelPermissions::onClickGroup(void* data)
{
+ LLPanelPermissions* panelp = (LLPanelPermissions*)data;
LLUUID owner_id;
LLString name;
BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, name);
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+
if(owners_identical && (owner_id == gAgent.getID()))
{
- LLFloaterGroups* fg;
- fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
- fg->setOkCallback( cbGroupID, data );
+ LLFloaterGroupPicker* fg;
+ fg = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID()));
+ fg->setSelectCallback( cbGroupID, data );
+
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, fg);
+ fg->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(fg);
+ }
}
}
diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp
index c4f958420b..c1885ad898 100644
--- a/indra/newview/llpreview.cpp
+++ b/indra/newview/llpreview.cpp
@@ -42,7 +42,8 @@ LLPreview::LLPreview(const std::string& name) :
mForceClose(FALSE),
mUserResized(FALSE),
mCloseAfterSave(FALSE),
- mAssetStatus(PREVIEW_ASSET_UNLOADED)
+ mAssetStatus(PREVIEW_ASSET_UNLOADED),
+ mItem(NULL)
{
// don't add to instance list, since ItemID is null
mAuxItem = new LLInventoryItem; // (LLPointer is auto-deleted)
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index 2d4a6d11d9..0c11226710 100644
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -1074,9 +1074,6 @@ LLPreviewLSL::LLPreviewLSL(const std::string& name, const LLRect& rect,
gUICtrlFactory->buildFloater(this,"floater_script_preview.xml", &factory_map);
- moveResizeHandleToFront();
-
-
const LLInventoryItem* item = getItem();
childSetCommitCallback("desc", LLPreview::onText, this);
@@ -1600,8 +1597,6 @@ LLLiveLSLEditor::LLLiveLSLEditor(const std::string& name,
LLCallbackMap::map_t factory_map;
factory_map["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this);
- moveResizeHandleToFront();
-
gUICtrlFactory->buildFloater(this,"floater_live_lsleditor.xml", &factory_map);
diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp
index 7df0e48762..22b47eb8ca 100644
--- a/indra/newview/llpreviewsound.cpp
+++ b/indra/newview/llpreviewsound.cpp
@@ -8,15 +8,16 @@
#include "llviewerprecompiledheaders.h"
-#include "llpreviewsound.h"
+#include "audioengine.h"
+#include "llagent.h" // gAgent
#include "llbutton.h"
-#include "llresmgr.h"
#include "llinventory.h"
#include "llinventoryview.h"
-#include "audioengine.h"
-#include "llviewermessage.h" // send_guid_sound_trigger
-#include "llagent.h" // gAgent
#include "lllineeditor.h"
+#include "llpreviewsound.h"
+#include "llresmgr.h"
+#include "llviewercontrol.h"
+#include "llviewermessage.h" // send_guid_sound_trigger
#include "llvieweruictrlfactory.h"
extern LLAudioEngine* gAudiop;
@@ -82,7 +83,7 @@ void LLPreviewSound::auditionSound( void *userdata )
if(item && gAudiop)
{
LLVector3d lpos_global = gAgent.getPositionGlobal();
-
- gAudiop->triggerSound(item->getAssetUUID(), gAgent.getID(), SOUND_GAIN, lpos_global);
+ F32 volume = SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(item->getAssetUUID(), gAgent.getID(), volume, lpos_global);
}
}
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
index 974ee27476..2462bb7bdf 100644
--- a/indra/newview/llpreviewtexture.cpp
+++ b/indra/newview/llpreviewtexture.cpp
@@ -415,7 +415,7 @@ void LLPreviewTexture::updateAspectRatio()
S32 old_left = mRect.mLeft;
if (getHost())
{
- getHost()->growToFit(this, view_width, view_height);
+ getHost()->growToFit(view_width, view_height);
}
else
{
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 117f020793..20761ded11 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -65,6 +65,8 @@
#include "lleventnotifier.h"
#include "llface.h"
#include "llfeaturemanager.h"
+#include "llfirstuse.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterchat.h"
#include "llfloatergesture.h"
#include "llfloaterland.h"
@@ -133,6 +135,7 @@
#include "llfasttimerview.h"
#include "llfloatermap.h"
#include "llweb.h"
+#include "llvoiceclient.h"
#if LL_LIBXUL_ENABLED
#include "llmozlib.h"
@@ -1402,6 +1405,9 @@ BOOL idle_startup()
gAutoLogin = FALSE;
show_connect_box = TRUE;
}
+
+ // Pass the user information to the voice chat server interface.
+ gVoiceClient->userAuthorized(firstname, lastname, gAgentID);
}
else
{
@@ -1700,14 +1706,7 @@ BOOL idle_startup()
{
if (gViewerWindow)
{
- if (gSavedSettings.getBOOL("MuteAudio"))
- {
- LLMediaEngine::updateClass( 0.0f );
- }
- else
- {
- LLMediaEngine::updateClass( gSavedSettings.getF32( "MediaAudioVolume" ) );
- }
+ audio_update_volume(true);
}
#if LL_QUICKTIME_ENABLED // windows only right now but will be ported to mac
@@ -2287,6 +2286,9 @@ BOOL idle_startup()
// On first start, ask user for gender
dialog_choose_gender_first_start();
+ // setup voice
+ LLFirstUse::useVoice();
+
// Start automatic replay if the flag is set.
if (gSavedSettings.getBOOL("StatsAutoRun"))
{
@@ -2314,7 +2316,7 @@ BOOL idle_startup()
}
}
}
-
+
// Clean up the userauth stuff.
if (gUserAuthp)
{
@@ -2323,13 +2325,9 @@ BOOL idle_startup()
}
gStartupState++;
- //RN: unmute audio now that we are entering world
- //JC: But only if the user wants audio working.
- if (gAudiop)
- {
- BOOL mute = gSavedSettings.getBOOL("MuteAudio");
- gAudiop->setMuted(mute);
- }
+
+ // Unmute audio if desired and setup volumes
+ audio_update_volume();
// reset keyboard focus to sane state of pointing at world
gFocusMgr.setKeyboardFocus(NULL, NULL);
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index bc27d9af9f..3df559dee5 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -50,11 +50,14 @@
#include "llviewerparcelmgr.h"
#include "llviewerthrottle.h"
#include "llvieweruictrlfactory.h"
+#include "llvoiceclient.h" // for gVoiceClient
#include "lltoolmgr.h"
#include "llfocusmgr.h"
#include "viewer.h"
+//#include "llfirstuse.h"
+
//
// Globals
//
@@ -76,6 +79,18 @@ const F32 ICON_FLASH_FREQUENCY = 2.f;
const S32 GRAPHIC_FUDGE = 4;
const S32 TEXT_HEIGHT = 18;
+static void onClickParcelInfo(void*);
+static void onClickBalance(void*);
+static void onClickBuyCurrency(void*);
+static void onClickHealth(void*);
+static void onClickFly(void*);
+static void onClickPush(void*);
+static void onClickVoice(void*);
+static void onClickBuild(void*);
+static void onClickScripts(void*);
+static void onClickBuyLand(void*);
+static void onClickScriptDebug(void*);
+
std::vector<std::string> LLStatusBar::sDays;
std::vector<std::string> LLStatusBar::sMonths;
const U32 LLStatusBar::MAX_DATE_STRING_LENGTH = 2000;
@@ -106,15 +121,6 @@ LLStatusBar::LLStatusBar(const std::string& name, const LLRect& rect)
// build date necessary data (must do after panel built)
setupDate();
- mBtnScriptOut = LLUICtrlFactory::getButtonByName( this, "scriptout" );
- mBtnHealth = LLUICtrlFactory::getButtonByName( this, "health" );
- mBtnFly = LLUICtrlFactory::getButtonByName( this, "fly" );
- mBtnBuild = LLUICtrlFactory::getButtonByName( this, "build" );
- mBtnScripts = LLUICtrlFactory::getButtonByName( this, "scripts" );
- mBtnPush = LLUICtrlFactory::getButtonByName( this, "restrictpush" );
- mBtnBuyLand = LLUICtrlFactory::getButtonByName( this, "buyland" );
- mBtnBuyCurrency = LLUICtrlFactory::getButtonByName( this, "buycurrency" );
-
mTextParcelName = LLUICtrlFactory::getTextBoxByName( this, "ParcelNameText" );
mTextBalance = LLUICtrlFactory::getTextBoxByName( this, "BalanceText" );
@@ -176,6 +182,7 @@ BOOL LLStatusBar::postBuild()
childSetAction("build", onClickBuild, this );
childSetAction("scripts", onClickScripts, this );
childSetAction("restrictpush", onClickPush, this );
+ childSetAction("status_voice", onClickVoice, this );
childSetActionTextbox("ParcelNameText", onClickParcelInfo );
childSetActionTextbox("BalanceText", onClickBalance );
@@ -285,13 +292,13 @@ void LLStatusBar::refresh()
{
childGetRect( "scriptout", buttonRect );
r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnScriptOut->setRect(r);
- mBtnScriptOut->setVisible(TRUE);
+ childSetRect("scriptout",r);
+ childSetVisible("scriptout", true);
x += buttonRect.getWidth();
}
else
{
- mBtnScriptOut->setVisible(FALSE);
+ childSetVisible("scriptout", false);
}
if ((region && region->getAllowDamage()) ||
@@ -300,19 +307,19 @@ void LLStatusBar::refresh()
// set visibility based on flashing
if( mHealthTimer->hasExpired() )
{
- mBtnHealth->setVisible( TRUE );
+ childSetVisible("health", true);
}
else
{
BOOL flash = S32(mHealthTimer->getElapsedSeconds() * ICON_FLASH_FREQUENCY) & 1;
- mBtnHealth->setVisible( flash );
+ childSetVisible("health", flash);
}
mTextHealth->setVisible(TRUE);
// Health
childGetRect( "health", buttonRect );
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnHealth->setRect(r);
+ childSetRect("health", r);
x += buttonRect.getWidth();
const S32 health_width = S32( LLFontGL::sSansSerifSmall->getWidth("100%") );
@@ -323,7 +330,7 @@ void LLStatusBar::refresh()
else
{
// invisible if region doesn't allow damage
- mBtnHealth->setVisible(FALSE);
+ childSetVisible("health", false);
mTextHealth->setVisible(FALSE);
}
@@ -332,24 +339,24 @@ void LLStatusBar::refresh()
{
// No Fly Zone
childGetRect( "fly", buttonRect );
- mBtnFly->setVisible(TRUE);
+ childSetVisible( "fly", true );
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnFly->setRect(r);
+ childSetRect( "fly", r );
x += buttonRect.getWidth();
}
else
{
- mBtnFly->setVisible(FALSE);
+ childSetVisible("fly", false);
}
BOOL no_build = parcel && !parcel->getAllowModify();
- mBtnBuild->setVisible( no_build );
+ childSetVisible("build", no_build);
if (no_build)
{
childGetRect( "build", buttonRect );
// No Build Zone
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnBuild->setRect(r);
+ childSetRect( "build", r );
x += buttonRect.getWidth();
}
@@ -361,37 +368,46 @@ void LLStatusBar::refresh()
{
no_scripts = TRUE;
}
- mBtnScripts->setVisible( no_scripts );
+ childSetVisible("scripts", no_scripts);
if (no_scripts)
{
// No scripts
childGetRect( "scripts", buttonRect );
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnScripts->setRect(r);
+ childSetRect( "scripts", r );
x += buttonRect.getWidth();
}
BOOL no_region_push = (region && region->getRestrictPushObject());
BOOL no_push = no_region_push || (parcel && parcel->getRestrictPushObject());
- mBtnPush->setVisible( no_push );
+ childSetVisible("restrictpush", no_push);
if (no_push)
{
childGetRect( "restrictpush", buttonRect );
- // No Push Zone
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnPush->setRect(r);
+ childSetRect( "restrictpush", r );
+ x += buttonRect.getWidth();
+ }
+
+ BOOL have_voice = gVoiceClient->getAreaVoiceDisabled() ? FALSE : TRUE;
+ childSetVisible("status_voice", have_voice);
+ if (have_voice)
+ {
+ childGetRect( "status_voice", buttonRect );
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ childSetRect( "status_voice", r );
x += buttonRect.getWidth();
}
BOOL canBuyLand = parcel
&& !parcel->isPublic()
&& gParcelMgr->canAgentBuyParcel(parcel, false);
- mBtnBuyLand->setVisible(canBuyLand);
+ childSetVisible("buyland", canBuyLand);
if (canBuyLand)
{
childGetRect( "buyland", buttonRect );
r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnBuyLand->setRect(r);
+ childSetRect( "buyland", r );
x += buttonRect.getWidth();
}
@@ -460,7 +476,7 @@ void LLStatusBar::setVisibleForMouselook(bool visible)
mTextTime->setVisible(visible);
mSGBandwidth->setVisible(visible);
mSGPacketLoss->setVisible(visible);
- mBtnBuyCurrency->setVisible(visible);
+ childSetVisible("buycurrency", visible);
setBackgroundVisible(visible);
}
@@ -569,58 +585,55 @@ S32 LLStatusBar::getSquareMetersLeft() const
return mSquareMetersCredit - mSquareMetersCommitted;
}
-// static
-void LLStatusBar::onClickParcelInfo(void* data)
+static void onClickParcelInfo(void* data)
{
gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
LLFloaterLand::show();
}
-// static
-void LLStatusBar::onClickBalance(void* data)
+static void onClickBalance(void* data)
{
LLFloaterBuyCurrency::buyCurrency();
}
-// static
-void LLStatusBar::onClickBuyCurrency(void* data)
+static void onClickBuyCurrency(void* data)
{
LLFloaterBuyCurrency::buyCurrency();
}
-// static
-void LLStatusBar::onClickHealth(void* )
+static void onClickHealth(void* )
{
LLNotifyBox::showXml("NotSafe");
}
-// static
-void LLStatusBar::onClickScriptDebug(void*)
+static void onClickScriptDebug(void*)
{
LLFloaterScriptDebug::show(LLUUID::null);
}
-// static
-void LLStatusBar::onClickFly(void* )
+static void onClickFly(void* )
{
LLNotifyBox::showXml("NoFly");
}
-// static
-void LLStatusBar::onClickPush(void* )
+static void onClickPush(void* )
{
LLNotifyBox::showXml("PushRestricted");
}
-// static
-void LLStatusBar::onClickBuild(void*)
+static void onClickVoice(void* )
+{
+ LLNotifyBox::showXml("VoiceAvailablity");
+ //LLFirstUse::useVoice();
+}
+
+static void onClickBuild(void*)
{
LLNotifyBox::showXml("NoBuild");
}
-// static
-void LLStatusBar::onClickScripts(void*)
+static void onClickScripts(void*)
{
LLViewerRegion* region = gAgent.getRegion();
if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
@@ -637,8 +650,7 @@ void LLStatusBar::onClickScripts(void*)
}
}
-// static
-void LLStatusBar::onClickBuyLand(void*)
+static void onClickBuyLand(void*)
{
gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
gParcelMgr->startBuyLand();
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index 94f758f054..2bbf2bab2d 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -61,19 +61,7 @@ public:
S32 getSquareMetersCommitted() const;
S32 getSquareMetersLeft() const;
-protected:
- static void onClickParcelInfo(void*);
- static void onClickBalance(void*);
- static void onClickBuyCurrency(void*);
- static void onClickRegionInfo(void*);
- static void onClickHealth(void*);
- static void onClickFly(void*);
- static void onClickPush(void*);
- static void onClickBuild(void*);
- static void onClickScripts(void*);
- static void onClickBuyLand(void*);
- static void onClickScriptDebug(void*);
-
+protected:
// simple method to setup the part that holds the date
void setupDate();
@@ -82,15 +70,6 @@ protected:
LLTextBox *mTextHealth;
LLTextBox *mTextTime;
- LLButton *mBtnScriptOut;
- LLButton *mBtnHealth;
- LLButton *mBtnFly;
- LLButton *mBtnBuild;
- LLButton *mBtnScripts;
- LLButton *mBtnPush;
- LLButton *mBtnBuyLand;
-
-
LLTextBox* mTextParcelName;
LLStatGraph *mSGBandwidth;
diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp
index e6cfefa62d..879e99973d 100644
--- a/indra/newview/lltoolbar.cpp
+++ b/indra/newview/lltoolbar.cpp
@@ -26,6 +26,7 @@
#include "llvoavatar.h"
#include "lltooldraganddrop.h"
#include "llinventoryview.h"
+#include "llfloaterchatterbox.h"
#include "llfloaterfriends.h"
#include "llfloatersnapshot.h"
#include "lltoolmgr.h"
@@ -94,15 +95,12 @@ LLToolBar::LLToolBar(const std::string& name, const LLRect& r)
BOOL LLToolBar::postBuild()
{
- childSetAction("im_btn", onClickIM, this);
- childSetControlName("im_btn", "ShowIM");
+ childSetAction("communicate_btn", onClickCommunicate, this);
+ childSetControlName("communicate_btn", "ShowCommunicate");
childSetAction("chat_btn", onClickChat, this);
childSetControlName("chat_btn", "ChatVisible");
- childSetAction("friends_btn", onClickFriends, this);
- childSetControlName("friends_btn", "ShowFriends");
-
childSetAction("appearance_btn", onClickAppearance, this);
childSetControlName("appearance_btn", "");
@@ -313,24 +311,9 @@ void LLToolBar::refresh()
// static
-void LLToolBar::onClickIM(void* user_data)
+void LLToolBar::onClickCommunicate(void* user_data)
{
- if(gIMView->getFloaterOpen())
- {
- // this is if we want Ctrl-T to be simply a toggle
- // gIMView->setFloaterOpen( FALSE );
- // three-state behavior follows
- if(gFocusMgr.childHasKeyboardFocus(gIMView->getFloater()))
- {
- gIMView->setFloaterOpen( FALSE );
- } else {
- gIMView->getFloater()->setFocus( TRUE );
- }
- }
- else
- {
- gIMView->setFloaterOpen( TRUE );
- }
+ LLFloaterChatterBox::toggleInstance(LLSD());
}
@@ -340,14 +323,6 @@ void LLToolBar::onClickChat(void* user_data)
handle_chat(NULL);
}
-
-// static
-void LLToolBar::onClickFriends(void*)
-{
- LLFloaterFriends::toggle();
-}
-
-
// static
void LLToolBar::onClickAppearance(void*)
{
diff --git a/indra/newview/lltoolbar.h b/indra/newview/lltoolbar.h
index f9eee1d4fb..b6e6a073e4 100644
--- a/indra/newview/lltoolbar.h
+++ b/indra/newview/lltoolbar.h
@@ -47,9 +47,8 @@ public:
void refresh();
// callbacks
- static void onClickIM(void*);
+ static void onClickCommunicate(void*);
static void onClickChat(void* data);
- static void onClickFriends(void* data);
static void onClickAppearance(void* data);
static void onClickClothing(void* data);
static void onClickFly(void*);
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index 930d6fa5f2..72a8cefdbd 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1219,7 +1219,6 @@ BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,
return FALSE;
}
}
-std::cout << "ASSET ID: " << new_item->getAssetUUID() << "\n";
hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
}
else if(!item->getPermissions().allowOperationBy(PERM_TRANSFER,
@@ -1233,7 +1232,6 @@ std::cout << "ASSET ID: " << new_item->getAssetUUID() << "\n";
// *FIX: may want to make sure agent can paint hit_obj.
// make sure the object has the texture in it's inventory.
-std::cout << "ASSET ID: " << new_item->getAssetUUID() << "\n";
hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
}
return TRUE;
diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h
index 2203df4287..c93b62c75f 100644
--- a/indra/newview/llviewercontrol.h
+++ b/indra/newview/llviewercontrol.h
@@ -34,9 +34,11 @@ protected:
LLTextEditor* mComment;
};
+// These functions found in llcontroldef.cpp *TODO: clean this up!
//setting variables are declared in this function
void declare_settings();
void fixup_settings();
+void settings_setup_listeners();
// saved at end of session
extern LLControlGroup gSavedSettings;
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index bc0be1b5a2..abcdb8cdbd 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -59,6 +59,8 @@
#include "llfloater.h"
#include "llfloaterabout.h"
#include "llfloaterbuycurrency.h"
+#include "llfloateractivespeakers.h"
+#include "llfloateranimpreview.h"
#include "llfloateravatarinfo.h"
#include "llfloateravatartextures.h"
#include "llfloaterbuildoptions.h"
@@ -71,6 +73,7 @@
#include "llfloatercustomize.h"
#include "llfloaterdirectory.h"
#include "llfloatereditui.h"
+#include "llfloaterchatterbox.h"
#include "llfloaterfriends.h"
#include "llfloatergesture.h"
#include "llfloatergodtools.h"
@@ -2454,7 +2457,7 @@ void set_god_level(U8 god_level)
U8 old_god_level = gAgent.getGodLevel();
gAgent.setGodLevel( god_level );
show_debug_menus();
- gIMView->refresh();
+ gIMMgr->refresh();
gParcelMgr->notifyObservers();
// Some classifieds change visibility on god mode
@@ -2657,7 +2660,7 @@ void request_friendship(const LLUUID& dest_id)
}
if (!fullname.empty())
{
- LLFloaterFriends::requestFriendship(dest_id, fullname);
+ LLPanelFriends::requestFriendship(dest_id, fullname);
LLNotifyBox::showXml("OfferedFriendship", args);
}
else
@@ -5355,7 +5358,7 @@ class LLShowFloater : public view_listener_t
}
else if (floater_name == "friends")
{
- LLFloaterFriends::toggle(NULL);
+ LLFloaterMyFriends::toggleInstance(0);
}
else if (floater_name == "preferences")
{
@@ -5367,11 +5370,11 @@ class LLShowFloater : public view_listener_t
}
else if (floater_name == "chat history")
{
- LLFloaterChat::toggle(NULL);
+ LLFloaterChat::toggleInstance(LLSD());
}
else if (floater_name == "im")
{
- LLToolBar::onClickIM(NULL);
+ LLFloaterChatterBox::toggleInstance(LLSD());
}
else if (floater_name == "inventory")
{
@@ -5480,6 +5483,10 @@ class LLShowFloater : public view_listener_t
{
LLFloaterAbout::show(NULL);
}
+ else if (floater_name == "active speakers")
+ {
+ LLFloaterActiveSpeakers::toggleInstance(LLSD());
+ }
return true;
}
};
@@ -5493,7 +5500,7 @@ class LLFloaterVisible : public view_listener_t
bool new_value = false;
if (floater_name == "friends")
{
- new_value = LLFloaterFriends::visible(NULL);
+ new_value = LLFloaterMyFriends::instanceVisible(0);
}
else if (floater_name == "toolbar")
{
@@ -5505,7 +5512,7 @@ class LLFloaterVisible : public view_listener_t
}
else if (floater_name == "im")
{
- new_value = gIMView && gIMView->mTalkFloater && gIMView->mTalkFloater->getVisible();
+ new_value = LLFloaterMyFriends::instanceVisible(0);
}
else if (floater_name == "mute list")
{
@@ -5523,6 +5530,10 @@ class LLFloaterVisible : public view_listener_t
{
new_value = gDebugView->mStatViewp->getVisible();
}
+ else if (floater_name == "active speakers")
+ {
+ new_value = LLFloaterActiveSpeakers::instanceVisible(LLSD());
+ }
gMenuHolder->findControl(control_name)->setValue(new_value);
return true;
}
@@ -5639,19 +5650,7 @@ class LLShowAgentGroups : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
- LLUUID agent_id;
- if (userdata.asString() == "agent")
- {
- agent_id = gAgent.getID();
- }
- else
- {
- agent_id = userdata.asUUID();
- }
- if(agent_id.notNull())
- {
- LLFloaterGroups::show(agent_id, LLFloaterGroups::AGENT_GROUPS);
- }
+ LLFloaterMyFriends::toggleInstance(1);
return true;
}
};
@@ -6151,10 +6150,10 @@ class LLAvatarSendIM : public view_listener_t
name.append( last->getString() );
}
- gIMView->setFloaterOpen(TRUE);
+ gIMMgr->setFloaterOpen(TRUE);
//EInstantMessage type = have_agent_callingcard(gLastHitObjectID)
// ? IM_SESSION_ADD : IM_SESSION_CARDLESS_START;
- gIMView->addSession(name,
+ gIMMgr->addSession(name,
IM_NOTHING_SPECIAL,
avatar->getID());
}
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 6a9b5a8031..7cf9ca9568 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -51,6 +51,7 @@
#include "llviewercontrol.h"
#include "lldrawpool.h"
#include "llfirstuse.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterbuycurrency.h"
#include "llfloaterbuyland.h"
#include "llfloaterchat.h"
@@ -1317,7 +1318,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
time_t timestamp = (time_t)t;
BOOL is_busy = gAgent.getBusy();
- BOOL is_muted = gMuteListp->isMuted(from_id, name);
+ BOOL is_muted = gMuteListp->isMuted(from_id, name, LLMute::flagTextChat);
BOOL is_linden = gMuteListp->isLinden(name);
BOOL is_owned_by_me = FALSE;
@@ -1368,7 +1369,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
{
// return a standard "busy" message, but only do it to online IM
// (i.e. not other auto responses and not store-and-forward IM)
- if (!gIMView->hasSession(session_id))
+ if (!gIMMgr->hasSession(session_id))
{
// if there is not a panel for this conversation (i.e. it is a new IM conversation
// initiated by the other party) then...
@@ -1393,14 +1394,10 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
snprintf(buffer, sizeof(buffer), "%s%s%s", name, separator_string, (message+message_offset)); /* Flawfinder: ignore */
- if(from_id == gAgentID)
- {
- from_id = LLUUID::null;
- }
llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
// add to IM panel, but do not bother the user
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name,
@@ -1450,15 +1447,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
formatted_time(timestamp, time_buf));
}
snprintf(buffer, sizeof(buffer), "%s%s%s%s", name, separator_string, saved,(message+message_offset)); /* Flawfinder: ignore */
- if(from_id == gAgentID)
- {
- from_id = LLUUID::null;
- }
+
llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
if (!is_muted || is_linden)
{
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name,
@@ -1489,14 +1483,14 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
case IM_TYPING_START:
{
LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
- gIMView->processIMTypingStart(im_info);
+ gIMMgr->processIMTypingStart(im_info);
}
break;
case IM_TYPING_STOP:
{
LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
- gIMView->processIMTypingStop(im_info);
+ gIMMgr->processIMTypingStop(im_info);
}
break;
@@ -1729,12 +1723,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
return;
}
- // System messages, specifically "Foo Bar has left this session"
- // are not shown unless you actually have that session open.
- // Band-aid. JC
- if (offline == IM_ONLINE
- && chat.mFromName == SYSTEM_FROM
- && !gIMView->hasSession(session_id))
+ // Only show messages if we have a session open (which
+ // should happen after you get an "invitation"
+ if ( !gIMMgr->hasSession(session_id) )
{
return;
}
@@ -1754,10 +1745,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
BOOL is_this_agent = FALSE;
if(from_id == gAgentID)
{
- from_id = LLUUID::null;
is_this_agent = TRUE;
}
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name,
@@ -1808,7 +1798,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
else
{
// original code resumes
- gIMView->addMessage(session_id, from_id, name, message);
+ gIMMgr->addMessage(session_id, from_id, name, message);
}
break;
@@ -2074,7 +2064,7 @@ void process_offer_callingcard(LLMessageSystem* msg, void**)
if(!source_name.empty())
{
if (gAgent.getBusy()
- || gMuteListp->isMuted(source_id, source_name))
+ || gMuteListp->isMuted(source_id, source_name, LLMute::flagTextChat))
{
// automatically decline offer
callingcard_offer_callback(1, (void*)offerdata);
@@ -2150,7 +2140,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
BOOL is_linden = FALSE;
if (gMuteListp)
{
- is_muted = gMuteListp->isMuted(from_id, from_name)
+ is_muted = gMuteListp->isMuted(from_id, from_name, LLMute::flagTextChat)
|| gMuteListp->isMuted(owner_id);
is_linden = gMuteListp->isLinden(from_name);
}
@@ -2174,16 +2164,15 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
gWorldPointer->mPartSim.addPartSource(psc);
}
- // only pay attention to other people chatting
+ // record last audible utterance
if (is_audible
- && (is_linden || (!is_muted && !is_busy))
- && chatter != gAgent.getAvatarObject())
+ && (is_linden || (!is_muted && !is_busy)))
{
- gAgent.heardChat(chat);
- if (ll_rand(2) == 0)
+ if (chat.mChatType != CHAT_TYPE_START
+ && chat.mChatType != CHAT_TYPE_STOP)
{
- gAgent.setLookAt(LOOKAT_TARGET_AUTO_LISTEN, chatter, LLVector3::zero);
- }
+ gAgent.heardChat(chat.mFromID);
+ }
}
is_owned_by_me = chatter->permYouOwner();
@@ -2214,6 +2203,8 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
// Look for the start of typing so we can put "..." in the bubbles.
if (CHAT_TYPE_START == chat.mChatType)
{
+ gLocalSpeakerMgr->setSpeakerTyping(from_id, TRUE);
+
// Might not have the avatar constructed yet, eg on login.
if (chatter && chatter->isAvatar())
{
@@ -2223,6 +2214,8 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
}
else if (CHAT_TYPE_STOP == chat.mChatType)
{
+ gLocalSpeakerMgr->setSpeakerTyping(from_id, FALSE);
+
// Might not have the avatar constructed yet, eg on login.
if (chatter && chatter->isAvatar())
{
@@ -2234,6 +2227,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
// We have a real utterance now, so can stop showing "..." and proceed.
if (chatter && chatter->isAvatar())
{
+ gLocalSpeakerMgr->setSpeakerTyping(from_id, FALSE);
((LLVOAvatar*)chatter)->stopTyping();
if (!is_muted && !is_busy)
@@ -2362,7 +2356,7 @@ void process_teleport_start(LLMessageSystem *msg, void**)
gTeleportDisplay = TRUE;
gAgent.setTeleportState( LLAgent::TELEPORT_START );
make_ui_sound("UISndTeleportOut");
-
+
// Don't call LLFirstUse::useTeleport here because this could be
// due to being killed, which would send you home, not to a Telehub
}
@@ -2743,7 +2737,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
{
avatarp->mFootPlane.clearVec();
}
-
+
// reset always run status
msg->newMessageFast(_PREHASH_SetAlwaysRun);
msg->nextBlockFast(_PREHASH_AgentData);
@@ -3207,7 +3201,7 @@ void process_sound_trigger(LLMessageSystem *msg, void **)
if (!gParcelMgr->canHearSound(pos_global)) return;
// Don't play sounds triggered by someone you muted.
- if (gMuteListp->isMuted(owner_id)) return;
+ if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return;
// Don't play sounds from an object you muted
if (gMuteListp->isMuted(object_id)) return;
@@ -3219,7 +3213,8 @@ void process_sound_trigger(LLMessageSystem *msg, void **)
return;
}
- gAudiop->triggerSound(sound_id, owner_id, gain, pos_global);
+ F32 volume = gain * gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(sound_id, owner_id, volume, pos_global);
}
void process_preload_sound(LLMessageSystem *msg, void **user_data)
@@ -3241,7 +3236,7 @@ void process_preload_sound(LLMessageSystem *msg, void **user_data)
if (!objectp) return;
if (gMuteListp->isMuted(object_id)) return;
- if (gMuteListp->isMuted(owner_id)) return;
+ if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return;
LLAudioSource *sourcep = objectp->getAudioSource(owner_id);
if (!sourcep) return;
@@ -3279,7 +3274,7 @@ void process_attached_sound(LLMessageSystem *msg, void **user_data)
if (gMuteListp->isMuted(object_id)) return;
- if (gMuteListp->isMuted(owner_id)) return;
+ if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return;
objectp->setAttachedSound(sound_id, owner_id, gain, flags);
}
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index e53f779473..bbb69594da 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -156,6 +156,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
mLatestRecvPacketID(0),
mData(NULL),
mAudioSourcep(NULL),
+ mAudioGain(1.f),
mAppAngle(0.f),
mPixelArea(1024.f),
mInventory(NULL),
@@ -2697,6 +2698,12 @@ void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent)
BOOL LLViewerObject::updateLOD()
{
+ // Update volume of looping sounds
+ if (mAudioSourcep && mAudioSourcep->isLoop())
+ {
+ F32 volume = mAudioGain * gSavedSettings.getF32("AudioLevelSFX");
+ mAudioSourcep->setGain(volume);
+ }
return FALSE;
}
@@ -4037,7 +4044,7 @@ void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& own
{
LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num);
//If the owner is muted, don't create the system
- if(gMuteListp->isMuted(owner_id)) return;
+ if(gMuteListp->isMuted(owner_id, LLMute::flagParticles)) return;
// We need to be able to deal with a particle source that hasn't changed, but still got an update!
if (pss)
@@ -4086,7 +4093,7 @@ void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_
{
LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp);
//If the owner is muted, don't create the system
- if(gMuteListp->isMuted(owner_id)) return;
+ if(gMuteListp->isMuted(owner_id, LLMute::flagParticles)) return;
// We need to be able to deal with a particle source that hasn't changed, but still got an update!
if (pss)
{
@@ -4204,7 +4211,9 @@ void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& ow
if (mAudioSourcep)
{
BOOL queue = flags & LL_SOUND_FLAG_QUEUE;
- mAudioSourcep->setGain(gain);
+ mAudioGain = gain;
+ F32 volume = gain * gSavedSettings.getF32("AudioLevelSFX");
+ mAudioSourcep->setGain(volume);
mAudioSourcep->setLoop(flags & LL_SOUND_FLAG_LOOP);
mAudioSourcep->setSyncMaster(flags & LL_SOUND_FLAG_SYNC_MASTER);
mAudioSourcep->setSyncSlave(flags & LL_SOUND_FLAG_SYNC_SLAVE);
@@ -4239,12 +4248,12 @@ void LLViewerObject::adjustAudioGain(const F32 gain)
{
return;
}
-
- if (!mAudioSourcep)
+ if (mAudioSourcep)
{
- return;
+ mAudioGain = gain;
+ F32 volume = mAudioGain * gSavedSettings.getF32("AudioLevelSFX");
+ mAudioSourcep->setGain(volume);
}
- mAudioSourcep->setGain(gain);
}
//----------------------------------------------------------------------------
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index f6aaadd40b..f52466cc2d 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -544,8 +544,9 @@ protected:
U8* mData;
LLPointer<LLViewerPartSourceScript> mPartSourcep; // Particle source associated with this object.
- LLAudioSourceVO *mAudioSourcep;
-
+ LLAudioSourceVO* mAudioSourcep;
+ F32 mAudioGain;
+
F32 mAppAngle; // Apparent visual arc in degrees
F32 mPixelArea; // Apparent area in pixels
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
index ed97cd3ec2..017641b278 100644
--- a/indra/newview/llviewerparcelmgr.cpp
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -1772,13 +1772,10 @@ void optionally_start_music(const LLString& music_url)
// now only play music when you enter a new parcel if the control is in PLAY state
// changed as part of SL-4878
- if ( gOverlayBar->getMusicRemoteControl ()->getTransportState () == LLMediaRemoteCtrl::Play )
+ if ( gOverlayBar && gOverlayBar->musicPlaying() )
{
- if (gAudiop)
- {
- gAudiop->startInternetStream(music_url.c_str());
- }
- };
+ LLOverlayBar::musicPlay(NULL);
+ }
}
}
@@ -1791,12 +1788,7 @@ void callback_start_music(S32 option, void* data)
{
gSavedSettings.setBOOL("AudioStreamingMusic", TRUE);
llinfos << "Starting first parcel music " << music_url << llendl;
- if (gAudiop)
- {
- gAudiop->startInternetStream(music_url->c_str());
- LLMediaRemoteCtrl* ctrl = gOverlayBar->getMusicRemoteControl();
- ctrl->setTransportState( LLMediaRemoteCtrl::Play, FALSE );
- }
+ LLOverlayBar::musicPlay(NULL);
}
else
{
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 4040a6d21b..c94aec8801 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1254,9 +1254,10 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("UntrustedSimulatorMessage");
capabilityNames.append("ParcelVoiceInfoRequest");
capabilityNames.append("ChatSessionRequest");
+ capabilityNames.append("ProvisionVoiceAccountRequest");
llinfos << "posting to seed " << url << llendl;
-
+
LLHTTPClient::post(url, capabilityNames, BaseCapabilitiesComplete::build(this));
}
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
index 3969230197..2fff820602 100644
--- a/indra/newview/llviewertexteditor.cpp
+++ b/indra/newview/llviewertexteditor.cpp
@@ -1247,8 +1247,8 @@ void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item )
const F32 SOUND_GAIN = 1.0f;
if(gAudiop)
{
- gAudiop->triggerSound(
- item->getAssetUUID(), gAgentID, SOUND_GAIN, lpos_global);
+ F32 volume = SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(item->getAssetUUID(), gAgentID, volume, lpos_global);
}
showCopyToInvDialog( item );
}
@@ -1417,7 +1417,7 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF
LLViewerTextEditor* text_editor = new LLViewerTextEditor(name,
rect,
max_text_length,
- text,
+ "",
font,
allow_embedded_items);
@@ -1433,7 +1433,18 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF
node->getAttributeBOOL("hide_scrollbar",hide_scrollbar);
text_editor->setHideScrollbarForShortDocs(hide_scrollbar);
+ BOOL hide_border = !text_editor->mBorder->getVisible();
+ node->getAttributeBOOL("hide_border", hide_border);
+ text_editor->setBorderVisible(!hide_border);
+
+ BOOL parse_html = text_editor->mParseHTML;
+ node->getAttributeBOOL("allow_html", parse_html);
+ text_editor->setParseHTML(parse_html);
+
text_editor->initFromXML(node, parent);
+ // add text after all parameters have been set
+ text_editor->appendStyledText(text, FALSE, FALSE, NULL);
+
return text_editor;
}
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index dd2c1c6d76..dbce01246f 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -19,6 +19,8 @@
#include "llviewercamera.h"
//#include "imdebug.h"
+#include "llvoiceclient.h" // for push-to-talk button handling
+
#ifdef SABINRIG
#include "cbw.h"
#endif //SABINRIG
@@ -67,9 +69,11 @@
#include "llfeaturemanager.h"
#include "llfilepicker.h"
#include "llfloater.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterbuildoptions.h"
#include "llfloaterbuyland.h"
#include "llfloaterchat.h"
+#include "llfloaterchatterbox.h"
#include "llfloatercustomize.h"
#include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor
#include "llfloaterland.h"
@@ -588,6 +592,7 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask
// Hide tooltips on mousedown
if( mToolTip )
{
+ mToolTipBlocked = TRUE;
mToolTip->setVisible( FALSE );
}
@@ -1088,6 +1093,21 @@ BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK m
return TRUE;
}
+BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ gVoiceClient->middleMouseState(true);
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ gVoiceClient->middleMouseState(false);
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask)
{
@@ -1104,7 +1124,8 @@ void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask
LLCoordGL prev_saved_mouse_point = mCurrentMousePoint;
LLCoordGL mouse_point(x, y);
saveLastMouse(mouse_point);
- BOOL mouse_actually_moved = (prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY);
+ BOOL mouse_actually_moved = !gFocusMgr.getMouseCapture() && // mouse is not currenty captured
+ ((prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY)); // mouse moved from last recorded position
gMouseIdleTimer.reset();
@@ -1216,6 +1237,9 @@ void LLViewerWindow::handleFocusLost(LLWindow *window)
BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
{
+ // Let the voice chat code check for its PTT key. Note that this never affects event processing.
+ gVoiceClient->keyDown(key, mask);
+
if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
{
gAgent.clearAFK();
@@ -1235,6 +1259,9 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask)
{
+ // Let the voice chat code check for its PTT key. Note that this never affects event processing.
+ gVoiceClient->keyUp(key, mask);
+
return FALSE;
}
@@ -1277,16 +1304,7 @@ BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
}
// Unmute audio
- if (!gSavedSettings.getBOOL("MuteAudio"))
- {
- if (gAudiop) gAudiop->setMuted(FALSE);
- F32 volume = gSavedSettings.getF32("MediaAudioVolume");
- if(LLMediaEngine::getInstance())
- {
- LLMediaEngine::getInstance()->setVolume(volume);
- LLMediaEngine::updateClass(volume);
- }
- }
+ audio_update_volume();
}
else
{
@@ -1301,14 +1319,7 @@ BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
stopGL();
}
// Mute audio
- if (gSavedSettings.getBOOL("MuteWhenMinimized"))
- {
- llinfos << "Muting audio on minimize" << llendl;
- if (gAudiop) gAudiop->setMuted(TRUE);
- F32 volume = 0.f;
- LLMediaEngine::getInstance()->setVolume(volume);
- LLMediaEngine::updateClass(volume);
- }
+ audio_update_volume();
}
return TRUE;
}
@@ -1708,7 +1719,7 @@ void LLViewerWindow::initBase()
LLRect notify_rect = full_window;
//notify_rect.mTop -= 24;
notify_rect.mBottom += STATUS_BAR_HEIGHT;
- gNotifyBoxView = new LLNotifyBoxView("notify", notify_rect, FALSE, FOLLOWS_ALL);
+ gNotifyBoxView = new LLNotifyBoxView("notify_container", notify_rect, FALSE, FOLLOWS_ALL);
mRootView->addChild(gNotifyBoxView, -2);
// Tooltips go above floaters
@@ -1862,16 +1873,12 @@ void LLViewerWindow::initWorldUI()
LLFloaterMove::show(NULL);
}
- // Must have one global chat floater so it can actually store
- // the history. JC
- gFloaterChat = new LLFloaterChat();
- gFloaterChat->setVisible( FALSE );
-
- if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) gFloaterChat->loadHistory();
+ gIMMgr = LLIMMgr::getInstance();
- gIMView = new LLIMView("gIMView", LLRect() );
- gIMView->setFollowsAll();
- mRootView->addChild(gIMView);
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
+ {
+ LLFloaterChat::getInstance(LLSD())->loadHistory();
+ }
LLRect morph_view_rect = full_window;
morph_view_rect.stretch( -STATUS_BAR_HEIGHT );
@@ -1921,6 +1928,7 @@ void LLViewerWindow::initWorldUI()
// sync bg color with menu bar
gStatusBar->setBackgroundColor( gMenuBarView->getBackgroundColor() );
+ LLFloaterChatterBox::createInstance(LLSD());
gViewerWindow->getRootView()->addChild(gStatusBar);
@@ -1950,13 +1958,12 @@ LLViewerWindow::~LLViewerWindow()
gFloaterTools = NULL;
gStatusBar = NULL;
gFloaterCamera = NULL;
- gIMView = NULL;
+ gIMMgr = NULL;
gHoverView = NULL;
gFloaterView = NULL;
gMorphView = NULL;
- gFloaterChat = NULL;
gFloaterMute = NULL;
gFloaterMap = NULL;
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index 91bf0d9918..bb60c8283e 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -65,6 +65,8 @@ public:
/*virtual*/ void handleQuit(LLWindow *window);
/*virtual*/ BOOL handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ void handleMouseLeave(LLWindow *window);
/*virtual*/ void handleResize(LLWindow *window, S32 x, S32 y);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 1af8efaf79..60cce34b72 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -96,6 +96,12 @@
//#include "vtune/vtuneapi.h"
+//Ventrella
+#include "llgesturemgr.h" //needed to trigger the voice gestculations
+#include "llvoicevisualizer.h"
+#include "llvoiceclient.h"
+//end Ventrella
+
// Direct imports, evil
extern LLSky gSky;
extern void set_avatar_character(void* charNameArg);
@@ -127,9 +133,7 @@ const F32 PELVIS_LAG_WALKING = 0.4f; // ...while walking
const F32 PELVIS_LAG_MOUSELOOK = 0.15f;
const F32 MOUSELOOK_PELVIS_FOLLOW_FACTOR = 0.5f;
-//Ventrella
const F32 PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON = 0.0001f; // not zero! - something gets divided by this!
-//end Ventrella
#define PELVIS_ROT_THRESHOLD_SLOW 60.0f // amount of deviation allowed between
#define PELVIS_ROT_THRESHOLD_FAST 2.0f // the pelvis and the view direction
@@ -201,10 +205,6 @@ const F32 CHAT_FADE_TIME = 8.0;
const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f;
const S32 MAX_BUBBLES = 7;
-
-const bool USING_VENTRELLA_AVATAR_MOTION_TEST = false;
-
-
S32 LLVOAvatar::sMaxVisible = 50;
LLVOAvatar::ETextureIndex LLVOAvatar::sBakedTextureIndices[BAKED_TEXTURE_COUNT] =
@@ -435,121 +435,16 @@ public:
// called after parameters have been set
// must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character)
- {
- //Ventrella
- // I'm replacing the code below because I need to change
- // the logic in order to add other body parts
- /*
+ {
mCharacter = character;
-
- if (!mChestState.setJoint( character->getJoint("mChest")))
- {
- return STATUS_FAILURE;
- }
-
- mChestState.setUsage(LLJointState::ROT);
-
- addJointState( &mChestState );
- return STATUS_SUCCESS;
- */
-
-
bool success = true;
if ( !mChestState.setJoint( character->getJoint( "mChest" ) ) ) { success = false; }
- if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
- {
- if ( !mNeckState.setJoint ( character->getJoint( "mNeck" )) ) { success = false; }
-
- if ( !mCollarLeftState.setJoint ( character->getJoint( "mCollarLeft" )) ) { success = false; }
- if ( !mShoulderLeftState.setJoint ( character->getJoint( "mShoulderLeft" )) ) { success = false; }
- if ( !mElbowLeftState.setJoint ( character->getJoint( "mElbowLeft" )) ) { success = false; }
- if ( !mWristLeftState.setJoint ( character->getJoint( "mWristLeft" )) ) { success = false; }
-
- if ( !mCollarRightState.setJoint ( character->getJoint( "mCollarRight" )) ) { success = false; }
- if ( !mShoulderRightState.setJoint ( character->getJoint( "mShoulderRight" )) ) { success = false; }
- if ( !mElbowRightState.setJoint ( character->getJoint( "mElbowRight" )) ) { success = false; }
- if ( !mWristRightState.setJoint ( character->getJoint( "mWristRight" )) ) { success = false; }
-
- if ( !mHipLeftState.setJoint ( character->getJoint( "mHipLeft" )) ) { success = false; }
- if ( !mKneeLeftState.setJoint ( character->getJoint( "mKneeLeft" )) ) { success = false; }
- if ( !mAnkleLeftState.setJoint ( character->getJoint( "mAnkleLeft" )) ) { success = false; }
-
- if ( !mHipRightState.setJoint ( character->getJoint( "mHipRight" )) ) { success = false; }
- if ( !mKneeRightState.setJoint ( character->getJoint( "mKneeRight" )) ) { success = false; }
- if ( !mAnkleRightState.setJoint ( character->getJoint( "mAnkleRight" )) ) { success = false; }
- }
-
if ( success )
{
mChestState.setUsage(LLJointState::ROT);
addJointState( &mChestState );
-
- if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
- {
- //-------------------------------------------
- // neck
- //-------------------------------------------
- mNeckState.setUsage(LLJointState::ROT);
- addJointState( &mNeckState );
-
- //-------------------------------------------
- // left arm
- //-------------------------------------------
- mCollarLeftState.setUsage(LLJointState::ROT);
- addJointState( &mCollarLeftState );
-
- mShoulderLeftState.setUsage(LLJointState::ROT);
- addJointState( &mShoulderLeftState );
-
- mElbowLeftState.setUsage(LLJointState::ROT);
- addJointState( &mElbowLeftState );
-
- mWristLeftState.setUsage(LLJointState::ROT);
- addJointState( &mWristLeftState );
-
-
- //-------------------------------------------
- // right arm
- //-------------------------------------------
- mCollarRightState.setUsage(LLJointState::ROT);
- addJointState( &mCollarRightState );
-
- mShoulderRightState.setUsage(LLJointState::ROT);
- addJointState( &mShoulderRightState );
-
- mElbowRightState.setUsage(LLJointState::ROT);
- addJointState( &mElbowRightState );
-
- mWristRightState.setUsage(LLJointState::ROT);
- addJointState( &mWristRightState );
-
- //-------------------------------------------
- // left leg
- //-------------------------------------------
- mHipLeftState.setUsage(LLJointState::ROT);
- addJointState( &mHipLeftState );
-
- mKneeLeftState.setUsage(LLJointState::ROT);
- addJointState( &mKneeLeftState );
-
- mAnkleLeftState.setUsage(LLJointState::ROT);
- addJointState( &mAnkleLeftState );
-
-
- //-------------------------------------------
- // right leg
- //-------------------------------------------
- mHipRightState.setUsage(LLJointState::ROT);
- addJointState( &mHipRightState );
-
- mKneeRightState.setUsage(LLJointState::ROT);
- addJointState( &mKneeRightState );
-
- mAnkleRightState.setUsage(LLJointState::ROT);
- addJointState( &mAnkleRightState );
- }
}
if ( success )
@@ -560,7 +455,6 @@ public:
{
return STATUS_FAILURE;
}
- //end Ventrella
}
// called when a motion is activated
@@ -579,37 +473,9 @@ public:
mChestState.setRotation(LLQuaternion(breathe_amt, LLVector3(0.f, 1.f, 0.f)));
- //Ventrella
- if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
- {
- F32 wave = ( sinf ( time * 2.0f ) * 0.5f );
-
- mChestState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
-
- mCollarLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
- mShoulderLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
- mElbowLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
- mWristLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
-
- mCollarRightState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
- mShoulderRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
- mElbowRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
- mWristRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
-
- mHipLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
- mKneeLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
- mAnkleLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
-
- mHipRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
- mKneeRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
- mAnkleRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
- }
- //end Ventrella
-
return TRUE;
}
-
// called when a motion is deactivated
virtual void onDeactivate() {}
@@ -617,26 +483,7 @@ public:
//-------------------------------------------------------------------------
// joint states to be animated
//-------------------------------------------------------------------------
- LLJointState mChestState;
-
- //Ventrella
- LLJointState mNeckState;
- LLJointState mCollarLeftState;
- LLJointState mShoulderLeftState;
- LLJointState mElbowLeftState;
- LLJointState mWristLeftState;
- LLJointState mCollarRightState;
- LLJointState mShoulderRightState;
- LLJointState mElbowRightState;
- LLJointState mWristRightState;
- LLJointState mHipLeftState;
- LLJointState mKneeLeftState;
- LLJointState mAnkleLeftState;
- LLJointState mHipRightState;
- LLJointState mKneeRightState;
- LLJointState mAnkleRightState;
- //end Ventrella
-
+ LLJointState mChestState;
F32 mBreatheRate;
LLCharacter* mCharacter;
};
@@ -799,6 +646,10 @@ LLVOAvatar::LLVOAvatar(
LLMemType mt(LLMemType::MTYPE_AVATAR);
//VTResume(); // VTune
+
+ // mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline
+ bool needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job
+ mVoiceVisualizer = ( LLVoiceVisualizer *)gHUDManager->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim );
lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl;
@@ -1049,9 +900,13 @@ LLVOAvatar::LLVOAvatar(
createMotion( ANIM_AGENT_CUSTOMIZE_DONE);
//VTPause(); // VTune
+
+ //Ventrella
+ mVoiceVisualizer->setVoiceEnabled( gVoiceClient->getVoiceEnabled( mID ) );
+ mCurrentGesticulationLevel = 0;
+ //END Ventrella
}
-
//------------------------------------------------------------------------
// LLVOAvatar::~LLVOAvatar()
//------------------------------------------------------------------------
@@ -1123,6 +978,8 @@ void LLVOAvatar::markDead()
sNumVisibleChatBubbles--;
}
+ mVoiceVisualizer->markDead();
+
mBeam = NULL;
LLViewerObject::markDead();
}
@@ -2439,7 +2296,84 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
updateCharacter(agent);
+ //Ventrella
+ bool voiceEnabled = gVoiceClient->getVoiceEnabled( mID ) && gVoiceClient->inProximalChannel();
+ // disable voice visualizer when in mouselook
+ mVoiceVisualizer->setVoiceEnabled( voiceEnabled && !(mIsSelf && gAgent.cameraMouselook()) );
+ if ( voiceEnabled )
+ {
+ //----------------------------------------------------------------
+ // Only do gesture triggering for your own avatar, and only when you're in a proximal channel.
+ //----------------------------------------------------------------
+ if( mIsSelf )
+ {
+ //----------------------------------------------------------------------------------------
+ // The following takes the voice signal and uses that to trigger gesticulations.
+ //----------------------------------------------------------------------------------------
+ int lastGesticulationLevel = mCurrentGesticulationLevel;
+ mCurrentGesticulationLevel = mVoiceVisualizer->getCurrentGesticulationLevel();
+
+ //---------------------------------------------------------------------------------------------------
+ // If "current gesticulation level" changes, we catch this, and trigger the new gesture
+ //---------------------------------------------------------------------------------------------------
+ if ( lastGesticulationLevel != mCurrentGesticulationLevel )
+ {
+ if ( mCurrentGesticulationLevel != VOICE_GESTICULATION_LEVEL_OFF )
+ {
+ LLString gestureString = "unInitialized";
+ if ( mCurrentGesticulationLevel == 0 ) { gestureString = "/voicelevel1"; }
+ else if ( mCurrentGesticulationLevel == 1 ) { gestureString = "/voicelevel2"; }
+ else if ( mCurrentGesticulationLevel == 2 ) { gestureString = "/voicelevel3"; }
+ else { printf( "oops - CurrentGesticulationLevel can be only 0, 1, or 2\n" ); }
+
+ // this is the call that Karl S. created for triggering gestures from within the code.
+ gGestureManager.triggerAndReviseString( gestureString );
+ }
+ }
+
+ } //if( mIsSelf )
+ //-----------------------------------------------------------------------------------------------------------------
+ // If the avatar is speaking, then the voice amplitude signal is passed to the voice visualizer.
+ // Also, here we trigger voice visualizer start and stop speaking, so it can animate the voice symbol.
+ //
+ // Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been
+ // "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking.
+ //-----------------------------------------------------------------------------------------------------------------
+ if ( gVoiceClient->getIsSpeaking( mID ) )
+ {
+ if ( ! mVoiceVisualizer->getCurrentlySpeaking() )
+ {
+ mVoiceVisualizer->setStartSpeaking();
+
+ //printf( "gAwayTimer.reset();\n" );
+ }
+
+ mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) );
+
+ if( mIsSelf )
+ {
+ gAgent.clearAFK();
+ }
+ }
+ else
+ {
+ if ( mVoiceVisualizer->getCurrentlySpeaking() )
+ {
+ mVoiceVisualizer->setStopSpeaking();
+ }
+ }
+
+ //--------------------------------------------------------------------------------------------
+ // here we get the approximate head position and set as sound source for the voice symbol
+ // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)
+ //--------------------------------------------------------------------------------------------
+ LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );
+ mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot.getWorldPosition() + headOffset );
+
+ }//if ( voiceEnabled )
+ //End Ventrella
+
if (LLVOAvatar::sJointDebug)
{
@@ -2449,7 +2383,6 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
LLJoint::sNumUpdates = 0;
LLJoint::sNumTouches = 0;
-
if (gNoRender)
{
return TRUE;
@@ -3252,14 +3185,7 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
LLVector3 pelvisDir( mRoot.getWorldMatrix().getFwdRow4().mV );
F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, PELVIS_ROT_THRESHOLD_SLOW, PELVIS_ROT_THRESHOLD_FAST);
-
- //Ventrella
- //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
- //{
- // pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, 1.0f, 1.0f);
- //}
- //end Ventrella
-
+
if (self_in_mouselook)
{
pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR;
@@ -3337,13 +3263,6 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
pelvis_lag_time = PELVIS_LAG_WALKING;
}
- //Ventrella
- //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
- //{
- // pelvis_lag_time = PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON;
- //}
- //end Ventrella
-
F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);
mRoot.setWorldRotation( slerp(u, mRoot.getWorldRotation(), wQv) );
@@ -3461,13 +3380,13 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED,
// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN );
- F32 gain = gSavedSettings.getF32("AudioLevelFootsteps");
+ F32 gain = .30f * gSavedSettings.getF32("AudioLevelAmbient");
LLUUID& step_sound_id = getStepSound();
LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent);
if (gParcelMgr && gParcelMgr->canHearSound(foot_pos_global)
- && gMuteListp && !gMuteListp->isMuted(getID()))
+ && gMuteListp && !gMuteListp->isMuted(getID(), LLMute::flagObjectSounds))
{
gAudiop->triggerSound(step_sound_id, getID(), gain, foot_pos_global);
}
@@ -3476,6 +3395,32 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
mRoot.updateWorldMatrixChildren();
+ // Send the speaker position to the spatialized voice system.
+ if(mIsSelf)
+ {
+ LLMatrix3 rot;
+ LLVector3d pos;
+#if 1
+ // character rotation (stable, shouldn't move with animations)
+ rot = mRoot.getWorldRotation().getMatrix3();
+#else
+ // actual head rotation (moves with animations, probably a bit too much)
+ rot.setRows(
+ LLVector3::x_axis * mSkullp->getWorldRotation(),
+ LLVector3::y_axis * mSkullp->getWorldRotation(),
+ LLVector3::z_axis * mSkullp->getWorldRotation());
+#endif
+
+ pos = getPositionGlobal();
+ pos += LLVector3d(mHeadOffset);
+
+ // MBW -- XXX -- Setting velocity to 0 for now. May figure it out later...
+ gVoiceClient->setAvatarPosition(
+ pos, // position
+ LLVector3::zero, // velocity
+ rot); // rotation matrix
+ }
+
if (!mDebugText.size() && mText.notNull())
{
mText->markDead();
@@ -4398,18 +4343,20 @@ BOOL LLVOAvatar::processSingleAnimationStateChange( const LLUUID& anim_id, BOOL
{
LLVector3d char_pos_global = gAgent.getPosGlobalFromAgent(getCharacterPosition());
if (gParcelMgr && gParcelMgr->canHearSound(char_pos_global)
- && gMuteListp && !gMuteListp->isMuted(getID()))
+ && gMuteListp && !gMuteListp->isMuted(getID(), LLMute::flagObjectSounds))
{
// RN: uncomment this to play on typing sound at fixed volume once sound engine is fixed
// to support both spatialized and non-spatialized instances of the same sound
//if (mIsSelf)
//{
- // gAudiop->triggerSound(LLUUID(gSavedSettings.getString("UISndTyping")), 0.8f);
+ // F32 volume = gain * gSavedSettings.getF32("AudioLevelUI")
+ // gAudiop->triggerSound(LLUUID(gSavedSettings.getString("UISndTyping")), volume);
//}
//else
{
LLUUID sound_id = LLUUID(gSavedSettings.getString("UISndTyping"));
- gAudiop->triggerSound(sound_id, getID(), 1.f, char_pos_global);
+ F32 volume = gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(sound_id, getID(), volume, char_pos_global);
}
}
}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index c98826d15b..0eac13206b 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -32,6 +32,11 @@
#include "llxmltree.h"
#include "llwearable.h"
+//Ventrella
+//#include "llvoiceclient.h"
+#include "llvoicevisualizer.h"
+//End Ventrella
+
const S32 VOAVATAR_SCRATCH_TEX_WIDTH = 512;
const S32 VOAVATAR_SCRATCH_TEX_HEIGHT = 512;
@@ -927,6 +932,17 @@ protected:
LLTexLayerSet* getLayerSet(ETextureIndex index) const;
LLHost getObjectHost() const;
S32 getLocalDiscardLevel( S32 index);
+
+//Ventrella
+ //-----------------------------------------------------------------------------------------------
+ // the Voice Visualizer is responsible for detecting the user's voice signal, and when the
+ // user speaks, it puts a voice symbol over the avatar's head, and triggering gesticulations
+ //-----------------------------------------------------------------------------------------------
+ private:
+ LLVoiceVisualizer * mVoiceVisualizer;
+ int mCurrentGesticulationLevel;
+//End Ventrella
+
};
#endif // LL_VO_AVATAR_H
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
new file mode 100644
index 0000000000..0dba3588e5
--- /dev/null
+++ b/indra/newview/llvoiceclient.cpp
@@ -0,0 +1,4056 @@
+/**
+ * @file llvoiceclient.cpp
+ * @brief Implementation of LLVoiceClient class which is the interface to the voice client process.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <boost/tokenizer.hpp>
+
+#include "llviewerprecompiledheaders.h"
+#include "llvoiceclient.h"
+
+#include "llsdutil.h"
+
+#include "llvoavatar.h"
+#include "llbufferstream.h"
+#include "llfile.h"
+#include "expat/expat.h"
+#include "llcallbacklist.h"
+#include "llviewerregion.h"
+#include "llviewernetwork.h" // for gUserServerChoice
+#include "llfloateractivespeakers.h" // for LLSpeakerMgr
+#include "llbase64.h"
+#include "llviewercontrol.h"
+#include "llkeyboard.h"
+#include "viewer.h" // for gDisconnected, gDisableVoice
+#include "llmutelist.h" // to check for muted avatars
+#include "llagent.h"
+#include "llcachename.h"
+#include "llimview.h" // for LLIMMgr
+#include "llimpanel.h" // for LLVoiceChannel
+#include "llparcel.h"
+#include "llviewerparcelmgr.h"
+#include "llfirstuse.h"
+#include "llviewerwindow.h"
+
+// for base64 decoding
+#include "apr-1/apr_base64.h"
+
+// for SHA1 hash
+#include "apr-1/apr_sha1.h"
+
+// If we are connecting to agni AND the user's last name is "Linden", join this channel instead of looking up the sim name.
+// If we are connecting to agni and the user's last name is NOT "Linden", disable voice.
+#define AGNI_LINDENS_ONLY_CHANNEL "SL"
+static bool sConnectingToAgni = false;
+F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
+
+const F32 SPEAKING_TIMEOUT = 1.f;
+
+const int VOICE_MAJOR_VERSION = 1;
+const int VOICE_MINOR_VERSION = 0;
+
+LLVoiceClient *gVoiceClient = NULL;
+
+// Don't retry connecting to the daemon more frequently than this:
+const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
+
+// Don't send positional updates more frequently than this:
+const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
+
+const F32 LOGIN_RETRY_SECONDS = 10.0f;
+const int MAX_LOGIN_RETRIES = 12;
+
+class LLViewerVoiceAccountProvisionResponder :
+ public LLHTTPClient::Responder
+{
+public:
+ LLViewerVoiceAccountProvisionResponder(int retries)
+ {
+ mRetries = retries;
+ }
+
+ virtual void error(U32 status, const std::string& reason)
+ {
+ if ( mRetries > 0 )
+ {
+ if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
+ mRetries - 1);
+ }
+ else
+ {
+ //TODO: throw an error message?
+ if ( gVoiceClient ) gVoiceClient->giveUp();
+ }
+ }
+
+ virtual void result(const LLSD& content)
+ {
+ if ( gVoiceClient )
+ {
+ gVoiceClient->login(
+ content["username"].asString(),
+ content["password"].asString());
+ }
+ }
+
+private:
+ int mRetries;
+};
+
+/**
+ * @class LLVivoxProtocolParser
+ * @brief This class helps construct new LLIOPipe specializations
+ * @see LLIOPipe
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLVivoxProtocolParser : public LLIOPipe
+{
+ LOG_CLASS(LLVivoxProtocolParser);
+public:
+ LLVivoxProtocolParser();
+ virtual ~LLVivoxProtocolParser();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+ std::string mInput;
+
+ // Expat control members
+ XML_Parser parser;
+ int responseDepth;
+ bool ignoringTags;
+ bool isEvent;
+ int ignoreDepth;
+
+ // Members for processing responses. The values are transient and only valid within a call to processResponse().
+ int returnCode;
+ int statusCode;
+ std::string statusString;
+ std::string uuidString;
+ std::string actionString;
+ std::string connectorHandle;
+ std::string accountHandle;
+ std::string sessionHandle;
+ std::string eventSessionHandle;
+
+ // Members for processing events. The values are transient and only valid within a call to processResponse().
+ std::string eventTypeString;
+ int state;
+ std::string uriString;
+ bool isChannel;
+ std::string nameString;
+ std::string audioMediaString;
+ std::string displayNameString;
+ int participantType;
+ bool isLocallyMuted;
+ bool isModeratorMuted;
+ bool isSpeaking;
+ int volume;
+ F32 energy;
+
+ // Members for processing text between tags
+ std::string textBuffer;
+ bool accumulateText;
+
+ void reset();
+
+ void processResponse(std::string tag);
+
+static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr);
+static void XMLCALL ExpatEndTag(void *data, const char *el);
+static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len);
+
+ void StartTag(const char *tag, const char **attr);
+ void EndTag(const char *tag);
+ void CharData(const char *buffer, int length);
+
+};
+
+LLVivoxProtocolParser::LLVivoxProtocolParser()
+{
+ parser = NULL;
+ parser = XML_ParserCreate(NULL);
+
+ reset();
+}
+
+void LLVivoxProtocolParser::reset()
+{
+ responseDepth = 0;
+ ignoringTags = false;
+ accumulateText = false;
+ textBuffer.clear();
+}
+
+//virtual
+LLVivoxProtocolParser::~LLVivoxProtocolParser()
+{
+ if (parser)
+ XML_ParserFree(parser);
+}
+
+// virtual
+LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ LLBufferStream istr(channels, buffer.get());
+ std::ostringstream ostr;
+ while (istr.good())
+ {
+ char buf[1024];
+ istr.read(buf, sizeof(buf));
+ mInput.append(buf, istr.gcount());
+ }
+
+ // MBW -- XXX -- This should no longer be necessary. Or even possible.
+ // We've read all the data out of the buffer. Make sure it doesn't accumulate.
+// buffer->clear();
+
+ // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser.
+ int start = 0;
+ int delim;
+ while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
+ {
+ // Turn this on to log incoming XML
+ if(0)
+ {
+ int foo = mInput.find("Set3DPosition", start);
+ int bar = mInput.find("ParticipantPropertiesEvent", start);
+ if(foo != std::string::npos && (foo < delim))
+ {
+ // This is a Set3DPosition response. Don't print it, since these are way too spammy.
+ }
+ else if(bar != std::string::npos && (bar < delim))
+ {
+ // This is a ParticipantPropertiesEvent response. Don't print it, since these are way too spammy.
+ }
+ else
+ {
+ llinfos << "parsing: " << mInput.substr(start, delim - start) << llendl;
+ }
+ }
+
+ // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
+ reset();
+
+ XML_ParserReset(parser, NULL);
+ XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag);
+ XML_SetCharacterDataHandler(parser, ExpatCharHandler);
+ XML_SetUserData(parser, this);
+ XML_Parse(parser, mInput.data() + start, delim - start, false);
+
+ start = delim + 3;
+ }
+
+ if(start != 0)
+ mInput = mInput.substr(start);
+
+// llinfos << "at end, mInput is: " << mInput << llendl;
+
+ if(!gVoiceClient->mConnected)
+ {
+ // If voice has been disabled, we just want to close the socket. This does so.
+ llinfos << "returning STATUS_STOP" << llendl;
+ return STATUS_STOP;
+ }
+
+ return STATUS_OK;
+}
+
+void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr)
+{
+ if (data)
+ {
+ LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
+ object->StartTag(el, attr);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el)
+{
+ if (data)
+ {
+ LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
+ object->EndTag(el);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len)
+{
+ if (data)
+ {
+ LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
+ object->CharData(s, len);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+
+void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
+{
+ // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags
+ textBuffer.clear();
+ // only accumulate text if we're not ignoring tags.
+ accumulateText = !ignoringTags;
+
+ if (responseDepth == 0)
+ {
+ isEvent = strcmp("Event", tag) == 0;
+
+ if (strcmp("Response", tag) == 0 || isEvent)
+ {
+ // Grab the attributes
+ while (*attr)
+ {
+ const char *key = *attr++;
+ const char *value = *attr++;
+
+ if (strcmp("requestId", key) == 0)
+ {
+ uuidString = value;
+ }
+ else if (strcmp("action", key) == 0)
+ {
+ actionString = value;
+ }
+ else if (strcmp("type", key) == 0)
+ {
+ eventTypeString = value;
+ }
+ }
+ }
+ //llinfos << tag << " (" << responseDepth << ")" << llendl;
+ }
+ else
+ {
+ if (ignoringTags)
+ {
+ //llinfos << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << llendl;
+ }
+ else
+ {
+ //llinfos << tag << " (" << responseDepth << ")" << llendl;
+
+ // Ignore the InputXml stuff so we don't get confused
+ if (strcmp("InputXml", tag) == 0)
+ {
+ ignoringTags = true;
+ ignoreDepth = responseDepth;
+ accumulateText = false;
+
+ //llinfos << "starting ignore, ignoreDepth is " << ignoreDepth << llendl;
+ }
+ else if (strcmp("CaptureDevices", tag) == 0)
+ {
+ gVoiceClient->clearCaptureDevices();
+ }
+ else if (strcmp("RenderDevices", tag) == 0)
+ {
+ gVoiceClient->clearRenderDevices();
+ }
+ }
+ }
+ responseDepth++;
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::EndTag(const char *tag)
+{
+ const char *string = textBuffer.c_str();
+ bool clearbuffer = true;
+
+ responseDepth--;
+
+ if (ignoringTags)
+ {
+ if (ignoreDepth == responseDepth)
+ {
+ //llinfos << "end of ignore" << llendl;
+ ignoringTags = false;
+ }
+ else
+ {
+ //llinfos << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << llendl;
+ }
+ }
+
+ if (!ignoringTags)
+ {
+ //llinfos << "processing tag " << tag << " (depth = " << responseDepth << ")" << llendl;
+
+ // Closing a tag. Finalize the text we've accumulated and reset
+ if (strcmp("ReturnCode", tag) == 0)
+ returnCode = strtol(string, NULL, 10);
+ else if (strcmp("StatusCode", tag) == 0)
+ statusCode = strtol(string, NULL, 10);
+ else if (strcmp("ConnectorHandle", tag) == 0)
+ connectorHandle = string;
+ else if (strcmp("AccountHandle", tag) == 0)
+ accountHandle = string;
+ else if (strcmp("SessionHandle", tag) == 0)
+ {
+ if (isEvent)
+ eventSessionHandle = string;
+ else
+ sessionHandle = string;
+ }
+ else if (strcmp("StatusString", tag) == 0)
+ statusString = string;
+ else if (strcmp("State", tag) == 0)
+ state = strtol(string, NULL, 10);
+ else if (strcmp("URI", tag) == 0)
+ uriString = string;
+ else if (strcmp("IsChannel", tag) == 0)
+ isChannel = strcmp(string, "true") == 0;
+ else if (strcmp("Name", tag) == 0)
+ nameString = string;
+ else if (strcmp("AudioMedia", tag) == 0)
+ audioMediaString = string;
+ else if (strcmp("ChannelName", tag) == 0)
+ nameString = string;
+ else if (strcmp("ParticipantURI", tag) == 0)
+ uriString = string;
+ else if (strcmp("DisplayName", tag) == 0)
+ displayNameString = string;
+ else if (strcmp("AccountName", tag) == 0)
+ nameString = string;
+ else if (strcmp("ParticipantTyppe", tag) == 0)
+ participantType = strtol(string, NULL, 10);
+ else if (strcmp("IsLocallyMuted", tag) == 0)
+ isLocallyMuted = strcmp(string, "true") == 0;
+ else if (strcmp("IsModeratorMuted", tag) == 0)
+ isModeratorMuted = strcmp(string, "true") == 0;
+ else if (strcmp("IsSpeaking", tag) == 0)
+ isSpeaking = strcmp(string, "true") == 0;
+ else if (strcmp("Volume", tag) == 0)
+ volume = strtol(string, NULL, 10);
+ else if (strcmp("Energy", tag) == 0)
+ energy = (F32)strtod(string, NULL);
+ else if (strcmp("MicEnergy", tag) == 0)
+ energy = (F32)strtod(string, NULL);
+ else if (strcmp("ChannelName", tag) == 0)
+ nameString = string;
+ else if (strcmp("ChannelURI", tag) == 0)
+ uriString = string;
+ else if (strcmp("ChannelListResult", tag) == 0)
+ {
+ gVoiceClient->addChannelMapEntry(nameString, uriString);
+ }
+ else if (strcmp("Device", tag) == 0)
+ {
+ // This closing tag shouldn't clear the accumulated text.
+ clearbuffer = false;
+ }
+ else if (strcmp("CaptureDevice", tag) == 0)
+ {
+ gVoiceClient->addCaptureDevice(textBuffer);
+ }
+ else if (strcmp("RenderDevice", tag) == 0)
+ {
+ gVoiceClient->addRenderDevice(textBuffer);
+ }
+
+ if(clearbuffer)
+ {
+ textBuffer.clear();
+ accumulateText= false;
+ }
+
+ if (responseDepth == 0)
+ {
+ // We finished all of the XML, process the data
+ processResponse(tag);
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::CharData(const char *buffer, int length)
+{
+ /*
+ This method is called for anything that isn't a tag, which can be text you
+ want that lies between tags, and a lot of stuff you don't want like file formatting
+ (tabs, spaces, CR/LF, etc).
+
+ Only copy text if we are in accumulate mode...
+ */
+ if (accumulateText)
+ textBuffer.append(buffer, length);
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::processResponse(std::string tag)
+{
+// llinfos << tag << llendl;
+
+ if (isEvent)
+ {
+ if (eventTypeString == "LoginStateChangeEvent")
+ {
+ gVoiceClient->loginStateChangeEvent(accountHandle, statusCode, statusString, state);
+ }
+ else if (eventTypeString == "SessionNewEvent")
+ {
+ gVoiceClient->sessionNewEvent(accountHandle, eventSessionHandle, state, nameString, uriString);
+ }
+ else if (eventTypeString == "SessionStateChangeEvent")
+ {
+ gVoiceClient->sessionStateChangeEvent(uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString);
+ }
+ else if (eventTypeString == "ParticipantStateChangeEvent")
+ {
+ gVoiceClient->participantStateChangeEvent(uriString, statusCode, statusString, state, nameString, displayNameString, participantType);
+
+ }
+ else if (eventTypeString == "ParticipantPropertiesEvent")
+ {
+ gVoiceClient->participantPropertiesEvent(uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy);
+ }
+ else if (eventTypeString == "AuxAudioPropertiesEvent")
+ {
+ gVoiceClient->auxAudioPropertiesEvent(energy);
+ }
+ }
+ else
+ {
+ if (actionString == "Connector.Create.1")
+ {
+ gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle);
+ }
+ else if (actionString == "Account.Login.1")
+ {
+ gVoiceClient->loginResponse(statusCode, statusString, accountHandle);
+ }
+ else if (actionString == "Session.Create.1")
+ {
+ gVoiceClient->sessionCreateResponse(statusCode, statusString, sessionHandle);
+ }
+ else if (actionString == "Session.Connect.1")
+ {
+ gVoiceClient->sessionConnectResponse(statusCode, statusString);
+ }
+ else if (actionString == "Session.Terminate.1")
+ {
+ gVoiceClient->sessionTerminateResponse(statusCode, statusString);
+ }
+ else if (actionString == "Account.Logout.1")
+ {
+ gVoiceClient->logoutResponse(statusCode, statusString);
+ }
+ else if (actionString == "Connector.InitiateShutdown.1")
+ {
+ gVoiceClient->connectorShutdownResponse(statusCode, statusString);
+ }
+ else if (actionString == "Account.ChannelGetList.1")
+ {
+ gVoiceClient->channelGetListResponse(statusCode, statusString);
+ }
+/*
+ else if (actionString == "Connector.AccountCreate.1")
+ {
+
+ }
+ else if (actionString == "Connector.MuteLocalMic.1")
+ {
+
+ }
+ else if (actionString == "Connector.MuteLocalSpeaker.1")
+ {
+
+ }
+ else if (actionString == "Connector.SetLocalMicVolume.1")
+ {
+
+ }
+ else if (actionString == "Connector.SetLocalSpeakerVolume.1")
+ {
+
+ }
+ else if (actionString == "Session.ListenerSetPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.SpeakerSetPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.Set3DPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.AudioSourceSetPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.GetChannelParticipants.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelCreate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelUpdate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelDelete.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelCreateAndInvite.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelFolderCreate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelFolderUpdate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelFolderDelete.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelAddModerator.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelDeleteModerator.1")
+ {
+
+ }
+*/
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+class LLVoiceClientPrefsListener: public LLSimpleListener
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Note: Ignore the specific event value, look up the ones we want
+
+ gVoiceClient->setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat"));
+ gVoiceClient->setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled"));
+ std::string keyString = gSavedSettings.getString("PushToTalkButton");
+ gVoiceClient->setPTTKey(keyString);
+ gVoiceClient->setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
+ gVoiceClient->setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
+ std::string serverName = gSavedSettings.getString("VivoxDebugServerName");
+ gVoiceClient->setVivoxDebugServerName(serverName);
+
+ std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
+ gVoiceClient->setCaptureDevice(inputDevice);
+ std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
+ gVoiceClient->setRenderDevice(outputDevice);
+
+ return true;
+ }
+};
+static LLVoiceClientPrefsListener voice_prefs_listener;
+
+class LLVoiceClientMuteListObserver : public LLMuteListObserver
+{
+ /* virtual */ void onChange() { gVoiceClient->muteListChanged();}
+};
+static LLVoiceClientMuteListObserver mutelist_listener;
+static bool sMuteListListener_listening = false;
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+class LLVoiceClientCapResponder : public LLHTTPClient::Responder
+{
+public:
+ LLVoiceClientCapResponder(void){};
+
+ virtual void error(U32 status, const std::string& reason); // called with bad status codes
+ virtual void result(const LLSD& content);
+
+private:
+};
+
+void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
+{
+ llwarns << "LLVoiceClientCapResponder::error("
+ << status << ": " << reason << ")"
+ << llendl;
+}
+
+void LLVoiceClientCapResponder::result(const LLSD& content)
+{
+ LLSD::map_const_iterator iter;
+ for(iter = content.beginMap(); iter != content.endMap(); ++iter)
+ {
+ llinfos << "LLVoiceClientCapResponder::result got "
+ << iter->first << llendl;
+ }
+
+ if ( content.has("voice_credentials") )
+ {
+ LLSD voice_credentials = content["voice_credentials"];
+ std::string uri;
+ std::string credentials;
+
+ if ( voice_credentials.has("channel_uri") )
+ {
+ uri = voice_credentials["channel_uri"].asString();
+ }
+ if ( voice_credentials.has("channel_credentials") )
+ {
+ credentials =
+ voice_credentials["channel_credentials"].asString();
+ }
+
+ gVoiceClient->setSpatialChannel(uri, credentials);
+ }
+}
+
+
+
+#if LL_WINDOWS
+static HANDLE sGatewayHandle = 0;
+
+static bool isGatewayRunning()
+{
+ bool result = false;
+ if(sGatewayHandle != 0)
+ {
+ DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
+ if(waitresult != WAIT_OBJECT_0)
+ {
+ result = true;
+ }
+ }
+ return result;
+}
+static void killGateway()
+{
+ if(sGatewayHandle != 0)
+ {
+ TerminateProcess(sGatewayHandle,0);
+ }
+}
+
+#else // Mac and linux
+
+static pid_t sGatewayPID = 0;
+static bool isGatewayRunning()
+{
+ bool result = false;
+ if(sGatewayPID != 0)
+ {
+ // A kill with signal number 0 has no effect, just does error checking. It should return an error if the process no longer exists.
+ if(kill(sGatewayPID, 0) == 0)
+ {
+ result = true;
+ }
+ }
+ return result;
+}
+
+static void killGateway()
+{
+ if(sGatewayPID != 0)
+ {
+ kill(sGatewayPID, SIGTERM);
+ }
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+LLVoiceClient::LLVoiceClient()
+{
+ gVoiceClient = this;
+ mWriteInProgress = false;
+ mAreaVoiceDisabled = false;
+ mPTT = true;
+ mUserPTTState = false;
+ mMuteMic = false;
+ mSessionTerminateRequested = false;
+ mCommandCookie = 0;
+ mNonSpatialChannel = false;
+ mNextSessionSpatial = true;
+ mNextSessionNoReconnect = false;
+ mSessionP2P = false;
+ mCurrentParcelLocalID = 0;
+ mLoginRetryCount = 0;
+ mVivoxErrorStatusCode = 0;
+
+ mNextSessionResetOnClose = false;
+ mSessionResetOnClose = false;
+ mSpeakerVolume = 0;
+ mMicVolume = 0;
+
+ // Initial dirty state
+ mSpatialCoordsDirty = false;
+ mPTTDirty = true;
+ mVolumeDirty = true;
+ mSpeakerVolumeDirty = true;
+ mMicVolumeDirty = true;
+ mCaptureDeviceDirty = false;
+ mRenderDeviceDirty = false;
+
+ // Load initial state from prefs.
+ mVoiceEnabled = gSavedSettings.getBOOL("EnableVoiceChat");
+ mUsePTT = gSavedSettings.getBOOL("EnablePushToTalk");
+ std::string keyString = gSavedSettings.getString("PushToTalkButton");
+ setPTTKey(keyString);
+ mPTTIsToggle = gSavedSettings.getBOOL("PushToTalkToggle");
+ mEarLocation = gSavedSettings.getS32("VoiceEarLocation");
+ setVoiceVolume(gSavedSettings.getF32("AudioLevelVoice"));
+ std::string captureDevice = gSavedSettings.getString("VoiceInputAudioDevice");
+ setCaptureDevice(captureDevice);
+ std::string renderDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
+ setRenderDevice(renderDevice);
+
+ // Set up our listener to get updates on all prefs values we care about.
+ gSavedSettings.getControl("EnableVoiceChat")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("PTTCurrentlyEnabled")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("PushToTalkButton")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("PushToTalkToggle")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VoiceEarLocation")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VivoxDebugServerName")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VoiceInputAudioDevice")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VoiceOutputAudioDevice")->addListener(&voice_prefs_listener);
+
+ mTuningMode = false;
+ mTuningEnergy = 0.0f;
+ mTuningMicVolume = 0;
+ mTuningMicVolumeDirty = true;
+ mTuningSpeakerVolume = 0;
+ mTuningSpeakerVolumeDirty = true;
+ mTuningCaptureRunning = false;
+
+ // gMuteListp isn't set up at this point, so we defer this until later.
+// gMuteListp->addObserver(&mutelist_listener);
+
+ mParticipantMapChanged = false;
+
+ // stash the pump for later use
+ // This now happens when init() is called instead.
+ mPump = NULL;
+
+#if LL_DARWIN || LL_LINUX
+ // MBW -- XXX -- THIS DOES NOT BELONG HERE
+ // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
+ // This should cause us to ignore SIGPIPE and handle the error through proper channels.
+ // This should really be set up elsewhere. Where should it go?
+ signal(SIGPIPE, SIG_IGN);
+
+ // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
+ // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that.
+ signal(SIGCHLD, SIG_IGN);
+#endif
+
+ // set up state machine
+ setState(stateDisabled);
+
+ gIdleCallbacks.addFunction(idle, this);
+}
+
+//---------------------------------------------------
+
+LLVoiceClient::~LLVoiceClient()
+{
+}
+
+//----------------------------------------------
+
+
+
+void LLVoiceClient::init(LLPumpIO *pump)
+{
+ // constructor will set up gVoiceClient
+ LLVoiceClient::getInstance()->mPump = pump;
+}
+
+void LLVoiceClient::terminate()
+{
+ if(gVoiceClient)
+ {
+ gVoiceClient->sessionTerminateSendMessage();
+ gVoiceClient->logout();
+ gVoiceClient->connectorShutdown();
+ gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
+
+ // This will do unpleasant things on windows.
+// killGateway();
+
+ // Don't do this anymore -- LLSingleton will take care of deleting the object.
+// delete gVoiceClient;
+
+ // Hint to other code not to access the voice client anymore.
+ gVoiceClient = NULL;
+ }
+}
+
+
+/////////////////////////////
+// utility functions
+
+bool LLVoiceClient::writeString(const std::string &str)
+{
+ bool result = false;
+ if(mConnected)
+ {
+ apr_status_t err;
+ apr_size_t size = (apr_size_t)str.size();
+ apr_size_t written = size;
+
+// llinfos << "sending: " << str << llendl;
+
+ // MBW -- XXX -- check return code - sockets will fail (broken, etc.)
+ err = apr_socket_send(
+ mSocket->getSocket(),
+ (const char*)str.data(),
+ &written);
+
+ if(err == 0)
+ {
+ // Success.
+ result = true;
+ }
+ // MBW -- XXX -- handle partial writes (written is number of bytes written)
+ // Need to set socket to non-blocking before this will work.
+// else if(APR_STATUS_IS_EAGAIN(err))
+// {
+// //
+// }
+ else
+ {
+ // Assume any socket error means something bad. For now, just close the socket.
+ char buf[MAX_STRING];
+ llwarns << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << llendl;
+ daemonDied();
+ }
+ }
+
+ return result;
+}
+
+
+/////////////////////////////
+// session control messages
+void LLVoiceClient::connectorCreate()
+{
+ std::ostringstream stream;
+ std::string logpath;
+ std::string loglevel = "0";
+
+ // Transition to stateConnectorStarted when the connector handle comes back.
+ setState(stateConnectorStarting);
+
+ std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
+
+ if(savedLogLevel != "-1")
+ {
+ llinfos << "creating connector with logging enabled" << llendl;
+ loglevel = "10";
+ logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
+ }
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
+ << "<ClientName>V2 SDK</ClientName>"
+ << "<AccountManagementServer>" << mAccountServerURI << "</AccountManagementServer>"
+ << "<Logging>"
+ << "<Enabled>false</Enabled>"
+ << "<Folder>" << logpath << "</Folder>"
+ << "<FileNamePrefix>Connector</FileNamePrefix>"
+ << "<FileNameSuffix>.log</FileNameSuffix>"
+ << "<LogLevel>" << loglevel << "</LogLevel>"
+ << "</Logging>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::connectorShutdown()
+{
+ setState(stateConnectorStopping);
+
+ if(!mConnectorHandle.empty())
+ {
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "</Request>"
+ << "\n\n\n";
+
+ mConnectorHandle.clear();
+
+ writeString(stream.str());
+ }
+}
+
+void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID)
+{
+ mAccountFirstName = firstName;
+ mAccountLastName = lastName;
+
+ mAccountDisplayName = firstName;
+ mAccountDisplayName += " ";
+ mAccountDisplayName += lastName;
+
+ llinfos << "name \"" << mAccountDisplayName << "\" , ID " << agentID << llendl;
+
+ std::string userserver = gUserServerName;
+ LLString::toLower(userserver);
+ if((gUserServerChoice == USERSERVER_AGNI) ||
+ ((gUserServerChoice == USERSERVER_OTHER) && (userserver.find("agni") != std::string::npos)))
+ {
+ sConnectingToAgni = true;
+ }
+
+ // MBW -- XXX -- Enable this when the bhd.vivox.com server gets a real ssl cert.
+ if(sConnectingToAgni)
+ {
+ // Use the release account server
+ mAccountServerName = "bhr.vivox.com";
+ mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
+ }
+ else
+ {
+ // Use the development account server
+ mAccountServerName = gSavedSettings.getString("VivoxDebugServerName");
+ mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
+ }
+
+ mAccountName = nameFromID(agentID);
+}
+
+void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
+{
+ if ( gAgent.getRegion() && mVoiceEnabled )
+ {
+ std::string url =
+ gAgent.getRegion()->getCapability(
+ "ProvisionVoiceAccountRequest");
+
+ if ( url == "" ) return;
+
+ LLHTTPClient::post(
+ url,
+ LLSD(),
+ new LLViewerVoiceAccountProvisionResponder(retries));
+ }
+}
+
+void LLVoiceClient::login(
+ const std::string& accountName,
+ const std::string &password)
+{
+ if((getState() >= stateLoggingIn) && (getState() < stateLoggedOut))
+ {
+ // Already logged in. This is an internal error.
+ llerrs << "called from wrong state." << llendl;
+ }
+ else if ( accountName != mAccountName )
+ {
+ //TODO: error?
+ llinfos << "Wrong account name! " << accountName
+ << " instead of " << mAccountName << llendl;
+ }
+ else
+ {
+ mAccountPassword = password;
+ }
+}
+
+void LLVoiceClient::idle(void* user_data)
+{
+ LLVoiceClient* self = (LLVoiceClient*)user_data;
+ self->stateMachine();
+}
+
+const char *LLVoiceClient::state2string(LLVoiceClient::state inState)
+{
+ const char *result = "UNKNOWN";
+
+ // Prevent copy-paste errors when updating this list...
+#define CASE(x) case x: result = #x; break
+
+ switch(inState)
+ {
+ CASE(stateDisabled);
+ CASE(stateStart);
+ CASE(stateDaemonLaunched);
+ CASE(stateConnecting);
+ CASE(stateIdle);
+ CASE(stateConnectorStart);
+ CASE(stateConnectorStarting);
+ CASE(stateConnectorStarted);
+ CASE(stateMicTuningNoLogin);
+ CASE(stateLoginRetry);
+ CASE(stateLoginRetryWait);
+ CASE(stateNeedsLogin);
+ CASE(stateLoggingIn);
+ CASE(stateLoggedIn);
+ CASE(stateNoChannel);
+ CASE(stateMicTuningLoggedIn);
+ CASE(stateSessionCreate);
+ CASE(stateSessionConnect);
+ CASE(stateJoiningSession);
+ CASE(stateSessionJoined);
+ CASE(stateRunning);
+ CASE(stateLeavingSession);
+ CASE(stateSessionTerminated);
+ CASE(stateLoggingOut);
+ CASE(stateLoggedOut);
+ CASE(stateConnectorStopping);
+ CASE(stateConnectorStopped);
+ CASE(stateConnectorFailed);
+ CASE(stateConnectorFailedWaiting);
+ CASE(stateLoginFailed);
+ CASE(stateLoginFailedWaiting);
+ CASE(stateJoinSessionFailed);
+ CASE(stateJoinSessionFailedWaiting);
+ CASE(stateJail);
+ }
+
+#undef CASE
+
+ return result;
+}
+
+const char *LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
+{
+ const char *result = "UNKNOWN";
+
+ // Prevent copy-paste errors when updating this list...
+#define CASE(x) case x: result = #x; break
+
+ switch(inStatus)
+ {
+ CASE(STATUS_JOINING);
+ CASE(STATUS_JOINED);
+ CASE(STATUS_LEFT_CHANNEL);
+ CASE(ERROR_CHANNEL_FULL);
+ CASE(ERROR_CHANNEL_LOCKED);
+ CASE(ERROR_UNKNOWN);
+ default:
+ break;
+ }
+
+#undef CASE
+
+ return result;
+}
+
+void LLVoiceClient::setState(state inState)
+{
+ llinfos << "entering state " << state2string(inState) << llendl;
+
+ mState = inState;
+}
+
+void LLVoiceClient::stateMachine()
+{
+ if(gDisconnected)
+ {
+ // The viewer has been disconnected from the sim. Disable voice.
+ setVoiceEnabled(false);
+ }
+
+ if(!mVoiceEnabled)
+ {
+ if(getState() != stateDisabled)
+ {
+ // User turned off voice support. Send the cleanup messages, close the socket, and reset.
+ if(!mConnected)
+ {
+ // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
+ llinfos << "Disabling voice before connection to daemon, terminating." << llendl;
+ killGateway();
+ }
+
+ sessionTerminateSendMessage();
+ logout();
+ connectorShutdown();
+ closeSocket();
+ removeAllParticipants();
+
+ setState(stateDisabled);
+ }
+ }
+
+ // Check for parcel boundary crossing
+ {
+ LLViewerRegion *region = gAgent.getRegion();
+ LLParcel *parcel = NULL;
+
+ if(gParcelMgr)
+ {
+ parcel = gParcelMgr->getAgentParcel();
+ }
+
+ if(region && parcel)
+ {
+ S32 parcelLocalID = parcel->getLocalID();
+ std::string regionName = region->getName();
+ std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
+
+// llinfos << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << llendl;
+
+ // The region name starts out empty and gets filled in later.
+ // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
+ // If either is empty, wait for the next time around.
+ if(!regionName.empty() && !capURI.empty())
+ {
+ if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName))
+ {
+ // We have changed parcels. Initiate a parcel channel lookup.
+ mCurrentParcelLocalID = parcelLocalID;
+ mCurrentRegionName = regionName;
+
+ parcelChanged();
+ }
+ }
+ }
+ }
+
+ switch(getState())
+ {
+ case stateDisabled:
+ if(mVoiceEnabled && (!mAccountName.empty() || mTuningMode))
+ {
+ setState(stateStart);
+ }
+ break;
+
+ case stateStart:
+ if(gDisableVoice)
+ {
+ // Voice is locked out, we must not launch the vivox daemon.
+ setState(stateJail);
+ }
+ else if(!isGatewayRunning())
+ {
+ if(true)
+ {
+ // Launch the voice daemon
+ std::string exe_path = gDirUtilp->getAppRODataDir();
+ exe_path += gDirUtilp->getDirDelimiter();
+#if LL_WINDOWS
+ exe_path += "SLVoice.exe";
+#else
+ // This will be the same for mac and linux
+ exe_path += "SLVoice";
+#endif
+ // See if the vivox executable exists
+ llstat s;
+ if(!LLFile::stat(exe_path.c_str(), &s))
+ {
+ // vivox executable exists. Build the command line and launch the daemon.
+ std::string args = " -p tcp -h -c";
+ std::string cmd;
+ std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
+
+ if(loglevel.empty())
+ {
+ loglevel = "-1"; // turn logging off completely
+ }
+
+ args += " -ll ";
+ args += loglevel;
+
+// llinfos << "Args for SLVoice: " << args << llendl;
+
+#if LL_WINDOWS
+ PROCESS_INFORMATION pinfo;
+ STARTUPINFOA sinfo;
+ memset(&sinfo, 0, sizeof(sinfo));
+ std::string exe_dir = gDirUtilp->getAppRODataDir();
+ cmd = "SLVoice.exe";
+ cmd += args;
+
+ // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
+ char *args2 = new char[args.size() + 1];
+ strcpy(args2, args.c_str());
+
+ if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo))
+ {
+// DWORD dwErr = GetLastError();
+ }
+ else
+ {
+ // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
+ // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
+ sGatewayHandle = pinfo.hProcess;
+ CloseHandle(pinfo.hThread); // stops leaks - nothing else
+ }
+
+ delete args2;
+#else // LL_WINDOWS
+ // This should be the same for mac and linux
+ {
+ std::vector<std::string> arglist;
+ arglist.push_back(exe_path.c_str());
+
+ // Split the argument string into separate strings for each argument
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(args, sep);
+ tokenizer::iterator token_iter;
+
+ for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ arglist.push_back(*token_iter);
+ }
+
+ // create an argv vector for the child process
+ char **fakeargv = new char*[arglist.size() + 1];
+ int i;
+ for(i=0; i < arglist.size(); i++)
+ fakeargv[i] = const_cast<char*>(arglist[i].c_str());
+
+ fakeargv[i] = NULL;
+
+ pid_t id = vfork();
+ if(id == 0)
+ {
+ // child
+ execv(exe_path.c_str(), fakeargv);
+
+ // If we reach this point, the exec failed.
+ // Use _exit() instead of exit() per the vfork man page.
+ _exit(0);
+ }
+
+ // parent
+ delete[] fakeargv;
+ sGatewayPID = id;
+ }
+#endif // LL_WINDOWS
+ mDaemonHost = LLHost("127.0.0.1", 44124);
+ }
+ else
+ {
+ llinfos << exe_path << "not found." << llendl
+ }
+ }
+ else
+ {
+ // We can connect to a client gateway running on another host. This is useful for testing.
+ // To do this, launch the gateway on a nearby host like this:
+ // vivox-gw.exe -p tcp -i 0.0.0.0:44124
+ // and put that host's IP address here.
+ mDaemonHost = LLHost("127.0.0.1", 44124);
+ }
+
+ mUpdateTimer.start();
+ mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+
+ setState(stateDaemonLaunched);
+
+ // Dirty the states we'll need to sync with the daemon when it comes up.
+ mPTTDirty = true;
+ mSpeakerVolumeDirty = true;
+ // These only need to be set if they're not default (i.e. empty string).
+ mCaptureDeviceDirty = !mCaptureDevice.empty();
+ mRenderDeviceDirty = !mRenderDevice.empty();
+ }
+ break;
+
+ case stateDaemonLaunched:
+// llinfos << "Connecting to vivox daemon" << llendl;
+ if(mUpdateTimer.hasExpired())
+ {
+ mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+
+ if(!mSocket)
+ {
+ mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+ }
+
+ mConnected = mSocket->blockingConnect(mDaemonHost);
+ if(mConnected)
+ {
+ setState(stateConnecting);
+ }
+ else
+ {
+ // If the connect failed, the socket may have been put into a bad state. Delete it.
+ closeSocket();
+ }
+ }
+ break;
+
+ case stateConnecting:
+ // Can't do this until we have the pump available.
+ if(mPump)
+ {
+ // MBW -- Note to self: pumps and pipes examples in
+ // indra/test/io.cpp
+ // indra/test/llpipeutil.{cpp|h}
+
+ // Attach the pumps and pipes
+
+ LLPumpIO::chain_t readChain;
+
+ readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
+ readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
+
+ mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
+
+ setState(stateIdle);
+ }
+
+ break;
+
+ case stateIdle:
+ // Initial devices query
+ getCaptureDevicesSendMessage();
+ getRenderDevicesSendMessage();
+
+ mLoginRetryCount = 0;
+
+ setState(stateConnectorStart);
+
+ break;
+
+ case stateConnectorStart:
+ if(!mVoiceEnabled)
+ {
+ // We were never logged in. This will shut down the connector.
+ setState(stateLoggedOut);
+ }
+ else if(!mAccountServerURI.empty())
+ {
+ connectorCreate();
+ }
+ else if(mTuningMode)
+ {
+ setState(stateMicTuningNoLogin);
+ }
+ break;
+
+ case stateConnectorStarting: // waiting for connector handle
+ // connectorCreateResponse() will transition from here to stateConnectorStarted.
+ break;
+
+ case stateConnectorStarted: // connector handle received
+ if(!mVoiceEnabled)
+ {
+ // We were never logged in. This will shut down the connector.
+ setState(stateLoggedOut);
+ }
+ else if(!mAccountName.empty())
+ {
+ LLViewerRegion *region = gAgent.getRegion();
+
+ if(region)
+ {
+ if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
+ {
+ if ( mAccountPassword.empty() )
+ {
+ requestVoiceAccountProvision();
+ }
+ setState(stateNeedsLogin);
+ }
+ }
+ }
+ break;
+
+ case stateMicTuningNoLogin:
+ case stateMicTuningLoggedIn:
+ {
+ // Both of these behave essentially the same. The only difference is where the exit transition goes to.
+ if(mTuningMode && mVoiceEnabled && !mSessionTerminateRequested)
+ {
+ if(!mTuningCaptureRunning)
+ {
+ // duration parameter is currently unused, per Mike S.
+ tuningCaptureStartSendMessage(10000);
+ }
+
+ if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty || mCaptureDeviceDirty || mRenderDeviceDirty)
+ {
+ std::ostringstream stream;
+
+ if(mTuningMicVolumeDirty)
+ {
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
+ << "<Level>" << mTuningMicVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mTuningSpeakerVolumeDirty)
+ {
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
+ << "<Level>" << mTuningSpeakerVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mCaptureDeviceDirty)
+ {
+ buildSetCaptureDevice(stream);
+ }
+
+ if(mRenderDeviceDirty)
+ {
+ buildSetRenderDevice(stream);
+ }
+
+ mTuningMicVolumeDirty = false;
+ mTuningSpeakerVolumeDirty = false;
+ mCaptureDeviceDirty = false;
+ mRenderDeviceDirty = false;
+
+ if(!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+ }
+ }
+ else
+ {
+ // transition out of mic tuning
+ if(mTuningCaptureRunning)
+ {
+ tuningCaptureStopSendMessage();
+ }
+
+ if(getState() == stateMicTuningNoLogin)
+ {
+ setState(stateConnectorStart);
+ }
+ else
+ {
+ setState(stateNoChannel);
+ }
+ }
+ }
+ break;
+
+ case stateLoginRetry:
+ if(mLoginRetryCount == 0)
+ {
+ // First retry -- display a message to the user
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
+ }
+
+ mLoginRetryCount++;
+
+ if(mLoginRetryCount > MAX_LOGIN_RETRIES)
+ {
+ llinfos << "too many login retries, giving up." << llendl;
+ setState(stateLoginFailed);
+ }
+ else
+ {
+ llinfos << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << llendl;
+ mUpdateTimer.start();
+ mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
+ setState(stateLoginRetryWait);
+ }
+ break;
+
+ case stateLoginRetryWait:
+ if(mUpdateTimer.hasExpired())
+ {
+ setState(stateNeedsLogin);
+ }
+ break;
+
+ case stateNeedsLogin:
+ if(!mAccountPassword.empty())
+ {
+ setState(stateLoggingIn);
+ loginSendMessage();
+ }
+ break;
+
+ case stateLoggingIn: // waiting for account handle
+ // loginResponse() will transition from here to stateLoggedIn.
+ break;
+
+ case stateLoggedIn: // account handle received
+ // Initial kick-off of channel lookup logic
+ parcelChanged();
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+
+ // Set up the mute list observer if it hasn't been set up already.
+ if((!sMuteListListener_listening) && (gMuteListp))
+ {
+ gMuteListp->addObserver(&mutelist_listener);
+ sMuteListListener_listening = true;
+ }
+
+ setState(stateNoChannel);
+ break;
+
+ case stateNoChannel:
+ if(mSessionTerminateRequested || !mVoiceEnabled)
+ {
+ // MBW -- XXX -- Is this the right way out of this state?
+ setState(stateSessionTerminated);
+ }
+ else if(mTuningMode)
+ {
+ setState(stateMicTuningLoggedIn);
+ }
+ else if(!mNextSessionHandle.empty())
+ {
+ setState(stateSessionConnect);
+ }
+ else if(!mNextSessionURI.empty())
+ {
+ setState(stateSessionCreate);
+ }
+ break;
+
+ case stateSessionCreate:
+ sessionCreateSendMessage();
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+ setState(stateJoiningSession);
+ break;
+
+ case stateSessionConnect:
+ sessionConnectSendMessage();
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+ setState(stateJoiningSession);
+ break;
+
+ case stateJoiningSession: // waiting for session handle
+ // sessionCreateResponse() will transition from here to stateSessionJoined.
+ if(!mVoiceEnabled)
+ {
+ // User bailed out during connect -- jump straight to teardown.
+ setState(stateSessionTerminated);
+ }
+ else if(mSessionTerminateRequested)
+ {
+ if(!mSessionHandle.empty())
+ {
+ // Only allow direct exits from this state in p2p calls (for cancelling an invite).
+ // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+ if(mSessionP2P)
+ {
+ sessionTerminateSendMessage();
+ setState(stateSessionTerminated);
+ }
+ }
+ }
+ break;
+
+ case stateSessionJoined: // session handle received
+ // MBW -- XXX -- It appears that I need to wait for BOTH the Session.Create response and the SessionStateChangeEvent with state 4
+ // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
+ // For now, the Session.Create response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
+ // This is a cheap way to make sure both have happened before proceeding.
+ if(!mSessionHandle.empty())
+ {
+ // Events that need to happen when a session is joined could go here.
+ // Maybe send initial spatial data?
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+
+ // Dirty state that may need to be sync'ed with the daemon.
+ mPTTDirty = true;
+ mSpeakerVolumeDirty = true;
+ mSpatialCoordsDirty = true;
+
+ setState(stateRunning);
+
+ // Start the throttle timer
+ mUpdateTimer.start();
+ mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+ }
+ else if(!mVoiceEnabled)
+ {
+ // User bailed out during connect -- jump straight to teardown.
+ setState(stateSessionTerminated);
+ }
+ else if(mSessionTerminateRequested)
+ {
+ // Only allow direct exits from this state in p2p calls (for cancelling an invite).
+ // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+ if(mSessionP2P)
+ {
+ sessionTerminateSendMessage();
+ setState(stateSessionTerminated);
+ }
+ }
+ break;
+
+ case stateRunning: // steady state
+ // sessionTerminateSendMessage() will transition from here to stateLeavingSession
+
+ // Disabling voice or disconnect requested.
+ if(!mVoiceEnabled || mSessionTerminateRequested)
+ {
+ sessionTerminateSendMessage();
+ }
+ else
+ {
+
+ // Figure out whether the PTT state needs to change
+ {
+ bool newPTT;
+ if(mUsePTT)
+ {
+ // If configured to use PTT, track the user state.
+ newPTT = mUserPTTState;
+ }
+ else
+ {
+ // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak).
+ newPTT = true;
+ }
+
+ if(mMuteMic)
+ {
+ // This always overrides any other PTT setting.
+ newPTT = false;
+ }
+
+ // Dirty if state changed.
+ if(newPTT != mPTT)
+ {
+ mPTT = newPTT;
+ mPTTDirty = true;
+ }
+ }
+
+ if(mNonSpatialChannel)
+ {
+ // When in a non-spatial channel, never send positional updates.
+ mSpatialCoordsDirty = false;
+ }
+ else
+ {
+ // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
+ enforceTether();
+ }
+
+ // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
+ // or every 10hz, whichever is sooner.
+ if(mVolumeDirty || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
+ {
+ mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+ sendPositionalUpdate();
+ }
+ }
+ break;
+
+ case stateLeavingSession: // waiting for terminate session response
+ // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
+ break;
+
+ case stateSessionTerminated:
+ // Always reset the terminate request flag when we get here.
+ mSessionTerminateRequested = false;
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+
+ if(mVoiceEnabled)
+ {
+ // SPECIAL CASE: if going back to spatial but in a parcel with an empty URI, transfer the non-spatial flag now.
+ // This fixes the case where you come out of a group chat in a parcel with voice disabled, and get stuck unable to rejoin spatial chat thereafter.
+ if(mNextSessionSpatial && mNextSessionURI.empty())
+ {
+ mNonSpatialChannel = !mNextSessionSpatial;
+ }
+
+ // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
+ setState(stateNoChannel);
+ }
+ else
+ {
+ // Shutting down voice, continue with disconnecting.
+ logout();
+ }
+
+ break;
+
+ case stateLoggingOut: // waiting for logout response
+ // The handler for the Account.Logout response will transition from here to stateLoggedOut.
+ break;
+ case stateLoggedOut: // logout response received
+ // shut down the connector
+ connectorShutdown();
+ break;
+
+ case stateConnectorStopping: // waiting for connector stop
+ // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
+ break;
+
+ case stateConnectorStopped: // connector stop received
+ // Clean up and reset everything.
+ closeSocket();
+ removeAllParticipants();
+ setState(stateDisabled);
+ break;
+
+ case stateConnectorFailed:
+ setState(stateConnectorFailedWaiting);
+ break;
+ case stateConnectorFailedWaiting:
+ break;
+
+ case stateLoginFailed:
+ setState(stateLoginFailedWaiting);
+ break;
+ case stateLoginFailedWaiting:
+ // No way to recover from these. Yet.
+ break;
+
+ case stateJoinSessionFailed:
+ // Transition to error state. Send out any notifications here.
+ llwarns << "stateJoinSessionFailed: (" << mVivoxErrorStatusCode << "): " << mVivoxErrorStatusString << llendl;
+ notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
+ setState(stateJoinSessionFailedWaiting);
+ break;
+
+ case stateJoinSessionFailedWaiting:
+ // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
+ // Region crossings may leave this state and try the join again.
+ if(mSessionTerminateRequested)
+ {
+ setState(stateSessionTerminated);
+ }
+ break;
+
+ case stateJail:
+ // We have given up. Do nothing.
+ break;
+ }
+
+ if(mParticipantMapChanged)
+ {
+ mParticipantMapChanged = false;
+ notifyObservers();
+ }
+
+}
+
+void LLVoiceClient::closeSocket(void)
+{
+ mSocket.reset();
+ mConnected = false;
+}
+
+void LLVoiceClient::loginSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<AccountName>" << mAccountName << "</AccountName>"
+ << "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
+ << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::logout()
+{
+ mAccountPassword = "";
+ setState(stateLoggingOut);
+ logoutSendMessage();
+}
+
+void LLVoiceClient::logoutSendMessage()
+{
+ if(!mAccountHandle.empty())
+ {
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">"
+ << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+ << "</Request>"
+ << "\n\n\n";
+
+ mAccountHandle.clear();
+
+ writeString(stream.str());
+ }
+}
+
+void LLVoiceClient::channelGetListSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ChannelGetList.1\">"
+ << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::sessionCreateSendMessage()
+{
+ llinfos << "requesting join: " << mNextSessionURI << llendl;
+
+ mSessionURI = mNextSessionURI;
+ mNonSpatialChannel = !mNextSessionSpatial;
+ mSessionResetOnClose = mNextSessionResetOnClose;
+ mNextSessionResetOnClose = false;
+ if(mNextSessionNoReconnect)
+ {
+ // Clear the stashed URI so it can't reconnect
+ mNextSessionURI.clear();
+ }
+ // Only p2p sessions are created with "no reconnect".
+ mSessionP2P = mNextSessionNoReconnect;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Create.1\">"
+ << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+ << "<URI>" << mSessionURI << "</URI>";
+
+ static const std::string allowed_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-._~";
+
+ if(!mNextSessionHash.empty())
+ {
+ stream
+ << "<Password>" << LLURI::escape(mNextSessionHash, allowed_chars) << "</Password>"
+ << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
+ }
+
+ stream
+ << "<Name>" << mChannelName << "</Name>"
+ << "</Request>\n\n\n";
+ writeString(stream.str());
+}
+
+void LLVoiceClient::sessionConnectSendMessage()
+{
+ llinfos << "connecting to session handle: " << mNextSessionHandle << llendl;
+
+ mSessionHandle = mNextSessionHandle;
+ mSessionURI = mNextP2PSessionURI;
+ mNextSessionHandle.clear(); // never want to re-use these.
+ mNextP2PSessionURI.clear();
+ mNonSpatialChannel = !mNextSessionSpatial;
+ mSessionResetOnClose = mNextSessionResetOnClose;
+ mNextSessionResetOnClose = false;
+ // Joining by session ID is only used to answer p2p invitations, so we know this is a p2p session.
+ mSessionP2P = true;
+
+ std::ostringstream stream;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Connect.1\">"
+ << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
+ << "<AudioMedia>default</AudioMedia>"
+ << "</Request>\n\n\n";
+ writeString(stream.str());
+}
+
+void LLVoiceClient::sessionTerminate()
+{
+ mSessionTerminateRequested = true;
+}
+
+void LLVoiceClient::sessionTerminateSendMessage()
+{
+ llinfos << "leaving session: " << mSessionURI << llendl;
+
+ switch(getState())
+ {
+ case stateNoChannel:
+ // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
+ // Skip the join failed transition state so we don't send out error notifications.
+ setState(stateJoinSessionFailedWaiting);
+ break;
+ case stateJoiningSession:
+ case stateSessionJoined:
+ case stateRunning:
+ if(!mSessionHandle.empty())
+ {
+ sessionTerminateByHandle(mSessionHandle);
+ setState(stateLeavingSession);
+ }
+ else
+ {
+ llwarns << "called with no session handle" << llendl;
+ setState(stateSessionTerminated);
+ }
+ break;
+ case stateJoinSessionFailed:
+ case stateJoinSessionFailedWaiting:
+ setState(stateSessionTerminated);
+ break;
+
+ default:
+ llwarns << "called from unknown state" << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::sessionTerminateByHandle(std::string &sessionHandle)
+{
+ llinfos << "Sending Session.Terminate with handle " << sessionHandle << llendl;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
+ << "<SessionHandle>" << sessionHandle << "</SessionHandle>"
+ << "</Request>"
+ << "\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::getCaptureDevicesSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::getRenderDevicesSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::clearCaptureDevices()
+{
+ // MBW -- XXX -- do something here
+ llinfos << "called" << llendl;
+ mCaptureDevices.clear();
+}
+
+void LLVoiceClient::addCaptureDevice(const std::string& name)
+{
+ // MBW -- XXX -- do something here
+ llinfos << name << llendl;
+
+ mCaptureDevices.push_back(name);
+}
+
+LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices()
+{
+ return &mCaptureDevices;
+}
+
+void LLVoiceClient::setCaptureDevice(const std::string& name)
+{
+ if(name == "Default")
+ {
+ if(!mCaptureDevice.empty())
+ {
+ mCaptureDevice.clear();
+ mCaptureDeviceDirty = true;
+ }
+ }
+ else
+ {
+ if(mCaptureDevice != name)
+ {
+ mCaptureDevice = name;
+ mCaptureDeviceDirty = true;
+ }
+ }
+}
+
+void LLVoiceClient::clearRenderDevices()
+{
+ // MBW -- XXX -- do something here
+ llinfos << "called" << llendl;
+ mRenderDevices.clear();
+}
+
+void LLVoiceClient::addRenderDevice(const std::string& name)
+{
+ // MBW -- XXX -- do something here
+ llinfos << name << llendl;
+ mRenderDevices.push_back(name);
+}
+
+LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices()
+{
+ return &mRenderDevices;
+}
+
+void LLVoiceClient::setRenderDevice(const std::string& name)
+{
+ if(name == "Default")
+ {
+ if(!mRenderDevice.empty())
+ {
+ mRenderDevice.clear();
+ mRenderDeviceDirty = true;
+ }
+ }
+ else
+ {
+ if(mRenderDevice != name)
+ {
+ mRenderDevice = name;
+ mRenderDeviceDirty = true;
+ }
+ }
+
+}
+
+void LLVoiceClient::tuningStart()
+{
+ mTuningMode = true;
+ if(getState() >= stateNoChannel)
+ {
+ sessionTerminate();
+ }
+}
+
+void LLVoiceClient::tuningStop()
+{
+ mTuningMode = false;
+}
+
+bool LLVoiceClient::inTuningMode()
+{
+ bool result = false;
+ switch(getState())
+ {
+ case stateMicTuningNoLogin:
+ case stateMicTuningLoggedIn:
+ result = true;
+ default:
+ break;
+ }
+ return result;
+}
+
+void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
+{
+ if(!inTuningMode())
+ return;
+
+ mTuningAudioFile = name;
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">"
+ << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
+ << "<Loop>" << (loop?"1":"0") << "</Loop>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::tuningRenderStopSendMessage()
+{
+ if(!inTuningMode())
+ return;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">"
+ << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::tuningCaptureStartSendMessage(int duration)
+{
+ if(!inTuningMode())
+ return;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
+ << "<Duration>" << duration << "</Duration>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+
+ mTuningCaptureRunning = true;
+}
+
+void LLVoiceClient::tuningCaptureStopSendMessage()
+{
+ if(!inTuningMode())
+ return;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+
+ mTuningCaptureRunning = false;
+}
+
+void LLVoiceClient::tuningSetMicVolume(float volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mTuningMicVolume)
+ {
+ mTuningMicVolume = scaledVolume;
+ mTuningMicVolumeDirty = true;
+ }
+}
+
+void LLVoiceClient::tuningSetSpeakerVolume(float volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mTuningSpeakerVolume)
+ {
+ mTuningSpeakerVolume = ((int)(volume * 100.0f)) - 100;
+ mTuningSpeakerVolumeDirty = true;
+ }
+}
+
+float LLVoiceClient::tuningGetEnergy(void)
+{
+ return mTuningEnergy;
+}
+
+bool LLVoiceClient::deviceSettingsAvailable()
+{
+ bool result = true;
+
+ if(!mConnected)
+ result = false;
+
+ if(mRenderDevices.empty())
+ result = false;
+
+ return result;
+}
+
+void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
+{
+ if(clearCurrentList)
+ {
+ clearCaptureDevices();
+ clearRenderDevices();
+ }
+ getCaptureDevicesSendMessage();
+ getRenderDevicesSendMessage();
+}
+
+void LLVoiceClient::daemonDied()
+{
+ // The daemon died, so the connection is gone. Reset everything and start over.
+ llwarns << "Connection to vivox daemon lost. Resetting state."<< llendl;
+
+ closeSocket();
+ removeAllParticipants();
+
+ // Try to relaunch the daemon
+ setState(stateDisabled);
+}
+
+void LLVoiceClient::giveUp()
+{
+ // All has failed. Clean up and stop trying.
+ closeSocket();
+ removeAllParticipants();
+
+ setState(stateJail);
+}
+
+void LLVoiceClient::sendPositionalUpdate(void)
+{
+ std::ostringstream stream;
+
+ if(mSpatialCoordsDirty)
+ {
+ LLVector3 l, u, a;
+
+ // Always send both speaker and listener positions together.
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"
+ << "<SessionHandle>" << mSessionHandle << "</SessionHandle>";
+
+ stream << "<SpeakerPosition>";
+
+ l = mAvatarRot.getLeftRow();
+ u = mAvatarRot.getUpRow();
+ a = mAvatarRot.getFwdRow();
+
+// llinfos << "Sending speaker position " << mSpeakerPosition << llendl;
+
+ stream
+ << "<Position>"
+ << "<X>" << mAvatarPosition[VX] << "</X>"
+ << "<Y>" << mAvatarPosition[VZ] << "</Y>"
+ << "<Z>" << mAvatarPosition[VY] << "</Z>"
+ << "</Position>"
+ << "<Velocity>"
+ << "<X>" << mAvatarVelocity[VX] << "</X>"
+ << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
+ << "<Z>" << mAvatarVelocity[VY] << "</Z>"
+ << "</Velocity>"
+ << "<AtOrientation>"
+ << "<X>" << l.mV[VX] << "</X>"
+ << "<Y>" << u.mV[VX] << "</Y>"
+ << "<Z>" << a.mV[VX] << "</Z>"
+ << "</AtOrientation>"
+ << "<UpOrientation>"
+ << "<X>" << l.mV[VZ] << "</X>"
+ << "<Y>" << u.mV[VY] << "</Y>"
+ << "<Z>" << a.mV[VZ] << "</Z>"
+ << "</UpOrientation>"
+ << "<LeftOrientation>"
+ << "<X>" << l.mV [VY] << "</X>"
+ << "<Y>" << u.mV [VZ] << "</Y>"
+ << "<Z>" << a.mV [VY] << "</Z>"
+ << "</LeftOrientation>";
+
+ stream << "</SpeakerPosition>";
+
+ stream << "<ListenerPosition>";
+
+ LLVector3d earPosition;
+ LLVector3 earVelocity;
+ LLMatrix3 earRot;
+
+ switch(mEarLocation)
+ {
+ case earLocCamera:
+ default:
+ earPosition = mCameraPosition;
+ earVelocity = mCameraVelocity;
+ earRot = mCameraRot;
+ break;
+
+ case earLocAvatar:
+ earPosition = mAvatarPosition;
+ earVelocity = mAvatarVelocity;
+ earRot = mAvatarRot;
+ break;
+
+ case earLocMixed:
+ earPosition = mAvatarPosition;
+ earVelocity = mAvatarVelocity;
+ earRot = mCameraRot;
+ break;
+ }
+
+ l = earRot.getLeftRow();
+ u = earRot.getUpRow();
+ a = earRot.getFwdRow();
+
+// llinfos << "Sending listener position " << mListenerPosition << llendl;
+
+ stream
+ << "<Position>"
+ << "<X>" << earPosition[VX] << "</X>"
+ << "<Y>" << earPosition[VZ] << "</Y>"
+ << "<Z>" << earPosition[VY] << "</Z>"
+ << "</Position>"
+ << "<Velocity>"
+ << "<X>" << earVelocity[VX] << "</X>"
+ << "<Y>" << earVelocity[VZ] << "</Y>"
+ << "<Z>" << earVelocity[VY] << "</Z>"
+ << "</Velocity>"
+ << "<AtOrientation>"
+ << "<X>" << l.mV[VX] << "</X>"
+ << "<Y>" << u.mV[VX] << "</Y>"
+ << "<Z>" << a.mV[VX] << "</Z>"
+ << "</AtOrientation>"
+ << "<UpOrientation>"
+ << "<X>" << l.mV[VZ] << "</X>"
+ << "<Y>" << u.mV[VY] << "</Y>"
+ << "<Z>" << a.mV[VZ] << "</Z>"
+ << "</UpOrientation>"
+ << "<LeftOrientation>"
+ << "<X>" << l.mV [VY] << "</X>"
+ << "<Y>" << u.mV [VZ] << "</Y>"
+ << "<Z>" << a.mV [VY] << "</Z>"
+ << "</LeftOrientation>";
+
+ stream << "</ListenerPosition>";
+
+ stream << "</Request>\n\n\n";
+ }
+
+ if(mPTTDirty)
+ {
+ // Send a local mute command.
+ // NOTE that the state of "PTT" is the inverse of "local mute".
+ // (i.e. when PTT is true, we send a mute command with "false", and vice versa)
+
+// llinfos << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << (mPTT?"false":"true") << "</Value>"
+ << "</Request>\n\n\n";
+
+ }
+
+ if(mVolumeDirty)
+ {
+ participantMap::iterator iter = mParticipantMap.begin();
+
+ for(; iter != mParticipantMap.end(); iter++)
+ {
+ participantState *p = iter->second;
+
+ if(p->mVolumeDirty)
+ {
+ int volume = p->mOnMuteList?0:p->mUserVolume;
+
+ llinfos << "Setting volume for avatar " << p->mAvatarID << " to " << volume << llendl;
+
+ // Send a mute/unumte command for the user (actually "volume for me").
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
+ << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
+ << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
+ << "<Volume>" << volume << "</Volume>"
+ << "</Request>\n\n\n";
+
+ p->mVolumeDirty = false;
+ }
+ }
+ }
+
+ if(mSpeakerMuteDirty)
+ {
+ const char *muteval = ((mSpeakerVolume == -100)?"true":"false");
+ llinfos << "Setting speaker mute to " << muteval << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << muteval << "</Value>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mSpeakerVolumeDirty)
+ {
+ llinfos << "Setting speaker volume to " << mSpeakerVolume << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << mSpeakerVolume << "</Value>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mMicVolumeDirty)
+ {
+ llinfos << "Setting mic volume to " << mMicVolume << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << mMicVolume << "</Value>"
+ << "</Request>\n\n\n";
+ }
+
+
+ // MBW -- XXX -- Maybe check to make sure the capture/render devices are in the current list here?
+ if(mCaptureDeviceDirty)
+ {
+ buildSetCaptureDevice(stream);
+ }
+
+ if(mRenderDeviceDirty)
+ {
+ buildSetRenderDevice(stream);
+ }
+
+ mSpatialCoordsDirty = false;
+ mPTTDirty = false;
+ mVolumeDirty = false;
+ mSpeakerVolumeDirty = false;
+ mMicVolumeDirty = false;
+ mSpeakerMuteDirty = false;
+ mCaptureDeviceDirty = false;
+ mRenderDeviceDirty = false;
+
+ if(!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+}
+
+void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
+{
+ llinfos << "Setting input device = \"" << mCaptureDevice << "\"" << llendl;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
+ << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
+ << "</Request>"
+ << "\n\n\n";
+}
+
+void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
+{
+ llinfos << "Setting output device = \"" << mRenderDevice << "\"" << llendl;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
+ << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
+ << "</Request>"
+ << "\n\n\n";
+}
+
+/////////////////////////////
+// Response/Event handlers
+
+void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Connector.Create response failure: " << statusString << llendl;
+ setState(stateConnectorFailed);
+ }
+ else
+ {
+ // Connector created, move forward.
+ mConnectorHandle = connectorHandle;
+ if(getState() == stateConnectorStarting)
+ {
+ setState(stateConnectorStarted);
+ }
+ }
+}
+
+void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle)
+{
+ llinfos << "Account.Login response (" << statusCode << "): " << statusString << llendl;
+
+ // Status code of 20200 means "bad password". We may want to special-case that at some point.
+
+ if ( statusCode == 401 )
+ {
+ // Login failure which is probably caused by the delay after a user's password being updated.
+ llinfos << "Account.Login response failure (" << statusCode << "): " << statusString << llendl;
+ setState(stateLoginRetry);
+ }
+ else if(statusCode != 0)
+ {
+ llwarns << "Account.Login response failure (" << statusCode << "): " << statusString << llendl;
+ setState(stateLoginFailed);
+ }
+ else
+ {
+ // Login succeeded, move forward.
+ mAccountHandle = accountHandle;
+ // MBW -- XXX -- This needs to wait until the LoginStateChangeEvent is received.
+// if(getState() == stateLoggingIn)
+// {
+// setState(stateLoggedIn);
+// }
+ }
+}
+
+void LLVoiceClient::channelGetListResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Account.ChannelGetList response failure: " << statusString << llendl;
+ switchChannel();
+ }
+ else
+ {
+ // Got the channel list, try to do a lookup.
+ std::string uri = findChannelURI(mChannelName);
+ if(uri.empty())
+ {
+ // Lookup failed, can't join a channel for this area.
+ llinfos << "failed to map channel name: " << mChannelName << llendl;
+ }
+ else
+ {
+ // We have a sip URL for this area.
+ llinfos << "mapped channel " << mChannelName << " to URI "<< uri << llendl;
+ }
+
+ // switchChannel with an empty uri string will do the right thing (leave channel and not rejoin)
+ switchChannel(uri);
+ }
+}
+
+void LLVoiceClient::sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Session.Create response failure (" << statusCode << "): " << statusString << llendl;
+// if(statusCode == 1015)
+// {
+// if(getState() == stateJoiningSession)
+// {
+// // this happened during a real join. Going to sessionTerminated should cause a retry in appropriate cases.
+// llwarns << "session handle \"" << sessionHandle << "\", mSessionStateEventHandle \"" << mSessionStateEventHandle << "\""<< llendl;
+// if(!sessionHandle.empty())
+// {
+// // This session is bad. Terminate it.
+// mSessionHandle = sessionHandle;
+// sessionTerminateByHandle(sessionHandle);
+// setState(stateLeavingSession);
+// }
+// else if(!mSessionStateEventHandle.empty())
+// {
+// mSessionHandle = mSessionStateEventHandle;
+// sessionTerminateByHandle(mSessionStateEventHandle);
+// setState(stateLeavingSession);
+// }
+// else
+// {
+// setState(stateSessionTerminated);
+// }
+// }
+// else
+// {
+// // We didn't think we were in the middle of a join. Don't change state.
+// llwarns << "Not in stateJoiningSession, ignoring" << llendl;
+// }
+// }
+// else
+ {
+ mVivoxErrorStatusCode = statusCode;
+ mVivoxErrorStatusString = statusString;
+ setState(stateJoinSessionFailed);
+ }
+ }
+ else
+ {
+ llinfos << "Session.Create response received (success), session handle is " << sessionHandle << llendl;
+ if(getState() == stateJoiningSession)
+ {
+ // This is also grabbed in the SessionStateChangeEvent handler, but it might be useful to have it early...
+ mSessionHandle = sessionHandle;
+ }
+ else
+ {
+ // We should never get a session.create response in any state except stateJoiningSession. Things are out of sync. Kill this session.
+ sessionTerminateByHandle(sessionHandle);
+ }
+ }
+}
+
+void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Session.Connect response failure (" << statusCode << "): " << statusString << llendl;
+// if(statusCode == 1015)
+// {
+// llwarns << "terminating existing session" << llendl;
+// sessionTerminate();
+// }
+// else
+ {
+ mVivoxErrorStatusCode = statusCode;
+ mVivoxErrorStatusString = statusString;
+ setState(stateJoinSessionFailed);
+ }
+ }
+ else
+ {
+ llinfos << "Session.Connect response received (success)" << llendl;
+ }
+}
+
+void LLVoiceClient::sessionTerminateResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Session.Terminate response failure: (" << statusCode << "): " << statusString << llendl;
+ if(getState() == stateLeavingSession)
+ {
+ // This is probably "(404): Server reporting Failure. Not a member of this conference."
+ // Do this so we don't get stuck.
+ setState(stateSessionTerminated);
+ }
+ }
+
+}
+
+void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Account.Logout response failure: " << statusString << llendl;
+ // MBW -- XXX -- Should this ever fail? do we care if it does?
+ }
+
+ if(getState() == stateLoggingOut)
+ {
+ setState(stateLoggedOut);
+ }
+}
+
+void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Connector.InitiateShutdown response failure: " << statusString << llendl;
+ // MBW -- XXX -- Should this ever fail? do we care if it does?
+ }
+
+ mConnected = false;
+
+ if(getState() == stateConnectorStopping)
+ {
+ setState(stateConnectorStopped);
+ }
+}
+
+void LLVoiceClient::sessionStateChangeEvent(
+ std::string &uriString,
+ int statusCode,
+ std::string &statusString,
+ std::string &sessionHandle,
+ int state,
+ bool isChannel,
+ std::string &nameString)
+{
+ switch(state)
+ {
+ case 4: // I see this when joining the session
+ llinfos << "joined session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << llendl;
+
+ // Session create succeeded, move forward.
+ mSessionStateEventHandle = sessionHandle;
+ mSessionStateEventURI = uriString;
+ if(sessionHandle == mSessionHandle)
+ {
+ // This is the session we're joining.
+ if(getState() == stateJoiningSession)
+ {
+ setState(stateSessionJoined);
+ //RN: the uriString being returned by vivox here is actually your account uri, not the channel
+ // you are attempting to join, so ignore it
+ //llinfos << "received URI " << uriString << "(previously " << mSessionURI << ")" << llendl;
+ //mSessionURI = uriString;
+ }
+ }
+ else if(sessionHandle == mNextSessionHandle)
+ {
+// llinfos << "received URI " << uriString << ", name " << nameString << " for next session (handle " << mNextSessionHandle << ")" << llendl;
+ }
+ else
+ {
+ llwarns << "joining unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << llendl;
+ // MBW -- XXX -- Should we send a Session.Terminate here?
+ }
+
+ break;
+ case 5: // I see this when leaving the session
+ llinfos << "left session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << llendl;
+
+ // Set the session handle to the empty string. If we get back to stateJoiningSession, we'll want to wait for the new session handle.
+ if(sessionHandle == mSessionHandle)
+ {
+ // MBW -- XXX -- I think this is no longer necessary, now that we've got mNextSessionURI/mNextSessionHandle
+ // mSessionURI.clear();
+ // clear the session handle here just for sanity.
+ mSessionHandle.clear();
+ if(mSessionResetOnClose)
+ {
+ mSessionResetOnClose = false;
+ mNonSpatialChannel = false;
+ mNextSessionSpatial = true;
+ parcelChanged();
+ }
+
+ removeAllParticipants();
+
+ switch(getState())
+ {
+ case stateJoiningSession:
+ case stateSessionJoined:
+ case stateRunning:
+ case stateLeavingSession:
+ case stateJoinSessionFailed:
+ case stateJoinSessionFailedWaiting:
+ // normal transition
+ llinfos << "left session " << sessionHandle << "in state " << state2string(getState()) << llendl;
+ setState(stateSessionTerminated);
+ break;
+
+ case stateSessionTerminated:
+ // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
+ llwarns << "left session " << sessionHandle << "in state " << state2string(getState()) << llendl;
+ break;
+
+ default:
+ llwarns << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << llendl;
+ setState(stateSessionTerminated);
+ break;
+ }
+
+ // store status values for later notification of observers
+ mVivoxErrorStatusCode = statusCode;
+ mVivoxErrorStatusString = statusString;
+ }
+ else
+ {
+ llinfos << "leaving unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << llendl;
+ }
+
+ mSessionStateEventHandle.clear();
+ mSessionStateEventURI.clear();
+ break;
+ default:
+ llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::loginStateChangeEvent(
+ std::string &accountHandle,
+ int statusCode,
+ std::string &statusString,
+ int state)
+{
+ llinfos << "state is " << state << llendl;
+ /*
+ According to Mike S., status codes for this event are:
+ login_state_logged_out=0,
+ login_state_logged_in = 1,
+ login_state_logging_in = 2,
+ login_state_logging_out = 3,
+ login_state_resetting = 4,
+ login_state_error=100
+ */
+
+ switch(state)
+ {
+ case 1:
+ if(getState() == stateLoggingIn)
+ {
+ setState(stateLoggedIn);
+ }
+ break;
+
+ default:
+// llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::sessionNewEvent(
+ std::string &accountHandle,
+ std::string &eventSessionHandle,
+ int state,
+ std::string &nameString,
+ std::string &uriString)
+{
+// llinfos << "state is " << state << llendl;
+
+ switch(state)
+ {
+ case 0:
+ {
+ llinfos << "session handle = " << eventSessionHandle << ", name = " << nameString << ", uri = " << uriString << llendl;
+
+ LLUUID caller_id;
+ if(IDFromName(nameString, caller_id))
+ {
+ gIMMgr->inviteToSession(LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, caller_id),
+ LLString::null,
+ caller_id,
+ LLString::null,
+ IM_SESSION_P2P_INVITE,
+ eventSessionHandle);
+ }
+ else
+ {
+ llwarns << "Could not generate caller id from uri " << uriString << llendl;
+ }
+ }
+ break;
+
+ default:
+ llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::participantStateChangeEvent(
+ std::string &uriString,
+ int statusCode,
+ std::string &statusString,
+ int state,
+ std::string &nameString,
+ std::string &displayNameString,
+ int participantType)
+{
+ participantState *participant = NULL;
+ llinfos << "state is " << state << llendl;
+
+ switch(state)
+ {
+ case 7: // I see this when a participant joins
+ participant = addParticipant(uriString);
+ if(participant)
+ {
+ participant->mName = nameString;
+ llinfos << "added participant \"" << participant->mName
+ << "\" (" << participant->mAvatarID << ")"<< llendl;
+ }
+ break;
+ case 9: // I see this when a participant leaves
+ participant = findParticipant(uriString);
+ if(participant)
+ {
+ removeParticipant(participant);
+ }
+ break;
+ default:
+// llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::participantPropertiesEvent(
+ std::string &uriString,
+ int statusCode,
+ std::string &statusString,
+ bool isLocallyMuted,
+ bool isModeratorMuted,
+ bool isSpeaking,
+ int volume,
+ F32 energy)
+{
+ participantState *participant = findParticipant(uriString);
+ if(participant)
+ {
+ participant->mPTT = !isLocallyMuted;
+ participant->mIsSpeaking = isSpeaking;
+ if (isSpeaking)
+ {
+ participant->mSpeakingTimeout.reset();
+ }
+ participant->mPower = energy;
+ participant->mVolume = volume;
+ }
+ else
+ {
+ llwarns << "unknown participant: " << uriString << llendl;
+ }
+}
+
+void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
+{
+// llinfos << "got energy " << energy << llendl;
+ mTuningEnergy = energy;
+}
+
+void LLVoiceClient::muteListChanged()
+{
+ // The user's mute list has been updated. Go through the current participant list and sync it with the mute list.
+
+ participantMap::iterator iter = mParticipantMap.begin();
+
+ for(; iter != mParticipantMap.end(); iter++)
+ {
+ participantState *p = iter->second;
+
+ // Check to see if this participant is on the mute list already
+ updateMuteState(p);
+ }
+}
+
+/////////////////////////////
+// Managing list of participants
+LLVoiceClient::participantState::participantState(const std::string &uri) :
+ mURI(uri), mPTT(false), mIsSpeaking(false), mPower(0.0), mServiceType(serviceTypeUnknown),
+ mOnMuteList(false), mUserVolume(100), mVolumeDirty(false), mAvatarIDValid(false)
+{
+}
+
+LLVoiceClient::participantState *LLVoiceClient::addParticipant(const std::string &uri)
+{
+ participantState *result = NULL;
+
+ participantMap::iterator iter = mParticipantMap.find(uri);
+
+ if(iter != mParticipantMap.end())
+ {
+ // Found a matching participant already in the map.
+ result = iter->second;
+ }
+
+ if(!result)
+ {
+ // participant isn't already in one list or the other.
+ result = new participantState(uri);
+ mParticipantMap.insert(participantMap::value_type(uri, result));
+ mParticipantMapChanged = true;
+
+ // Try to do a reverse transform on the URI to get the GUID back.
+ {
+ LLUUID id;
+ if(IDFromName(uri, id))
+ {
+ result->mAvatarIDValid = true;
+ result->mAvatarID = id;
+
+ updateMuteState(result);
+ }
+ }
+
+ llinfos << "participant \"" << result->mURI << "\" added." << llendl;
+ }
+
+ return result;
+}
+
+void LLVoiceClient::updateMuteState(participantState *p)
+{
+ if(p->mAvatarIDValid)
+ {
+ bool isMuted = gMuteListp->isMuted(p->mAvatarID, LLMute::flagVoiceChat);
+ if(p->mOnMuteList != isMuted)
+ {
+ p->mOnMuteList = isMuted;
+ p->mVolumeDirty = true;
+ mVolumeDirty = true;
+ }
+ }
+}
+
+void LLVoiceClient::removeParticipant(LLVoiceClient::participantState *participant)
+{
+ if(participant)
+ {
+ participantMap::iterator iter = mParticipantMap.find(participant->mURI);
+
+ llinfos << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << llendl;
+
+ mParticipantMap.erase(iter);
+ delete participant;
+ mParticipantMapChanged = true;
+ }
+}
+
+void LLVoiceClient::removeAllParticipants()
+{
+ llinfos << "called" << llendl;
+
+ while(!mParticipantMap.empty())
+ {
+ removeParticipant(mParticipantMap.begin()->second);
+ }
+}
+
+LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
+{
+ return &mParticipantMap;
+}
+
+
+LLVoiceClient::participantState *LLVoiceClient::findParticipant(const std::string &uri)
+{
+ participantState *result = NULL;
+
+ // Don't find any participants if we're not connected. This is so that we don't continue to get stale data
+ // after the daemon dies.
+ if(mConnected)
+ {
+ participantMap::iterator iter = mParticipantMap.find(uri);
+
+ if(iter != mParticipantMap.end())
+ {
+ result = iter->second;
+ }
+ }
+
+ return result;
+}
+
+
+LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvatar *avatar)
+{
+ participantState * result = NULL;
+
+ // You'd think this would work, but it doesn't...
+// std::string uri = sipURIFromAvatar(avatar);
+
+ // Currently, the URI is just the account name.
+ std::string loginName = nameFromAvatar(avatar);
+ result = findParticipant(loginName);
+
+ if(result != NULL)
+ {
+ if(!result->mAvatarIDValid)
+ {
+ result->mAvatarID = avatar->getID();
+ result->mAvatarIDValid = true;
+
+ // We just figured out the avatar ID, so the participant list has "changed" from the perspective of anyone who uses that to identify participants.
+ mParticipantMapChanged = true;
+
+ updateMuteState(result);
+ }
+
+
+ }
+
+ return result;
+}
+
+LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
+{
+ participantState * result = NULL;
+
+ // Currently, the URI is just the account name.
+ std::string loginName = nameFromID(id);
+ result = findParticipant(loginName);
+
+ return result;
+}
+
+
+void LLVoiceClient::clearChannelMap(void)
+{
+ mChannelMap.clear();
+}
+
+void LLVoiceClient::addChannelMapEntry(std::string &name, std::string &uri)
+{
+// llinfos << "Adding channel name mapping: " << name << " -> " << uri << llendl;
+ mChannelMap.insert(channelMap::value_type(name, uri));
+}
+
+std::string LLVoiceClient::findChannelURI(std::string &name)
+{
+ std::string result;
+
+ channelMap::iterator iter = mChannelMap.find(name);
+
+ if(iter != mChannelMap.end())
+ {
+ result = iter->second;
+ }
+
+ return result;
+}
+
+void LLVoiceClient::parcelChanged()
+{
+ if(getState() >= stateLoggedIn)
+ {
+ // If the user is logged in, start a channel lookup.
+ llinfos << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << llendl;
+
+ std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
+ LLSD data;
+ data["method"] = "call";
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLVoiceClientCapResponder);
+ }
+ else
+ {
+ // The transition to stateLoggedIn needs to kick this off again.
+ llinfos << "not logged in yet, deferring" << llendl;
+ }
+}
+
+void LLVoiceClient::switchChannel(
+ std::string uri,
+ bool spatial,
+ bool noReconnect,
+ std::string hash)
+{
+ bool needsSwitch = false;
+
+ llinfos << "called in state " << state2string(getState()) << " with uri \"" << uri << "\"" << llendl;
+
+ switch(getState())
+ {
+ case stateJoinSessionFailed:
+ case stateJoinSessionFailedWaiting:
+ case stateNoChannel:
+ // Always switch to the new URI from these states.
+ needsSwitch = true;
+ break;
+
+ default:
+ if(mSessionTerminateRequested)
+ {
+ // If a terminate has been requested, we need to compare against where the URI we're already headed to.
+ if(mNextSessionURI != uri)
+ needsSwitch = true;
+ }
+ else
+ {
+ // Otherwise, compare against the URI we're in now.
+ if(mSessionURI != uri)
+ needsSwitch = true;
+ }
+ break;
+ }
+
+ if(needsSwitch)
+ {
+ mNextSessionURI = uri;
+ mNextSessionHash = hash;
+ mNextSessionHandle.clear();
+ mNextP2PSessionURI.clear();
+ mNextSessionSpatial = spatial;
+ mNextSessionNoReconnect = noReconnect;
+
+ if(uri.empty())
+ {
+ // Leave any channel we may be in
+ llinfos << "leaving channel" << llendl;
+ }
+ else
+ {
+ llinfos << "switching to channel " << uri << llendl;
+ }
+
+ if(getState() <= stateNoChannel)
+ {
+ // We're already set up to join a channel, just needed to fill in the session URI
+ }
+ else
+ {
+ // State machine will come around and rejoin if uri/handle is not empty.
+ sessionTerminate();
+ }
+ }
+}
+
+void LLVoiceClient::joinSession(std::string handle, std::string uri)
+{
+ mNextSessionURI.clear();
+ mNextSessionHash.clear();
+ mNextP2PSessionURI = uri;
+ mNextSessionHandle = handle;
+ mNextSessionSpatial = false;
+ mNextSessionNoReconnect = false;
+
+ if(getState() <= stateNoChannel)
+ {
+ // We're already set up to join a channel, just needed to fill in the session handle
+ }
+ else
+ {
+ // State machine will come around and rejoin if uri/handle is not empty.
+ sessionTerminate();
+ }
+}
+
+void LLVoiceClient::setNonSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials)
+{
+ switchChannel(uri, false, false, credentials);
+}
+
+void LLVoiceClient::setSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials)
+{
+ mSpatialSessionURI = uri;
+ mAreaVoiceDisabled = mSpatialSessionURI.empty();
+
+ llinfos << "got spatial channel uri: \"" << uri << "\"" << llendl;
+
+ if(mNonSpatialChannel || !mNextSessionSpatial)
+ {
+ // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels.
+ llinfos << "in non-spatial chat, not switching channels" << llendl;
+ }
+ else
+ {
+ switchChannel(mSpatialSessionURI, true, false, credentials);
+ }
+}
+
+void LLVoiceClient::callUser(LLUUID &uuid)
+{
+ std::string userURI = sipURIFromID(uuid);
+
+ switchChannel(userURI, false, true);
+}
+
+void LLVoiceClient::answerInvite(std::string &sessionHandle, LLUUID& other_user_id)
+{
+ joinSession(sessionHandle, sipURIFromID(other_user_id));
+}
+
+void LLVoiceClient::declineInvite(std::string &sessionHandle)
+{
+ sessionTerminateByHandle(sessionHandle);
+}
+
+void LLVoiceClient::leaveNonSpatialChannel()
+{
+ switchChannel(mSpatialSessionURI);
+}
+
+std::string LLVoiceClient::getCurrentChannel()
+{
+ if((getState() == stateRunning) && !mSessionTerminateRequested)
+ {
+ return mSessionURI;
+ }
+
+ return "";
+}
+
+bool LLVoiceClient::inProximalChannel()
+{
+ bool result = false;
+
+ if((getState() == stateRunning) && !mSessionTerminateRequested)
+ {
+ result = !mNonSpatialChannel;
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
+{
+ std::string result;
+ result = "sip:";
+ result += nameFromID(id);
+ result += "@";
+ result += mAccountServerName;
+
+ return result;
+}
+
+std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
+{
+ std::string result;
+ if(avatar)
+ {
+ result = "sip:";
+ result += nameFromID(avatar->getID());
+ result += "@";
+ result += mAccountServerName;
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
+{
+ std::string result;
+ if(avatar)
+ {
+ result = nameFromID(avatar->getID());
+ }
+ return result;
+}
+
+std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
+{
+ std::string result;
+ U8 rawuuid[UUID_BYTES + 1];
+ uuid.toCompressedString((char*)rawuuid);
+
+ // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
+ result = "x";
+
+ // Base64 encode and replace the pieces of base64 that are less compatible
+ // with e-mail local-parts.
+ // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
+ result += LLBase64::encode(rawuuid, UUID_BYTES);
+ LLString::replaceChar(result, '+', '-');
+ LLString::replaceChar(result, '/', '_');
+
+ // If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
+ // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
+
+ return result;
+}
+
+bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
+{
+ bool result = false;
+
+ // This will only work if the name is of the proper form.
+ // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
+ // "xFnPP04IpREWNkuw1cOXlhw=="
+
+ if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
+ {
+ // The name appears to have the right form.
+
+ // Reverse the transforms done by nameFromID
+ std::string temp = name;
+ LLString::replaceChar(temp, '-', '+');
+ LLString::replaceChar(temp, '_', '/');
+
+ U8 rawuuid[UUID_BYTES + 1];
+ int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1);
+ if(len == UUID_BYTES)
+ {
+ // The decode succeeded. Stuff the bits into the result's UUID
+ // MBW -- XXX -- there's no analogue of LLUUID::toCompressedString that allows you to set a UUID from binary data.
+ // The data field is public, so we cheat thusly:
+ memcpy(uuid.mData, rawuuid, UUID_BYTES);
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar)
+{
+ return avatar->getFullname();
+}
+
+std::string LLVoiceClient::sipURIFromName(std::string &name)
+{
+ std::string result;
+ result = "sip:";
+ result += name;
+ result += "@";
+ result += mAccountServerName;
+
+// LLString::toLower(result);
+
+ return result;
+}
+
+/////////////////////////////
+// Sending updates of current state
+
+void LLVoiceClient::enforceTether(void)
+{
+ LLVector3d tethered = mCameraRequestedPosition;
+
+ // constrain 'tethered' to within 50m of mAvatarPosition.
+ {
+ F32 max_dist = 50.0f;
+ LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
+ F32 camera_distance = (F32)camera_offset.magVec();
+ if(camera_distance > max_dist)
+ {
+ tethered = mAvatarPosition +
+ (max_dist / camera_distance) * camera_offset;
+ }
+ }
+
+ if(dist_vec(mCameraPosition, tethered) > 0.1)
+ {
+ mCameraPosition = tethered;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+{
+ mCameraRequestedPosition = position;
+
+ if(mCameraVelocity != velocity)
+ {
+ mCameraVelocity = velocity;
+ mSpatialCoordsDirty = true;
+ }
+
+ if(mCameraRot != rot)
+ {
+ mCameraRot = rot;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+{
+ if(dist_vec(mAvatarPosition, position) > 0.1)
+ {
+ mAvatarPosition = position;
+ mSpatialCoordsDirty = true;
+ }
+
+ if(mAvatarVelocity != velocity)
+ {
+ mAvatarVelocity = velocity;
+ mSpatialCoordsDirty = true;
+ }
+
+ if(mAvatarRot != rot)
+ {
+ mAvatarRot = rot;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name)
+{
+ bool result = false;
+
+ if(region)
+ {
+ name = region->getName();
+ }
+
+ if(!name.empty())
+ result = true;
+
+ return result;
+}
+
+void LLVoiceClient::leaveChannel(void)
+{
+ if(getState() == stateRunning)
+ {
+// llinfos << "leaving channel for teleport/logout" << llendl;
+ mChannelName.clear();
+ sessionTerminate();
+ }
+}
+
+void LLVoiceClient::setMuteMic(bool muted)
+{
+ mMuteMic = muted;
+}
+
+void LLVoiceClient::setUserPTTState(bool ptt)
+{
+ mUserPTTState = ptt;
+}
+
+bool LLVoiceClient::getUserPTTState()
+{
+ return mUserPTTState;
+}
+
+void LLVoiceClient::toggleUserPTTState(void)
+{
+ mUserPTTState = !mUserPTTState;
+}
+
+void LLVoiceClient::setVoiceEnabled(bool enabled)
+{
+ if (enabled != mVoiceEnabled)
+ {
+ mVoiceEnabled = enabled;
+ if (enabled)
+ {
+ LLVoiceChannel::getCurrentVoiceChannel()->activate();
+ }
+ else
+ {
+ // for now, leave active channel, to auto join when turning voice back on
+ //LLVoiceChannel::getCurrentVoiceChannel->deactivate();
+ }
+ }
+}
+
+bool LLVoiceClient::voiceEnabled()
+{
+ return gSavedSettings.getBOOL("EnableVoiceChat") && !gDisableVoice;
+}
+
+void LLVoiceClient::setUsePTT(bool usePTT)
+{
+ if(usePTT && !mUsePTT)
+ {
+ // When the user turns on PTT, reset the current state.
+ mUserPTTState = false;
+ }
+ mUsePTT = usePTT;
+}
+
+void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
+{
+ if(!PTTIsToggle && mPTTIsToggle)
+ {
+ // When the user turns off toggle, reset the current state.
+ mUserPTTState = false;
+ }
+
+ mPTTIsToggle = PTTIsToggle;
+}
+
+
+void LLVoiceClient::setPTTKey(std::string &key)
+{
+ if(key == "MiddleMouse")
+ {
+ mPTTIsMiddleMouse = true;
+ }
+ else
+ {
+ mPTTIsMiddleMouse = false;
+ if(!LLKeyboard::keyFromString(key, &mPTTKey))
+ {
+ // If the call failed, don't match any key.
+ key = KEY_NONE;
+ }
+ }
+}
+
+void LLVoiceClient::setEarLocation(S32 loc)
+{
+ if(mEarLocation != loc)
+ {
+ llinfos << "Setting mEarLocation to " << loc << llendl;
+
+ mEarLocation = loc;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLVoiceClient::setVoiceVolume(F32 volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mSpeakerVolume)
+ {
+ if((scaledVolume == -100) || (mSpeakerVolume == -100))
+ {
+ mSpeakerMuteDirty = true;
+ }
+
+ mSpeakerVolume = scaledVolume;
+ mSpeakerVolumeDirty = true;
+ }
+}
+
+void LLVoiceClient::setMicGain(F32 volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mMicVolume)
+ {
+ mMicVolume = scaledVolume;
+ mMicVolumeDirty = true;
+ }
+}
+
+void LLVoiceClient::setVivoxDebugServerName(std::string &serverName)
+{
+ if(!mAccountServerName.empty())
+ {
+ // The name has been filled in already, which means we know whether we're connecting to agni or not.
+ if(!sConnectingToAgni)
+ {
+ // Only use the setting if we're connecting to a development grid -- always use bhr when on agni.
+ mAccountServerName = serverName;
+ }
+ }
+}
+
+void LLVoiceClient::keyDown(KEY key, MASK mask)
+{
+// llinfos << "key is " << LLKeyboard::stringFromKey(key) << llendl;
+
+ if (gKeyboard->getKeyRepeated(key))
+ {
+ // ignore auto-repeat keys
+ return;
+ }
+
+ if(!mPTTIsMiddleMouse)
+ {
+ if(mPTTIsToggle)
+ {
+ if(key == mPTTKey)
+ {
+ toggleUserPTTState();
+ }
+ }
+ else if(mPTTKey != KEY_NONE)
+ {
+ setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
+ }
+ }
+}
+void LLVoiceClient::keyUp(KEY key, MASK mask)
+{
+ if(!mPTTIsMiddleMouse)
+ {
+ if(!mPTTIsToggle && (mPTTKey != KEY_NONE))
+ {
+ setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
+ }
+ }
+}
+void LLVoiceClient::middleMouseState(bool down)
+{
+ if(mPTTIsMiddleMouse)
+ {
+ if(mPTTIsToggle)
+ {
+ if(down)
+ {
+ toggleUserPTTState();
+ }
+ }
+ else
+ {
+ setUserPTTState(down);
+ }
+ }
+}
+
+/////////////////////////////
+// Accessors for data related to nearby speakers
+BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
+{
+ BOOL result = FALSE;
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ // I'm not sure what the semantics of this should be.
+ // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
+ result = TRUE;
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
+ {
+ participant->mIsSpeaking = FALSE;
+ }
+ result = participant->mIsSpeaking;
+ }
+
+ return result;
+}
+
+F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
+{
+ F32 result = 0;
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mPower;
+ }
+
+ return result;
+}
+
+
+LLString LLVoiceClient::getDisplayName(const LLUUID& id)
+{
+ LLString result;
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mDisplayName;
+ }
+
+ return result;
+}
+
+
+BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ // I'm not sure what the semantics of this should be.
+ // Does "using PTT" mean they're configured with a push-to-talk button?
+ // For now, we know there's no PTT mechanism in place, so nobody is using it.
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getPTTPressed(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mPTT;
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mOnMuteList;
+ }
+
+ return result;
+}
+
+// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
+// internal = 400 * external^2
+F32 LLVoiceClient::getUserVolume(const LLUUID& id)
+{
+ F32 result = 0.0f;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ S32 ires = participant->mUserVolume; // 0-400
+ result = sqrtf(((F32)ires) / 400.f);
+ }
+
+ return result;
+}
+
+void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
+{
+ participantState *participant = findParticipantByID(id);
+ if (participant)
+ {
+ // volume can amplify by as much as 4x!
+ S32 ivol = (S32)(400.f * volume * volume);
+ participant->mUserVolume = llclamp(ivol, 0, 400);
+ participant->mVolumeDirty = TRUE;
+ mVolumeDirty = TRUE;
+ }
+}
+
+
+
+LLVoiceClient::serviceType LLVoiceClient::getServiceType(const LLUUID& id)
+{
+ serviceType result = serviceTypeUnknown;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mServiceType;
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::getGroupID(const LLUUID& id)
+{
+ std::string result;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mGroupID;
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getAreaVoiceDisabled()
+{
+ return mAreaVoiceDisabled;
+}
+
+void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
+{
+ mObservers.insert(observer);
+}
+
+void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
+{
+ mObservers.erase(observer);
+}
+
+void LLVoiceClient::notifyObservers()
+{
+ for (observer_set_t::iterator it = mObservers.begin();
+ it != mObservers.end();
+ )
+ {
+ LLVoiceClientParticipantObserver* observer = *it;
+ observer->onChange();
+ // In case onChange() deleted an entry.
+ it = mObservers.upper_bound(observer);
+ }
+}
+
+void LLVoiceClient::addStatusObserver(LLVoiceClientStatusObserver* observer)
+{
+ mStatusObservers.insert(observer);
+}
+
+void LLVoiceClient::removeStatusObserver(LLVoiceClientStatusObserver* observer)
+{
+ mStatusObservers.erase(observer);
+}
+
+void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
+{
+ if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
+ {
+ switch(mVivoxErrorStatusCode)
+ {
+ case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break;
+ case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break;
+ }
+
+ // Reset the error code to make sure it won't be reused later by accident.
+ mVivoxErrorStatusCode = 0;
+ }
+
+ if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
+ //NOT_FOUND || TEMPORARILY_UNAVAILABLE || REQUEST_TIMEOUT
+ && (mVivoxErrorStatusCode == 404 || mVivoxErrorStatusCode == 480 || mVivoxErrorStatusCode == 408))
+ {
+ // call failed because other user was not available
+ // treat this as an error case
+ status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+
+ // Reset the error code to make sure it won't be reused later by accident.
+ mVivoxErrorStatusCode = 0;
+ }
+
+ llinfos << " " << LLVoiceClientStatusObserver::status2string(status) << ", session URI " << mSessionURI << llendl;
+
+ for (status_observer_set_t::iterator it = mStatusObservers.begin();
+ it != mStatusObservers.end();
+ )
+ {
+ LLVoiceClientStatusObserver* observer = *it;
+ observer->onChange(status, mSessionURI, !mNonSpatialChannel);
+ // In case onError() deleted an entry.
+ it = mStatusObservers.upper_bound(observer);
+ }
+
+}
+
+//static
+void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* user_data)
+{
+ participantState* statep = gVoiceClient->findParticipantByID(id);
+
+ if (statep)
+ {
+ statep->mDisplayName = llformat("%s %s", first, last);
+ }
+
+ gVoiceClient->notifyObservers();
+}
+
+class LLViewerParcelVoiceInfo : public LLHTTPNode
+{
+ virtual void post(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ //the parcel you are in has changed something about its
+ //voice information
+
+ if ( input.has("body") )
+ {
+ LLSD body = input["body"];
+
+ //body has "region_name" (str), "parcel_local_id"(int),
+ //"voice_credentials" (map).
+
+ //body["voice_credentials"] has "channel_uri" (str),
+ //body["voice_credentials"] has "channel_credentials" (str)
+ if ( body.has("voice_credentials") )
+ {
+ LLSD voice_credentials = body["voice_credentials"];
+ std::string uri;
+ std::string credentials;
+
+ if ( voice_credentials.has("channel_uri") )
+ {
+ uri = voice_credentials["channel_uri"].asString();
+ }
+ if ( voice_credentials.has("channel_credentials") )
+ {
+ credentials =
+ voice_credentials["channel_credentials"].asString();
+ }
+
+ gVoiceClient->setSpatialChannel(uri, credentials);
+ }
+ }
+ }
+};
+
+class LLViewerRequiredVoiceVersion : public LLHTTPNode
+{
+ static BOOL sAlertedUser;
+ virtual void post(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ //You received this messsage (most likely on region cross or
+ //teleport)
+ if ( input.has("body") && input["body"].has("major_version") )
+ {
+ int major_voice_version =
+ input["body"]["major_version"].asInteger();
+// int minor_voice_version =
+// input["body"]["minor_version"].asInteger();
+
+ if (gVoiceClient &&
+ (major_voice_version > VOICE_MAJOR_VERSION) )
+ {
+ if (!sAlertedUser)
+ {
+ //sAlertedUser = TRUE;
+ gViewerWindow->alertXml("VoiceVersionMismatch");
+ gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
+ }
+ }
+ }
+ }
+};
+BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE;
+
+LLHTTPRegistration<LLViewerParcelVoiceInfo>
+ gHTTPRegistrationMessageParcelVoiceInfo(
+ "/message/ParcelVoiceInfo");
+
+LLHTTPRegistration<LLViewerRequiredVoiceVersion>
+ gHTTPRegistrationMessageRequiredVoiceVersion(
+ "/message/RequiredVoiceVersion");
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
new file mode 100644
index 0000000000..d45b113e63
--- /dev/null
+++ b/indra/newview/llvoiceclient.h
@@ -0,0 +1,503 @@
+/**
+ * @file llvoiceclient.h
+ * @brief Declaration of LLVoiceClient class which is the interface to the voice client process.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_VOICE_CLIENT_H
+#define LL_VOICE_CLIENT_H
+
+// This would create a circular reference -- just do a forward definition of necessary class names.
+//#include "llvoavatar.h"
+class LLVOAvatar;
+class LLVivoxProtocolParser;
+
+#include "lliopipe.h"
+#include "llpumpio.h"
+#include "llchainio.h"
+#include "lliosocket.h"
+#include "v3math.h"
+#include "llframetimer.h"
+#include "llviewerregion.h"
+
+class LLVoiceClientParticipantObserver
+{
+public:
+ virtual ~LLVoiceClientParticipantObserver() { }
+ virtual void onChange() = 0;
+};
+
+class LLVoiceClientStatusObserver
+{
+public:
+ typedef enum e_voice_status_type
+ {
+ STATUS_LOGIN_RETRY,
+ STATUS_LOGGED_IN,
+ STATUS_JOINING,
+ STATUS_JOINED,
+ STATUS_LEFT_CHANNEL,
+ BEGIN_ERROR_STATUS,
+ ERROR_CHANNEL_FULL,
+ ERROR_CHANNEL_LOCKED,
+ ERROR_NOT_AVAILABLE,
+ ERROR_UNKNOWN
+ } EStatusType;
+
+ virtual ~LLVoiceClientStatusObserver() { }
+ virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) = 0;
+
+ static const char *status2string(EStatusType inStatus);
+};
+
+class LLVoiceClient: public LLSingleton<LLVoiceClient>
+{
+ LOG_CLASS(LLVoiceClient);
+ public:
+ LLVoiceClient();
+ ~LLVoiceClient();
+
+ public:
+ static void init(LLPumpIO *pump); // Call this once at application startup (creates connector)
+ static void terminate(); // Call this to clean up during shutdown
+
+ protected:
+ bool writeString(const std::string &str);
+
+ public:
+
+ enum serviceType
+ {
+ serviceTypeUnknown, // Unknown, returned if no data on the avatar is available
+ serviceTypeA, // spatialized local chat
+ serviceTypeB, // remote multi-party chat
+ serviceTypeC // one-to-one and small group chat
+ };
+ static F32 OVERDRIVEN_POWER_LEVEL;
+
+ /////////////////////////////
+ // session control messages
+ void connect();
+
+ void connectorCreate();
+ void connectorShutdown();
+
+ void requestVoiceAccountProvision(S32 retries = 3);
+ void userAuthorized(
+ const std::string& firstName,
+ const std::string& lastName,
+ const LLUUID &agentID);
+ void login(const std::string& accountName, const std::string &password);
+ void loginSendMessage();
+ void logout();
+ void logoutSendMessage();
+
+ void channelGetListSendMessage();
+ void sessionCreateSendMessage();
+ void sessionConnectSendMessage();
+ void sessionTerminate();
+ void sessionTerminateSendMessage();
+ void sessionTerminateByHandle(std::string &sessionHandle);
+
+ void getCaptureDevicesSendMessage();
+ void getRenderDevicesSendMessage();
+
+ void clearCaptureDevices();
+ void addCaptureDevice(const std::string& name);
+ void setCaptureDevice(const std::string& name);
+
+ void clearRenderDevices();
+ void addRenderDevice(const std::string& name);
+ void setRenderDevice(const std::string& name);
+
+ void tuningStart();
+ void tuningStop();
+ bool inTuningMode();
+
+ void tuningRenderStartSendMessage(const std::string& name, bool loop);
+ void tuningRenderStopSendMessage();
+
+ void tuningCaptureStartSendMessage(int duration);
+ void tuningCaptureStopSendMessage();
+
+ void tuningSetMicVolume(float volume);
+ void tuningSetSpeakerVolume(float volume);
+ float tuningGetEnergy(void);
+
+ // This returns true when it's safe to bring up the "device settings" dialog in the prefs.
+ // i.e. when the daemon is running and connected, and the device lists are populated.
+ bool deviceSettingsAvailable();
+
+ // Requery the vivox daemon for the current list of input/output devices.
+ // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
+ // (use this if you want to know when it's done).
+ // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
+ void refreshDeviceLists(bool clearCurrentList = true);
+
+ // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch.
+ void daemonDied();
+
+ // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away.
+ void giveUp();
+
+ /////////////////////////////
+ // Response/Event handlers
+ void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle);
+ void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle);
+ void channelGetListResponse(int statusCode, std::string &statusString);
+ void sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle);
+ void sessionConnectResponse(int statusCode, std::string &statusString);
+ void sessionTerminateResponse(int statusCode, std::string &statusString);
+ void logoutResponse(int statusCode, std::string &statusString);
+ void connectorShutdownResponse(int statusCode, std::string &statusString);
+
+ void loginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
+ void sessionNewEvent(std::string &accountHandle, std::string &eventSessionHandle, int state, std::string &nameString, std::string &uriString);
+ void sessionStateChangeEvent(std::string &uriString, int statusCode, std::string &statusString, std::string &sessionHandle, int state, bool isChannel, std::string &nameString);
+ void participantStateChangeEvent(std::string &uriString, int statusCode, std::string &statusString, int state, std::string &nameString, std::string &displayNameString, int participantType);
+ void participantPropertiesEvent(std::string &uriString, int statusCode, std::string &statusString, bool isLocallyMuted, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy);
+ void auxAudioPropertiesEvent(F32 energy);
+
+ void muteListChanged();
+
+ /////////////////////////////
+ // Sending updates of current state
+ void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
+ void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
+ bool channelFromRegion(LLViewerRegion *region, std::string &name);
+ void leaveChannel(void); // call this on logout or teleport begin
+
+
+ void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state.
+ void setUserPTTState(bool ptt);
+ bool getUserPTTState();
+ void toggleUserPTTState(void);
+ void setVoiceEnabled(bool enabled);
+ static bool voiceEnabled();
+ void setUsePTT(bool usePTT);
+ void setPTTIsToggle(bool PTTIsToggle);
+ void setPTTKey(std::string &key);
+ void setEarLocation(S32 loc);
+ void setVoiceVolume(F32 volume);
+ void setMicGain(F32 volume);
+ void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)
+ void setVivoxDebugServerName(std::string &serverName);
+
+ // PTT key triggering
+ void keyDown(KEY key, MASK mask);
+ void keyUp(KEY key, MASK mask);
+ void middleMouseState(bool down);
+
+ /////////////////////////////
+ // Accessors for data related to nearby speakers
+ BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar
+ BOOL getIsSpeaking(const LLUUID& id);
+ F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is...
+ BOOL getPTTPressed(const LLUUID& id); // This is the inverse of the "locally muted" property.
+ BOOL getOnMuteList(const LLUUID& id);
+ F32 getUserVolume(const LLUUID& id);
+ LLString getDisplayName(const LLUUID& id);
+
+ // MBW -- XXX -- Not sure how to get this data out of the TVC
+ BOOL getUsingPTT(const LLUUID& id);
+ serviceType getServiceType(const LLUUID& id); // type of chat the user is involved in (see bHear scope doc for definitions of A/B/C)
+ std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable)
+
+ /////////////////////////////
+ BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled.
+ // Use this to determine whether to show a "no speech" icon in the menu bar.
+
+ struct participantState
+ {
+ public:
+ participantState(const std::string &uri);
+ std::string mURI;
+ std::string mName;
+ std::string mDisplayName;
+ bool mPTT;
+ bool mIsSpeaking;
+ LLFrameTimer mSpeakingTimeout;
+ F32 mLastSpokeTimestamp;
+ F32 mPower;
+ int mVolume;
+ serviceType mServiceType;
+ std::string mGroupID;
+ bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted)
+ int mUserVolume;
+ bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed)
+ bool mAvatarIDValid;
+ LLUUID mAvatarID;
+ };
+ typedef std::map<std::string, participantState*> participantMap;
+
+ participantState *findParticipant(const std::string &uri);
+ participantState *findParticipantByAvatar(LLVOAvatar *avatar);
+ participantState *findParticipantByID(const LLUUID& id);
+
+ participantMap *getParticipantList(void);
+
+ void addObserver(LLVoiceClientParticipantObserver* observer);
+ void removeObserver(LLVoiceClientParticipantObserver* observer);
+
+ void addStatusObserver(LLVoiceClientStatusObserver* observer);
+ void removeStatusObserver(LLVoiceClientStatusObserver* observer);
+
+ static void onAvatarNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* user_data);
+ typedef std::vector<std::string> deviceList;
+
+ deviceList *getCaptureDevices();
+ deviceList *getRenderDevices();
+
+ void setNonSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials);
+ void setSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials);
+ void callUser(LLUUID &uuid);
+ void answerInvite(std::string &sessionHandle, LLUUID& other_user_id);
+ void declineInvite(std::string &sessionHandle);
+ void leaveNonSpatialChannel();
+
+ // Returns the URI of the current channel, or an empty string if not currently in a channel.
+ // NOTE that it will return an empty string if it's in the process of joining a channel.
+ std::string getCurrentChannel();
+
+ // returns true iff the user is currently in a proximal (local spatial) channel.
+ // Note that gestures should only fire if this returns true.
+ bool inProximalChannel();
+
+ std::string sipURIFromID(const LLUUID &id);
+
+ private:
+
+ // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages.
+ // Note: if you change this list, please make corresponding changes to LLVoiceClient::state2string().
+ enum state
+ {
+ stateDisabled, // Voice is turned off.
+ stateStart, // Class is initialized, socket is created
+ stateDaemonLaunched, // Daemon has been launched
+ stateConnecting, // connect() call has been issued
+ stateIdle, // socket is connected, ready for messaging
+ stateConnectorStart, // connector needs to be started
+ stateConnectorStarting, // waiting for connector handle
+ stateConnectorStarted, // connector handle received
+ stateMicTuningNoLogin, // mic tuning before login
+ stateLoginRetry, // need to retry login (failed due to changing password)
+ stateLoginRetryWait, // waiting for retry timer
+ stateNeedsLogin, // send login request
+ stateLoggingIn, // waiting for account handle
+ stateLoggedIn, // account handle received
+ stateNoChannel, //
+ stateMicTuningLoggedIn, // mic tuning for a logged in user
+ stateSessionCreate, // need to send Session.Create command
+ stateSessionConnect, // need to send Session.Connect command
+ stateJoiningSession, // waiting for session handle
+ stateSessionJoined, // session handle received
+ stateRunning, // in session, steady state
+ stateLeavingSession, // waiting for terminate session response
+ stateSessionTerminated, // waiting for terminate session response
+
+ stateLoggingOut, // waiting for logout response
+ stateLoggedOut, // logout response received
+ stateConnectorStopping, // waiting for connector stop
+ stateConnectorStopped, // connector stop received
+
+ // We go to this state if the login fails because the account needs to be provisioned.
+
+ // error states. No way to recover from these yet.
+ stateConnectorFailed,
+ stateConnectorFailedWaiting,
+ stateLoginFailed,
+ stateLoginFailedWaiting,
+ stateJoinSessionFailed,
+ stateJoinSessionFailedWaiting,
+
+ stateJail // Go here when all else has failed. Nothing will be retried, we're done.
+ };
+
+ state mState;
+ bool mSessionTerminateRequested;
+ bool mNonSpatialChannel;
+
+ void setState(state inState);
+ state getState(void) { return mState; };
+ static const char *state2string(state inState);
+
+ void stateMachine();
+ static void idle(void *user_data);
+
+ LLHost mDaemonHost;
+ LLSocket::ptr_t mSocket;
+ bool mConnected;
+
+ void closeSocket(void);
+
+ LLPumpIO *mPump;
+ friend class LLVivoxProtocolParser;
+
+ std::string mAccountName;
+ std::string mAccountPassword;
+ std::string mAccountDisplayName;
+ std::string mAccountFirstName;
+ std::string mAccountLastName;
+
+ std::string mNextP2PSessionURI; // URI of the P2P session to join next
+ std::string mNextSessionURI; // URI of the session to join next
+ std::string mNextSessionHandle; // Session handle of the session to join next
+ std::string mNextSessionHash; // Password hash for the session to join next
+ bool mNextSessionSpatial; // Will next session be a spatial chat?
+ bool mNextSessionNoReconnect; // Next session should not auto-reconnect (i.e. user -> user chat)
+ bool mNextSessionResetOnClose; // If this is true, go back to spatial chat when the next session terminates.
+
+ std::string mSessionStateEventHandle; // session handle received in SessionStateChangeEvents
+ std::string mSessionStateEventURI; // session URI received in SessionStateChangeEvents
+
+ bool mTuningMode;
+ float mTuningEnergy;
+ std::string mTuningAudioFile;
+ int mTuningMicVolume;
+ bool mTuningMicVolumeDirty;
+ int mTuningSpeakerVolume;
+ bool mTuningSpeakerVolumeDirty;
+ bool mTuningCaptureRunning;
+
+ std::string mSpatialSessionURI;
+
+ bool mSessionResetOnClose;
+
+ int mVivoxErrorStatusCode;
+ std::string mVivoxErrorStatusString;
+
+ std::string mChannelName; // Name of the channel to be looked up
+ bool mAreaVoiceDisabled;
+ std::string mSessionURI; // URI of the session we're in.
+ bool mSessionP2P; // true if this session is a p2p call
+
+ S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings
+ std::string mCurrentRegionName; // Used to detect parcel boundary crossings
+
+ std::string mConnectorHandle; // returned by "Create Connector" message
+ std::string mAccountHandle; // returned by login message
+ std::string mSessionHandle; // returned by ?
+ U32 mCommandCookie;
+
+ std::string mAccountServerName;
+ std::string mAccountServerURI;
+
+ int mLoginRetryCount;
+
+ participantMap mParticipantMap;
+ bool mParticipantMapChanged;
+
+ deviceList mCaptureDevices;
+ deviceList mRenderDevices;
+
+ std::string mCaptureDevice;
+ std::string mRenderDevice;
+ bool mCaptureDeviceDirty;
+ bool mRenderDeviceDirty;
+
+ participantState *addParticipant(const std::string &uri);
+ // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted.
+ // Take care not to use the pointer again after that.
+ void removeParticipant(participantState *participant);
+ void removeAllParticipants();
+
+ void updateMuteState(participantState *participant);
+
+ typedef std::map<std::string, std::string> channelMap;
+ channelMap mChannelMap;
+
+ // These are used by the parser when processing a channel list response.
+ void clearChannelMap(void);
+ void addChannelMapEntry(std::string &name, std::string &uri);
+ std::string findChannelURI(std::string &name);
+
+ // This should be called when the code detects we have changed parcels.
+ // It initiates the call to the server that gets the parcel channel.
+ void parcelChanged();
+
+ void switchChannel(std::string uri = "", bool spatial = true, bool noReconnect = false, std::string hash = "");
+ void joinSession(std::string handle, std::string uri);
+
+ std::string nameFromAvatar(LLVOAvatar *avatar);
+ std::string nameFromID(const LLUUID &id);
+ bool IDFromName(const std::string name, LLUUID &uuid);
+ std::string displayNameFromAvatar(LLVOAvatar *avatar);
+ std::string sipURIFromAvatar(LLVOAvatar *avatar);
+ std::string sipURIFromName(std::string &name);
+
+ void sendPositionalUpdate(void);
+
+ void buildSetCaptureDevice(std::ostringstream &stream);
+ void buildSetRenderDevice(std::ostringstream &stream);
+
+ void enforceTether(void);
+
+ bool mSpatialCoordsDirty;
+
+ LLVector3d mCameraPosition;
+ LLVector3d mCameraRequestedPosition;
+ LLVector3 mCameraVelocity;
+ LLMatrix3 mCameraRot;
+
+ LLVector3d mAvatarPosition;
+ LLVector3 mAvatarVelocity;
+ LLMatrix3 mAvatarRot;
+
+ bool mPTTDirty;
+ bool mPTT;
+
+ bool mUsePTT;
+ bool mPTTIsMiddleMouse;
+ KEY mPTTKey;
+ bool mPTTIsToggle;
+ bool mUserPTTState;
+ bool mMuteMic;
+
+ // Set to true when the mute state of someone in the participant list changes.
+ // The code will have to walk the list to find the changed participant(s).
+ bool mVolumeDirty;
+
+ enum
+ {
+ earLocCamera = 0, // ear at camera
+ earLocAvatar, // ear at avatar
+ earLocMixed // ear at avatar location/camera direction
+ };
+
+ S32 mEarLocation;
+
+ bool mSpeakerVolumeDirty;
+ bool mSpeakerMuteDirty;
+ int mSpeakerVolume;
+
+ int mMicVolume;
+ bool mMicVolumeDirty;
+
+ bool mVoiceEnabled;
+ bool mWriteInProgress;
+ std::string mWriteString;
+ size_t mWriteOffset;
+
+ LLTimer mUpdateTimer;
+
+ typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
+ observer_set_t mObservers;
+
+ void notifyObservers();
+
+ typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t;
+ status_observer_set_t mStatusObservers;
+
+ void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status);
+};
+
+extern LLVoiceClient *gVoiceClient;
+
+#endif //LL_VOICE_CLIENT_H
+
+
diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp
new file mode 100644
index 0000000000..fb3fd8af15
--- /dev/null
+++ b/indra/newview/llvoicevisualizer.cpp
@@ -0,0 +1,439 @@
+/**
+ * @file llvoicevisualizer.cpp
+ * @brief Draws in-world speaking indicators.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//----------------------------------------------------------------------
+// Voice Visualizer
+// author: JJ Ventrella
+// (information about this stuff can be found in "llvoicevisualizer.h")
+//----------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+#include "llviewercontrol.h"
+#include "llglheaders.h"
+#include "llsphere.h"
+#include "llvoicevisualizer.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llimagegl.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llvoiceclient.h"
+
+//brent's wave image
+//29de489d-0491-fb00-7dab-f9e686d31e83
+
+
+//--------------------------------------------------------------------------------------
+// sound symbol constants
+//--------------------------------------------------------------------------------------
+const F32 HEIGHT_ABOVE_HEAD = 0.3f; // how many meters vertically above the av's head the voice symbol will appear
+const F32 RED_THRESHOLD = LLVoiceClient::OVERDRIVEN_POWER_LEVEL; // value above which speaking amplitude causes the voice symbol to turn red
+const F32 GREEN_THRESHOLD = 0.2f; // value above which speaking amplitude causes the voice symbol to turn green
+const F32 FADE_OUT_DURATION = 0.4f; // how many seconds it takes for a pair of waves to fade away
+const F32 EXPANSION_RATE = 1.0f; // how many seconds it takes for the waves to expand to twice their original size
+const F32 EXPANSION_MAX = 1.5f; // maximum size scale to which the waves can expand before popping back to 1.0
+const F32 WAVE_WIDTH_SCALE = 0.03f; // base width of the waves
+const F32 WAVE_HEIGHT_SCALE = 0.02f; // base height of the waves
+const F32 BASE_BRIGHTNESS = 0.7f; // gray level of the voice indicator when quiet (below green threshold)
+const F32 DOT_SIZE = 0.05f; // size of the dot billboard texture
+const F32 DOT_OPACITY = 0.7f; // how opaque the dot is
+const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a function of speaking amplitude
+
+//--------------------------------------------------------------------------------------
+// gesticulation constants
+//--------------------------------------------------------------------------------------
+const F32 DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE = 0.2f;
+const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f;
+
+//--------------------------------------------------------------------------------------
+// other constants
+//--------------------------------------------------------------------------------------
+const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code.
+const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL
+
+//-----------------------------------------------
+// constructor
+//-----------------------------------------------
+LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
+:LLHUDEffect( type )
+{
+ mCurrentTime = mTimer.getTotalSeconds();
+ mPreviousTime = mCurrentTime;
+ mVoiceSourceWorldPosition = LLVector3( 0.0f, 0.0f, 0.0f );
+ mSpeakingAmplitude = 0.0f;
+ mCurrentlySpeaking = false;
+ mVoiceEnabled = false;
+ mMinGesticulationAmplitude = DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE;
+ mMaxGesticulationAmplitude = DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE;
+ mSoundSymbol.mActive = true;
+ mSoundSymbol.mPosition = LLVector3( 0.0f, 0.0f, 0.0f );
+
+ mTimer.reset();
+
+ LLUUID sound_level_img[] =
+ {
+ LLUUID(gSavedSettings.getString("VoiceImageLevel0")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel1")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel2")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel3")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel4")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel5")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel6"))
+ };
+
+ for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++)
+ {
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ mSoundSymbol.mTexture [i] = gImageList.getUIImageByID(sound_level_img[i]);
+ mSoundSymbol.mWaveActive [i] = false;
+ mSoundSymbol.mWaveOpacity [i] = 1.0f;
+ mSoundSymbol.mWaveExpansion [i] = 1.0f;
+ }
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setMinGesticulationAmplitude( F32 m )
+{
+ mMinGesticulationAmplitude = m;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setMaxGesticulationAmplitude( F32 m )
+{
+ mMaxGesticulationAmplitude = m;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setVoiceEnabled( bool v )
+{
+ mVoiceEnabled = v;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setStartSpeaking()
+{
+ mCurrentlySpeaking = true;
+ mSoundSymbol.mActive = true;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+bool LLVoiceVisualizer::getCurrentlySpeaking()
+{
+ return mCurrentlySpeaking;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setStopSpeaking()
+{
+ mCurrentlySpeaking = false;
+ mSpeakingAmplitude = 0.0f;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setSpeakingAmplitude( F32 a )
+{
+ mSpeakingAmplitude = a;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+// this method is inherited from HUD Effect
+//---------------------------------------------------
+void LLVoiceVisualizer::render()
+{
+ if ( ! mVoiceEnabled )
+ {
+ return;
+ }
+
+ if ( mSoundSymbol.mActive )
+ {
+ mPreviousTime = mCurrentTime;
+ mCurrentTime = mTimer.getTotalSeconds();
+
+ //---------------------------------------------------------------
+ // set the sound symbol position over the source (avatar's head)
+ //---------------------------------------------------------------
+ mSoundSymbol.mPosition = mVoiceSourceWorldPosition + WORLD_UPWARD_DIRECTION * HEIGHT_ABOVE_HEAD;
+
+ //---------------------------------------------------------------
+ // some gl state
+ //---------------------------------------------------------------
+ LLGLEnable tex( GL_TEXTURE_2D );
+ LLGLEnable blend( GL_BLEND );
+
+ //-------------------------------------------------------------
+ // create coordinates of the geometry for the dot
+ //-------------------------------------------------------------
+ LLVector3 l = gCamera->getLeftAxis() * DOT_SIZE;
+ LLVector3 u = gCamera->getUpAxis() * DOT_SIZE;
+
+ LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u;
+ LLVector3 bottomRight = mSoundSymbol.mPosition - l - u;
+ LLVector3 topLeft = mSoundSymbol.mPosition + l + u;
+ LLVector3 topRight = mSoundSymbol.mPosition - l + u;
+
+ //-----------------------------
+ // bind texture 0 (the dot)
+ //-----------------------------
+ mSoundSymbol.mTexture[0]->bind();
+
+ //-------------------------------------------------------------
+ // now render the dot
+ //-------------------------------------------------------------
+ glColor4fv( LLColor4( 1.0f, 1.0f, 1.0f, DOT_OPACITY ).mV );
+
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 0, 0 ); glVertex3fv( bottomLeft.mV );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 1, 1 ); glVertex3fv( topRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+
+
+ //--------------------------------------------------------------------------------------
+ // if currently speaking, trigger waves (1 through 6) based on speaking amplitude
+ //--------------------------------------------------------------------------------------
+ if ( mCurrentlySpeaking )
+ {
+ F32 min = 0.2f;
+ F32 max = 0.7f;
+ F32 fraction = ( mSpeakingAmplitude - min ) / ( max - min );
+
+ // in case mSpeakingAmplitude > max....
+ if ( fraction > 1.0f )
+ {
+ fraction = 1.0f;
+ }
+
+ S32 level = 1 + (int)( fraction * ( NUM_VOICE_SYMBOL_WAVES - 2 ) );
+
+ for (int i=0; i<level+1; i++)
+ {
+ mSoundSymbol.mWaveActive [i] = true;
+ mSoundSymbol.mWaveOpacity [i] = 1.0f;
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ }
+
+ } // if currently speaking
+
+ //---------------------------------------------------
+ // determine color
+ //---------------------------------------------------
+ F32 red = 0.0f;
+ F32 green = 0.0f;
+ F32 blue = 0.0f;
+ if ( mSpeakingAmplitude < RED_THRESHOLD )
+ {
+ if ( mSpeakingAmplitude < GREEN_THRESHOLD )
+ {
+ red = BASE_BRIGHTNESS;
+ green = BASE_BRIGHTNESS;
+ blue = BASE_BRIGHTNESS;
+ }
+ else
+ {
+ //---------------------------------------------------
+ // fade from gray to bright green
+ //---------------------------------------------------
+ F32 fraction = ( mSpeakingAmplitude - GREEN_THRESHOLD ) / ( 1.0f - GREEN_THRESHOLD );
+ red = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS );
+ green = BASE_BRIGHTNESS + fraction * ( 1.0f - BASE_BRIGHTNESS );
+ blue = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS );
+ }
+ }
+ else
+ {
+ //---------------------------------------------------
+ // redish
+ //---------------------------------------------------
+ red = 1.0f;
+ green = 0.2f;
+ blue = 0.2f;
+ }
+
+ for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++)
+ {
+ if ( mSoundSymbol.mWaveActive[i] )
+ {
+ F32 fadeOutFraction = (F32)( mCurrentTime - mSoundSymbol.mWaveFadeOutStartTime[i] ) / FADE_OUT_DURATION;
+
+ mSoundSymbol.mWaveOpacity[i] = 1.0f - fadeOutFraction;
+
+ if ( mSoundSymbol.mWaveOpacity[i] < 0.0f )
+ {
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ mSoundSymbol.mWaveOpacity [i] = 0.0f;
+ mSoundSymbol.mWaveActive [i] = false;
+ }
+
+ //----------------------------------------------------------------------------------
+ // This is where we calculate the expansion of the waves - that is, the
+ // rate at which they are scaled greater than 1.0 so that they grow over time.
+ //----------------------------------------------------------------------------------
+ F32 timeSlice = (F32)( mCurrentTime - mPreviousTime );
+ F32 waveSpeed = mSpeakingAmplitude * WAVE_MOTION_RATE;
+ mSoundSymbol.mWaveExpansion[i] *= ( 1.0f + EXPANSION_RATE * timeSlice * waveSpeed );
+
+ if ( mSoundSymbol.mWaveExpansion[i] > EXPANSION_MAX )
+ {
+ mSoundSymbol.mWaveExpansion[i] = 1.0f;
+ }
+
+ //----------------------------------------------------------------------------------
+ // create geometry for the wave billboard textures
+ //----------------------------------------------------------------------------------
+ F32 width = i * WAVE_WIDTH_SCALE * mSoundSymbol.mWaveExpansion[i];
+ F32 height = i * WAVE_HEIGHT_SCALE * mSoundSymbol.mWaveExpansion[i];
+
+ LLVector3 l = gCamera->getLeftAxis() * width;
+ LLVector3 u = gCamera->getUpAxis() * height;
+
+ LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u;
+ LLVector3 bottomRight = mSoundSymbol.mPosition - l - u;
+ LLVector3 topLeft = mSoundSymbol.mPosition + l + u;
+ LLVector3 topRight = mSoundSymbol.mPosition - l + u;
+
+ glColor4fv( LLColor4( red, green, blue, mSoundSymbol.mWaveOpacity[i] ).mV );
+ mSoundSymbol.mTexture[i]->bind();
+
+ //---------------------------------------------------
+ // now, render the mofo
+ //---------------------------------------------------
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 0, 0 ); glVertex3fv( bottomLeft.mV );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 1, 1 ); glVertex3fv( topRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+ } //if ( mSoundSymbol.mWaveActive[i] )
+
+ }// for loop
+
+ }//if ( mSoundSymbol.mActive )
+
+}//---------------------------------------------------
+
+
+
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p )
+{
+ mVoiceSourceWorldPosition = p;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+VoiceGesticulationLevel LLVoiceVisualizer::getCurrentGesticulationLevel()
+{
+ VoiceGesticulationLevel gesticulationLevel = VOICE_GESTICULATION_LEVEL_OFF; //default
+
+ //-----------------------------------------------------------------------------------------
+ // Within the range of gesticulation amplitudes, the sound signal is split into
+ // three equal amplitude regimes, each specifying one of three gesticulation levels.
+ //-----------------------------------------------------------------------------------------
+ F32 range = mMaxGesticulationAmplitude - mMinGesticulationAmplitude;
+
+ if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.66666f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_HIGH; }
+ else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.33333f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_MEDIUM; }
+ else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.00000f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_LOW; }
+
+ return gesticulationLevel;
+
+}//---------------------------------------------------
+
+
+
+//------------------------------------
+// Destructor
+//------------------------------------
+LLVoiceVisualizer::~LLVoiceVisualizer()
+{
+}//----------------------------------------------
+
+
+//---------------------------------------------------
+// "packData" is inherited from HUDEffect
+//---------------------------------------------------
+void LLVoiceVisualizer::packData(LLMessageSystem *mesgsys)
+{
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // TODO -- pack the relevant data for voice effects
+ // we'll come up with some cool configurations....TBD
+ //U8 packed_data[41];
+ //mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 41);
+ U8 packed_data = 0;
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, &packed_data, 1);
+}
+
+
+//---------------------------------------------------
+// "unpackData" is inherited from HUDEffect
+//---------------------------------------------------
+void LLVoiceVisualizer::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ // TODO -- find the speaker, unpack binary data, set the properties of this effect
+ /*
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != 1)
+ {
+ llwarns << "Voice effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 1, blocknum);
+ */
+}
+
+
+//------------------------------------------------------------------
+// this method is inherited from HUD Effect
+//------------------------------------------------------------------
+void LLVoiceVisualizer::markDead()
+{
+ mCurrentlySpeaking = false;
+ mVoiceEnabled = false;
+ mSoundSymbol.mActive = false;
+
+}//------------------------------------------------------------------
+
+
+
+
+
+
+
+
diff --git a/indra/newview/llvoicevisualizer.h b/indra/newview/llvoicevisualizer.h
new file mode 100644
index 0000000000..bd54bd435b
--- /dev/null
+++ b/indra/newview/llvoicevisualizer.h
@@ -0,0 +1,119 @@
+/**
+ * @file llvoicevisualizer.h
+ * @brief Draws in-world speaking indicators.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//--------------------------------------------------------------------
+//
+// VOICE VISUALIZER
+// author: JJ Ventrella, Linden Lab
+// (latest update to this info: Jan 18, 2007)
+//
+// The Voice Visualizer is responsible for taking realtime signals from actual users speaking and
+// visualizing this speech in two forms:
+//
+// (1) as a dynamic sound symbol (also referred to as the "voice indicator" that appears over the avatar's head
+// (2) as gesticulation events that are used to trigger avatr gestures
+//
+// The input for the voice visualizer is a continual stream of voice amplitudes.
+
+//-----------------------------------------------------------------------------
+#ifndef LL_VOICE_VISUALIZER_H
+#define LL_VOICE_VISUALIZER_H
+
+#include "llhudeffect.h"
+
+//-----------------------------------------------------------------------------------------------
+// The values of voice gesticulation represent energy levels for avatar animation, based on
+// amplitude surge events parsed from the voice signal. These are made available so that
+// the appropriate kind of avatar animation can be triggered, and thereby simulate the physical
+// motion effects of speech. It is recommended that multiple body parts be animated as well as
+// lips, such as head, shoulders, and hands, with large gestures used when the energy level is high.
+//-----------------------------------------------------------------------------------------------
+enum VoiceGesticulationLevel
+{
+ VOICE_GESTICULATION_LEVEL_OFF = -1,
+ VOICE_GESTICULATION_LEVEL_LOW = 0,
+ VOICE_GESTICULATION_LEVEL_MEDIUM,
+ VOICE_GESTICULATION_LEVEL_HIGH,
+ NUM_VOICE_GESTICULATION_LEVELS
+};
+
+const static int NUM_VOICE_SYMBOL_WAVES = 7;
+
+//----------------------------------------------------
+// LLVoiceVisualizer class
+//----------------------------------------------------
+class LLVoiceVisualizer : public LLHUDEffect
+{
+ //---------------------------------------------------
+ // public methods
+ //---------------------------------------------------
+ public:
+ LLVoiceVisualizer ( const U8 type ); //constructor
+ ~LLVoiceVisualizer(); //destructor
+
+ friend class LLHUDObject;
+
+ void setVoiceSourceWorldPosition( const LLVector3 &p ); // this should be the position of the speaking avatar's head
+ void setMinGesticulationAmplitude( F32 ); // the lower range of meaningful amplitude for setting gesticulation level
+ void setMaxGesticulationAmplitude( F32 ); // the upper range of meaningful amplitude for setting gesticulation level
+ void setStartSpeaking(); // tell me when the av starts speaking
+ void setVoiceEnabled( bool ); // tell me whether or not the user is voice enabled
+ void setSpeakingAmplitude( F32 ); // tell me how loud the av is speaking (ranges from 0 to 1)
+ void setStopSpeaking(); // tell me when the av stops speaking
+ bool getCurrentlySpeaking(); // the get for the above set
+ VoiceGesticulationLevel getCurrentGesticulationLevel(); // based on voice amplitude, I'll give you the current "energy level" of avatar speech
+
+ void render(); // inherited from HUD Effect
+ void packData(LLMessageSystem *mesgsys); // inherited from HUD Effect
+ void unpackData(LLMessageSystem *mesgsys, S32 blocknum); // inherited from HUD Effect
+ void markDead(); // inherited from HUD Effect
+
+ //----------------------------------------------------------------------------------------------
+ // "setMaxGesticulationAmplitude" and "setMinGesticulationAmplitude" allow for the tuning of the
+ // gesticulation level detector to be responsive to different kinds of signals. For instance, we
+ // may find that the average voice amplitude rarely exceeds 0.7 (in a range from 0 to 1), and
+ // therefore we may want to set 0.7 as the max, so we can more easily catch all the variance
+ // within that range. Also, we may find that there is often noise below a certain range like 0.1,
+ // and so we would want to set 0.1 as the min so as not to accidentally use this as signal.
+ //----------------------------------------------------------------------------------------------
+ void setMaxGesticulationAmplitude();
+ void setMinGesticulationAmplitude();
+
+ //---------------------------------------------------
+ // private members
+ //---------------------------------------------------
+ private:
+
+ struct SoundSymbol
+ {
+ F32 mWaveExpansion [ NUM_VOICE_SYMBOL_WAVES ];
+ bool mWaveActive [ NUM_VOICE_SYMBOL_WAVES ];
+ F64 mWaveFadeOutStartTime [ NUM_VOICE_SYMBOL_WAVES ];
+ F32 mWaveOpacity [ NUM_VOICE_SYMBOL_WAVES ];
+ LLPointer<LLImageGL> mTexture [ NUM_VOICE_SYMBOL_WAVES ];
+ bool mActive;
+ LLVector3 mPosition;
+ };
+
+ LLFrameTimer mTimer; // so I can ask the current time in seconds
+ F64 mCurrentTime; // current time in seconds, captured every step
+ F64 mPreviousTime; // copy of "current time" from last frame
+ SoundSymbol mSoundSymbol; // the sound symbol that appears over the avatar's head
+ bool mVoiceEnabled; // if off, no rendering should happen
+ bool mCurrentlySpeaking; // is the user currently speaking?
+ LLVector3 mVoiceSourceWorldPosition; // give this to me every step - I need it to update the sound symbol
+ F32 mSpeakingAmplitude; // this should be set as often as possible when the user is speaking
+ F32 mMaxGesticulationAmplitude; // this is the upper-limit of the envelope of detectable gesticulation leves
+ F32 mMinGesticulationAmplitude; // this is the lower-limit of the envelope of detectable gesticulation leves
+
+};//-----------------------------------------------------------------
+ // end of LLVoiceVisualizer class
+//------------------------------------------------------------------
+
+#endif //LL_VOICE_VISUALIZER_H
+
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index a126237e00..57b046c082 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -780,6 +780,8 @@ BOOL LLVOVolume::updateLOD()
mLODChanged = TRUE;
}
+ lod_changed |= LLViewerObject::updateLOD();
+
return lod_changed;
}
@@ -1108,7 +1110,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
mSculptChanged = FALSE;
mFaceMappingChanged = FALSE;
- return TRUE;
+ return LLViewerObject::updateGeometry(drawable);
}
void LLVOVolume::updateFaceSize(S32 idx)
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index 6c99d99732..ab95b9eda6 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -292,8 +292,7 @@ void LLWorldMapView::draw()
const S32 half_height = height / 2;
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
- LLGLEnable scissor_test(GL_SCISSOR_TEST);
- LLUI::setScissorRegionLocal(LLRect(0, height, width, 0));
+ LLLocalClipRect clip(getLocalRect());
{
LLGLSNoTexture no_texture;
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index c93b302312..9136a06c24 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -69,25 +69,42 @@ class ViewerManifest(LLManifest):
self.path("lsl_guide.html")
self.path("gpu_table.txt")
+ def channel_unique(self):
+ return self.args['channel'].replace("Second Life", "").strip()
+ def channel_oneword(self):
+ return "".join(self.channel_unique().split())
+ def channel_lowerword(self):
+ return self.channel_oneword().lower()
+
def flags_list(self):
""" Convenience function that returns the command-line flags for the grid"""
- if(self.args['grid'] == ''):
- return ""
- elif(self.args['grid'] == 'firstlook'):
- return '-settings settings_firstlook.xml'
- else:
- return ("-settings settings_beta.xml --%(grid)s -helperuri http://preview-%(grid)s.secondlife.com/helpers/" % {'grid':self.args['grid']})
+ channel_flags = ''
+ grid_flags = ''
+ if not self.default_grid():
+ if self.default_channel():
+ # beta grid viewer
+ channel_flags = '-settings settings_beta.xml'
+ grid_flags = "--%(grid)s -helperuri http://preview-%(grid)s.secondlife.com/helpers/" % {'grid':self.args['grid']}
+
+ if not self.default_channel():
+ # some channel on some grid
+ channel_flags = '-settings settings_%s.xml -channel "%s"' % (self.channel_lowerword(), self.args['channel'])
+ return " ".join((grid_flags, channel_flags)).strip()
def login_url(self):
""" Convenience function that returns the appropriate login url for the grid"""
if(self.args.get('login_url')):
return self.args['login_url']
else:
- if(self.args['grid'] == ''):
- return 'http://secondlife.com/app/login/'
- elif(self.args['grid'] == 'firstlook'):
- return 'http://secondlife.com/app/login/firstlook/'
+ if(self.default_grid()):
+ if(self.default_channel()):
+ # agni release
+ return 'http://secondlife.com/app/login/'
+ else:
+ # first look (or other) on agni
+ return 'http://secondlife.com/app/login/%s/' % self.channel_lowerword()
else:
+ # beta grid
return 'http://secondlife.com/app/login/beta/'
def replace_login_url(self):
@@ -97,14 +114,14 @@ class ViewerManifest(LLManifest):
class WindowsManifest(ViewerManifest):
def final_exe(self):
- # *NOTE: these are the only two executable names that the crash reporter recognizes
- if self.args['grid'] == '':
- return "SecondLife.exe"
- elif self.args['grid'] == 'firstlook':
- return "SecondLifeFirstLook.exe"
+ if self.default_channel():
+ if self.default_grid():
+ return "SecondLife.exe"
+ else:
+ return "SecondLifePreview.exe"
else:
- return "SecondLifePreview.exe"
- # return "SecondLifePreview%s.exe" % (self.args['grid'], )
+ return ''.join(self.args['channel'].split()) + '.exe'
+
def construct(self):
super(WindowsManifest, self).construct()
@@ -154,6 +171,20 @@ class WindowsManifest(ViewerManifest):
self.path("res/*/*")
self.end_prefix()
+ # Vivox runtimes
+ if self.prefix(src="vivox-runtime/i686-win32", dst=""):
+ self.path("SLVoice.exe")
+ self.path("SLVoiceAgent.exe")
+ self.path("libeay32.dll")
+ self.path("srtp.dll")
+ self.path("ssleay32.dll")
+ self.path("tntk.dll")
+ self.path("alut.dll")
+ self.path("vivoxsdk.dll")
+ self.path("ortp.dll")
+ self.path("wrap_oal.dll")
+ self.end_prefix()
+
# pull in the crash logger and updater from other projects
self.path(src="../win_crash_logger/win_crash_logger.exe", dst="win_crash_logger.exe")
self.path(src="../win_updater/updater.exe", dst="updater.exe")
@@ -203,49 +234,75 @@ class WindowsManifest(ViewerManifest):
return result
def package_finish(self):
- version_vars_template = """
+ # a standard map of strings for replacing in the templates
+ substitution_strings = {
+ 'version' : '.'.join(self.args['version']),
+ 'version_short' : '.'.join(self.args['version'][:-1]),
+ 'version_dashes' : '-'.join(self.args['version']),
+ 'final_exe' : self.final_exe(),
+ 'grid':self.args['grid'],
+ 'grid_caps':self.args['grid'].upper(),
+ # escape quotes becase NSIS doesn't handle them well
+ 'flags':self.flags_list().replace('"', '$\\"'),
+ 'channel':self.args['channel'],
+ 'channel_oneword':self.channel_oneword(),
+ 'channel_unique':self.channel_unique(),
+ }
+
+ version_vars = """
!define INSTEXE "%(final_exe)s"
!define VERSION "%(version_short)s"
!define VERSION_LONG "%(version)s"
!define VERSION_DASHES "%(version_dashes)s"
- """
- if(self.args['grid'] == ''):
- installer_file = "Second Life %(version_dashes)s Setup.exe"
- grid_vars_template = """
- OutFile "%(outfile)s"
- !define INSTFLAGS "%(flags)s"
- !define INSTNAME "SecondLife"
- !define SHORTCUT "Second Life"
- !define URLNAME "secondlife"
- Caption "Second Life ${VERSION}"
- """
+ """ % substitution_strings
+ if self.default_channel():
+ if self.default_grid():
+ # release viewer
+ installer_file = "Second Life %(version_dashes)s Setup.exe"
+ grid_vars_template = """
+ OutFile "%(installer_file)s"
+ !define INSTFLAGS "%(flags)s"
+ !define INSTNAME "SecondLife"
+ !define SHORTCUT "Second Life"
+ !define URLNAME "secondlife"
+ Caption "Second Life ${VERSION}"
+ """
+ else:
+ # beta grid viewer
+ installer_file = "Second Life %(version_dashes)s (%(grid_caps)s) Setup.exe"
+ grid_vars_template = """
+ OutFile "%(installer_file)s"
+ !define INSTFLAGS "%(flags)s"
+ !define INSTNAME "SecondLife%(grid_caps)s"
+ !define SHORTCUT "Second Life (%(grid_caps)s)"
+ !define URLNAME "secondlife%(grid)s"
+ !define UNINSTALL_SETTINGS 1
+ Caption "Second Life %(grid)s ${VERSION}"
+ """
else:
- installer_file = "Second Life %(version_dashes)s (%(grid_caps)s) Setup.exe"
+ # some other channel on some grid
+ installer_file = "Second Life %(version_dashes)s %(channel_unique)s Setup.exe"
grid_vars_template = """
- OutFile "%(outfile)s"
+ OutFile "%(installer_file)s"
!define INSTFLAGS "%(flags)s"
- !define INSTNAME "SecondLife%(grid_caps)s"
- !define SHORTCUT "Second Life (%(grid_caps)s)"
- !define URLNAME "secondlife%(grid)s"
+ !define INSTNAME "SecondLife%(channel_oneword)s"
+ !define SHORTCUT "%(channel)s"
+ !define URLNAME "secondlife"
!define UNINSTALL_SETTINGS 1
- Caption "Second Life %(grid)s ${VERSION}"
+ Caption "%(channel)s ${VERSION}"
"""
if(self.args.has_key('installer_name')):
installer_file = self.args['installer_name']
else:
- installer_file = installer_file % {'version_dashes' : '-'.join(self.args['version']),
- 'grid_caps' : self.args['grid'].upper()}
- tempfile = "../secondlife_setup.nsi"
- # the following is an odd sort of double-string replacement
+ installer_file = installer_file % substitution_strings
+ substitution_strings['installer_file'] = installer_file
+
+ tempfile = "../secondlife_setup_tmp.nsi"
+ # the following replaces strings in the nsi template
+ # it also does python-style % substitution
self.replace_in("installers/windows/installer_template.nsi", tempfile, {
- "%%VERSION%%":version_vars_template%{'version_short' : '.'.join(self.args['version'][:-1]),
- 'version' : '.'.join(self.args['version']),
- 'version_dashes' : '-'.join(self.args['version']),
- 'final_exe' : self.final_exe()},
- "%%GRID_VARS%%":grid_vars_template%{'grid':self.args['grid'],
- 'grid_caps':self.args['grid'].upper(),
- 'outfile':installer_file,
- 'flags':self.flags_list()},
+ "%%VERSION%%":version_vars,
+ "%%GRID_VARS%%":grid_vars_template % substitution_strings,
"%%INSTALL_FILES%%":self.nsi_file_commands(True),
"%%DELETE_FILES%%":self.nsi_file_commands(False)})
@@ -307,16 +364,26 @@ class DarwinManifest(ViewerManifest):
def package_finish(self):
+ channel_standin = 'Second Life' # hah, our default channel is not usable on its own
+ if not self.default_channel():
+ channel_standin = self.args['channel']
+
imagename="SecondLife_" + '_'.join(self.args['version'])
- if(self.args['grid'] != ''):
- imagename = imagename + '_' + self.args['grid'].upper()
+ if self.default_channel():
+ if not self.default_grid():
+ # beta case
+ imagename = imagename + '_' + self.args['grid'].upper()
+ else:
+ # first look, etc
+ imagename = imagename + '_' + self.channel_oneword().upper()
sparsename = imagename + ".sparseimage"
finalname = imagename + ".dmg"
# make sure we don't have stale files laying about
self.remove(sparsename, finalname)
- self.run_command('hdiutil create "%(sparse)s" -volname "Second Life" -fs HFS+ -type SPARSE -megabytes 300' % {'sparse':sparsename})
+ self.run_command('hdiutil create "%(sparse)s" -volname "Second Life" -fs HFS+ -type SPARSE -megabytes 300' % {
+ 'sparse':sparsename})
# mount the image and get the name of the mount point and device node
hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"')
@@ -324,15 +391,17 @@ class DarwinManifest(ViewerManifest):
volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip()
# Copy everything in to the mounted .dmg
- # TODO change name of .app once mac_updater can handle it.
- for s,d in {
- self.get_dst_prefix():"Second Life.app",
- "lsl_guide.html":"Linden Scripting Language Guide.html",
- "releasenotes.txt":"Release Notes.txt",
- "installers/darwin/mac_image_hidden":".hidden",
- "installers/darwin/mac_image_background.tga":"background.tga",
- "installers/darwin/mac_image_DS_Store":".DS_Store"}.items():
+ if self.default_channel() and not self.default_grid():
+ app_name = "Second Life " + self.args['grid']
+ else:
+ app_name = channel_standin.strip()
+ for s,d in {self.get_dst_prefix():app_name + ".app",
+ "lsl_guide.html":"Linden Scripting Language Guide.html",
+ "releasenotes.txt":"Release Notes.txt",
+ "installers/darwin/mac_image_hidden":".hidden",
+ "installers/darwin/mac_image_background.tga":"background.tga",
+ "installers/darwin/mac_image_DS_Store":".DS_Store"}.items():
print "Copying to dmg", s, d
self.copy_action(self.src_path_of(s), os.path.join(volpath, d))