diff --git a/etc/message.xml b/etc/message.xml
index e0746acbbdcba3a93766ee48b1f2ead3434f2702..304ba320b197d23067ec2b94eefab5a3a2287cb4 100644
--- a/etc/message.xml
+++ b/etc/message.xml
@@ -334,14 +334,6 @@
 					<boolean>true</boolean>
 				</map>
 
-				<key>ProvisionVoiceAccountRequest</key>
-				<map>
-					<key>flavor</key>
-					<string>llsd</string>
-					<key>trusted-sender</key>
-					<boolean>false</boolean>
-				</map>
-
 				<key>avatarnotesrequest</key>
 				<map>
 					<key>service_name</key>
@@ -381,6 +373,25 @@
 					<key>trusted-sender</key>
 					<boolean>false</boolean>
 				</map>
+				
+				<key>ProvisionVoiceAccountRequest</key>
+				<map>
+					<key>flavor</key>
+					<string>llsd</string>
+					<key>trusted-sender</key>
+					<boolean>false</boolean>
+				</map>
+
+				<!-- Server to client -->
+				<key>RequiredVoiceVersion</key>
+				<map>
+					<key>flavor</key>
+					<string>llsd</string>
+					<key>trusted-sender</key>
+					<boolean>true</boolean>
+				</map>
+
+
 		  </map>
   	  	<key>capBans</key>
     	<map>
diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp
index a53956223c18a084acebe9f606ecb34ee977b5b7..73aefa664026b337227011905d6bf4e149ebb03d 100644
--- a/indra/llcharacter/llmotion.cpp
+++ b/indra/llcharacter/llmotion.cpp
@@ -106,6 +106,11 @@ void LLMotion::setDeactivateCallback( void (*cb)(void *), void* userdata )
 	mDeactivateCallbackUserData = userdata;
 }
 
+BOOL LLMotion::isBlending()
+{
+	return mPose.getWeight() < 1.f;
+}
+
 //-----------------------------------------------------------------------------
 // activate()
 //-----------------------------------------------------------------------------
@@ -122,10 +127,16 @@ void LLMotion::activate()
 void LLMotion::deactivate()
 {
 	mActive = FALSE;
+	mPose.setWeight(0.f);
 
 	if (mDeactivateCallback) (*mDeactivateCallback)(mDeactivateCallbackUserData);
 
 	onDeactivate();
 }
 
+BOOL LLMotion::canDeprecate()
+{
+	return TRUE;
+}
+
 // End
diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h
index 66882a2c111c22fa788fb651bfb4091a974b0a1b..77edba667fde5ad27e593ba1ce463544965edf82 100644
--- a/indra/llcharacter/llmotion.h
+++ b/indra/llcharacter/llmotion.h
@@ -79,13 +79,14 @@ class LLMotion
 
 	void setStopped(BOOL stopped) { mStopped = stopped; }
 
+	BOOL isBlending();
+
 	void activate();
 
 	void deactivate();
 
 	BOOL isActive() { return mActive; }
 
-
 public:
 	//-------------------------------------------------------------------------
 	// animation callbacks to be implemented by subclasses
@@ -125,6 +126,11 @@ class LLMotion
 	// called when a motion is deactivated
 	virtual void onDeactivate() = 0;
 
+	// can we crossfade this motion with a new instance when restarted?
+	// should ultimately always be TRUE, but lack of emote blending, etc
+	// requires this
+	virtual BOOL canDeprecate();
+
 	// optional callback routine called when animation deactivated.
 	void	setDeactivateCallback( void (*cb)(void *), void* userdata );
 
diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp
index 592a6bae2c003f4ea89da5a0f6145e05e1fdbdf7..bef5ea5dbba422572a6098bcbfd746e7c4f7eb77 100644
--- a/indra/llcharacter/llmotioncontroller.cpp
+++ b/indra/llcharacter/llmotioncontroller.cpp
@@ -174,34 +174,44 @@ void LLMotionController::deleteAllMotions()
 //-----------------------------------------------------------------------------
 void LLMotionController::addLoadedMotion(LLMotion* motionp)
 {
+	std::set<LLUUID> motions_to_kill;
+
+	// gather all inactive, loaded motions
 	if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
 	{
 		// too many motions active this frame, kill all blenders
 		mPoseBlender.clearBlenders();
 
-		for (U32 i = 0; i < mLoadedMotions.size(); i++)
+		for (motion_list_t::iterator loaded_motion_it = mLoadedMotions.begin(); 
+			loaded_motion_it != mLoadedMotions.end(); 
+			++loaded_motion_it)
 		{
-			LLMotion* cur_motionp = mLoadedMotions.front();
-			mLoadedMotions.pop_front();
+			LLMotion* cur_motionp = *loaded_motion_it;
 			
 			// motion isn't playing, delete it
 			if (!isMotionActive(cur_motionp))
 			{
-				mCharacter->requestStopMotion(cur_motionp);
-				mAllMotions.erase(cur_motionp->getID());
-				delete cur_motionp;
-				if (mLoadedMotions.size() <= MAX_MOTION_INSTANCES)
-				{
-					break;
-				}
-			}
-			else
-			{
-				// put active motion on back
-				mLoadedMotions.push_back(cur_motionp);
+				motions_to_kill.insert(cur_motionp->getID());
 			}
 		}
 	}
+
+	// clean up all inactive, loaded motions
+	for (std::set<LLUUID>::iterator motion_it = motions_to_kill.begin();
+		motion_it != motions_to_kill.end();
+		++motion_it)
+	{
+		// look up the motion again by ID to get canonical instance
+		// and kill it only if that one is inactive
+		LLUUID motion_id = *motion_it;
+		LLMotion* motionp = findMotion(motion_id);
+		if (motionp && !isMotionActive(motionp))
+		{
+			removeMotion(motion_id);
+		}
+	}
+
+	// add new motion to loaded list
 	mLoadedMotions.push_back(motionp);
 }
 
@@ -260,14 +270,24 @@ BOOL LLMotionController::addMotion( const LLUUID& id, LLMotionConstructor constr
 void LLMotionController::removeMotion( const LLUUID& id)
 {
 	LLMotion* motionp = findMotion(id);
+	
+	removeMotionInstance(motionp);
+
+	mAllMotions.erase(id);
+}
+
+// removes instance of a motion from all runtime structures, but does
+// not erase entry by ID, as this could be a duplicate instance
+// use removeMotion(id) to remove all references to a given motion by id.
+void LLMotionController::removeMotionInstance(LLMotion* motionp)
+{
 	if (motionp)
 	{
-		stopMotionLocally(id, TRUE);
+		stopMotionInstance(motionp, TRUE);
 
 		mLoadingMotions.erase(motionp);
 		mLoadedMotions.remove(motionp);
 		mActiveMotions.remove(motionp);
-		mAllMotions.erase(id);
 		delete motionp;
 	}
 }
@@ -328,28 +348,39 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id )
 //-----------------------------------------------------------------------------
 BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
 {
-	// look for motion in our list of created motions
-	LLMotion *motion = createMotion(id);
+	// do we have an instance of this motion for this character?
+	LLMotion *motion = findMotion(id);
+
+	// motion that is stopping will be allowed to stop but
+	// replaced by a new instance of that motion
+	if (motion
+		&& motion->canDeprecate()
+		&& motion->getFadeWeight() > 0.01f // not LOD-ed out
+		&& (motion->isBlending() || motion->getStopTime() != 0.f))
+	{
+		deprecateMotionInstance(motion);
+		// force creation of new instance
+		motion = NULL;
+	}
+
+	// create new motion instance
+	if (!motion)
+	{
+		motion = createMotion(id);
+	}
 
 	if (!motion)
 	{
 		return FALSE;
 	}
-	//if the motion is already active, then we're done
-	else if (isMotionActive(motion)) // motion is playing and...
+	//if the motion is already active and allows deprecation, then let it keep playing
+	else if (motion->canDeprecate() && isMotionActive(motion))
 	{	
-		if (motion->isStopped()) // motion has been stopped
-		{
-			deactivateMotion(motion, false);
-		}
-		else if (mTime < motion->mSendStopTimestamp)	// motion is still active
-		{
-			return TRUE;
-		}
+		return TRUE;
 	}
 
 //	llinfos << "Starting motion " << name << llendl;
-	return activateMotion(motion, mTime - start_offset);
+	return activateMotionInstance(motion, mTime - start_offset);
 }
 
 
@@ -360,6 +391,12 @@ BOOL LLMotionController::stopMotionLocally(const LLUUID &id, BOOL stop_immediate
 {
 	// if already inactive, return false
 	LLMotion *motion = findMotion(id);
+
+	return stopMotionInstance(motion, stop_immediate);
+}
+
+BOOL LLMotionController::stopMotionInstance(LLMotion* motion, BOOL stop_immediate)
+{
 	if (!motion)
 	{
 		return FALSE;
@@ -376,7 +413,7 @@ BOOL LLMotionController::stopMotionLocally(const LLUUID &id, BOOL stop_immediate
 
 		if (stop_immediate)
 		{
-			deactivateMotion(motion, false);
+			deactivateMotionInstance(motion);
 		}
 		return TRUE;
 	}
@@ -472,7 +509,7 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
 		{
 			if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
 			{
-				deactivateMotion(motionp, false);
+				deactivateMotionInstance(motionp);
 			}
 			else if (motionp->isStopped() && mTime > motionp->getStopTime())
 			{
@@ -490,7 +527,7 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
 				if (mLastTime <= motionp->mSendStopTimestamp)
 				{
 					mCharacter->requestStopMotion( motionp );
-					stopMotionLocally(motionp->getID(), FALSE);
+					stopMotionInstance(motionp, FALSE);
 				}
 			}
 			else if (mTime >= motionp->mActivationTimestamp)
@@ -518,7 +555,7 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
 				if (mLastTime <= motionp->mSendStopTimestamp)
 				{
 					mCharacter->requestStopMotion( motionp );
-					stopMotionLocally(motionp->getID(), FALSE);
+					stopMotionInstance(motionp, FALSE);
 				}
 			}
 
@@ -526,7 +563,8 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
 			{
 				if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
 				{
-					deactivateMotion(motionp, true);
+					posep->setWeight(0.f);
+					deactivateMotionInstance(motionp);
 				}
 				continue;
 			}
@@ -552,7 +590,8 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
 			}
 			else
 			{
-				deactivateMotion(motionp, true);
+				posep->setWeight(0.f);
+				deactivateMotionInstance(motionp);
 				continue;
 			}
 		}
@@ -597,7 +636,7 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
 				if (mLastTime <= motionp->mSendStopTimestamp)
 				{
 					mCharacter->requestStopMotion( motionp );
-					stopMotionLocally(motionp->getID(), FALSE);
+					stopMotionInstance(motionp, FALSE);
 				}
 			}
 
@@ -641,7 +680,7 @@ void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_ty
 				// propagate this to the network
 				// as not all viewers are guaranteed to have access to the same logic
 				mCharacter->requestStopMotion( motionp );
-				stopMotionLocally(motionp->getID(), FALSE);
+				stopMotionInstance(motionp, FALSE);
 			}
 
 		}
@@ -713,7 +752,7 @@ void LLMotionController::updateMotion()
 			// this motion should be playing
 			if (!motionp->isStopped())
 			{
-				activateMotion(motionp, mTime);
+				activateMotionInstance(motionp, mTime);
 			}
 		}
 		else if (status == LLMotion::STATUS_FAILURE)
@@ -721,6 +760,7 @@ void LLMotionController::updateMotion()
 			llinfos << "Motion " << motionp->getID() << " init failed." << llendl;
 			sRegistry.markBad(motionp->getID());
 			mLoadingMotions.erase(curiter);
+
 			mAllMotions.erase(motionp->getID());
 			delete motionp;
 		}
@@ -753,9 +793,9 @@ void LLMotionController::updateMotion()
 
 
 //-----------------------------------------------------------------------------
-// activateMotion()
+// activateMotionInstance()
 //-----------------------------------------------------------------------------
-BOOL LLMotionController::activateMotion(LLMotion *motion, F32 time)
+BOOL LLMotionController::activateMotionInstance(LLMotion *motion, F32 time)
 {
 	if (mLoadingMotions.find(motion) != mLoadingMotions.end())
 	{
@@ -798,23 +838,38 @@ BOOL LLMotionController::activateMotion(LLMotion *motion, F32 time)
 }
 
 //-----------------------------------------------------------------------------
-// deactivateMotion()
+// deactivateMotionInstance()
 //-----------------------------------------------------------------------------
-BOOL LLMotionController::deactivateMotion(LLMotion *motion, bool remove_weight)
+BOOL LLMotionController::deactivateMotionInstance(LLMotion *motion)
 {
-	if( remove_weight )
+	motion->deactivate();
+
+	motion_set_t::iterator found_it = mDeprecatedMotions.find(motion);
+	if (found_it != mDeprecatedMotions.end())
 	{
-		// immediately remove pose weighting instead of letting it time out
-		LLPose *posep = motion->getPose();
-		posep->setWeight(0.f);
+		// deprecated motions need to be completely excised
+		removeMotionInstance(motion);	
+		mDeprecatedMotions.erase(found_it);
+	}
+	else
+	{
+		// for motions that we are keeping, simply remove from active queue
+		mActiveMotions.remove(motion);
 	}
-	
-	motion->deactivate();
-	mActiveMotions.remove(motion);
 
 	return TRUE;
 }
 
+void LLMotionController::deprecateMotionInstance(LLMotion* motion)
+{
+	mDeprecatedMotions.insert(motion);
+
+	//fade out deprecated motion
+	stopMotionInstance(motion, FALSE);
+	//no longer canonical
+	mAllMotions.erase(motion->getID());
+}
+
 //-----------------------------------------------------------------------------
 // isMotionActive()
 //-----------------------------------------------------------------------------
@@ -837,7 +892,7 @@ bool LLMotionController::isMotionLoading(LLMotion* motion)
 //-----------------------------------------------------------------------------
 LLMotion *LLMotionController::findMotion(const LLUUID& id)
 {
-	return mAllMotions[id];
+	return get_if_there<LLUUID, LLMotion*>(mAllMotions, id, NULL);
 }
 
 //-----------------------------------------------------------------------------
diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h
index 19bab09a11f729203c00c9ba9c1479a4182da94c..d5ea568479a3071b7385061b17ab788acd838f75 100644
--- a/indra/llcharacter/llmotioncontroller.h
+++ b/indra/llcharacter/llmotioncontroller.h
@@ -159,16 +159,21 @@ class LLMotionController
 	LLMotion *findMotion( const LLUUID& id );
 
 protected:
+	// internal operations act on motion instances directly
+	// as there can be duplicate motions per id during blending overlap
 	void deleteAllMotions();
 	void addLoadedMotion(LLMotion *motion);
-	BOOL activateMotion(LLMotion *motion, F32 time);
-	BOOL deactivateMotion(LLMotion *motion, bool remove_weight);
+	BOOL activateMotionInstance(LLMotion *motion, F32 time);
+	BOOL deactivateMotionInstance(LLMotion *motion);
+	void deprecateMotionInstance(LLMotion* motion);
+	BOOL stopMotionInstance(LLMotion *motion, BOOL stop_imemdiate);
+	void removeMotionInstance(LLMotion* motion);
 	void updateRegularMotions();
 	void updateAdditiveMotions();
 	void resetJointSignatures();
 	void updateMotionsByType(LLMotion::LLMotionBlendType motion_type);
-protected:
 
+protected:
 	F32					mTimeFactor;
 	static LLMotionRegistry	sRegistry;
 	LLPoseBlender		mPoseBlender;
@@ -183,11 +188,13 @@ class LLMotionController
 //	Once an animations is loaded, it will be initialized and put on the mLoadedMotions deque.
 //	Any animation that is currently playing also sits in the mActiveMotions list.
 
-	std::map<LLUUID, LLMotion*>	mAllMotions;
+	typedef std::map<LLUUID, LLMotion*> motion_map_t;
+	motion_map_t	mAllMotions;
 
 	motion_set_t		mLoadingMotions;
 	motion_list_t		mLoadedMotions;
 	motion_list_t		mActiveMotions;
+	motion_set_t		mDeprecatedMotions;
 	
 	LLFrameTimer		mTimer;
 	F32					mTime;
diff --git a/indra/llcharacter/llpose.cpp b/indra/llcharacter/llpose.cpp
index e1b24d62cbea4e26db7f8f8aaf054a5eb56a7161..1f6d208b84cac10a9d52429f86612f85c1ec580f 100644
--- a/indra/llcharacter/llpose.cpp
+++ b/indra/llcharacter/llpose.cpp
@@ -255,9 +255,9 @@ void LLJointStateBlender::blendJointStates(BOOL apply_now)
 		joint_state_index < JSB_NUM_JOINT_STATES && mJointStates[joint_state_index] != NULL;
 		joint_state_index++)
 	{
-		U32 current_usage = mJointStates[joint_state_index]->getUsage();
-		F32 current_weight = mJointStates[joint_state_index]->getWeight();
 		LLJointState* jsp = mJointStates[joint_state_index];
+		U32 current_usage = jsp->getUsage();
+		F32 current_weight = jsp->getWeight();
 
 		if (current_weight == 0.f)
 		{
@@ -272,17 +272,14 @@ void LLJointStateBlender::blendJointStates(BOOL apply_now)
 
 				// add in pos for this jointstate modulated by weight
 				added_pos += jsp->getPosition() * (new_weight_sum - sum_weights[POS_WEIGHT]);
-				//sum_weights[POS_WEIGHT] = new_weight_sum;
 			}
 
-			// now do scale
 			if(current_usage & LLJointState::SCALE)
 			{
 				F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
 
 				// add in scale for this jointstate modulated by weight
 				added_scale += jsp->getScale() * (new_weight_sum - sum_weights[SCALE_WEIGHT]);
-				//sum_weights[SCALE_WEIGHT] = new_weight_sum;
 			}
 
 			if (current_usage & LLJointState::ROT)
@@ -291,7 +288,6 @@ void LLJointStateBlender::blendJointStates(BOOL apply_now)
 
 				// add in rotation for this jointstate modulated by weight
 				added_rot = nlerp((new_weight_sum - sum_weights[ROT_WEIGHT]), added_rot, jsp->getRotation()) * added_rot;
-				//sum_weights[ROT_WEIGHT] = new_weight_sum;
 			}
 		}
 		else
@@ -306,13 +302,13 @@ void LLJointStateBlender::blendJointStates(BOOL apply_now)
 					F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[POS_WEIGHT]);
 
 					// blend positions from both
-					blended_pos = lerp(mJointStates[joint_state_index]->getPosition(), blended_pos, sum_weights[POS_WEIGHT] / new_weight_sum);
+					blended_pos = lerp(jsp->getPosition(), blended_pos, sum_weights[POS_WEIGHT] / new_weight_sum);
 					sum_weights[POS_WEIGHT] = new_weight_sum;
 				} 
 				else
 				{
 					// copy position from current
-					blended_pos = mJointStates[joint_state_index]->getPosition();
+					blended_pos = jsp->getPosition();
 					sum_weights[POS_WEIGHT] = current_weight;
 				}
 			}
@@ -325,13 +321,13 @@ void LLJointStateBlender::blendJointStates(BOOL apply_now)
 					F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
 
 					// blend scales from both
-					blended_scale = lerp(mJointStates[joint_state_index]->getScale(), blended_scale, sum_weights[SCALE_WEIGHT] / new_weight_sum);
+					blended_scale = lerp(jsp->getScale(), blended_scale, sum_weights[SCALE_WEIGHT] / new_weight_sum);
 					sum_weights[SCALE_WEIGHT] = new_weight_sum;
 				} 
 				else
 				{
 					// copy scale from current
-					blended_scale = mJointStates[joint_state_index]->getScale();
+					blended_scale = jsp->getScale();
 					sum_weights[SCALE_WEIGHT] = current_weight;
 				}
 			}
@@ -344,13 +340,13 @@ void LLJointStateBlender::blendJointStates(BOOL apply_now)
 					F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[ROT_WEIGHT]);
 
 					// blend rotations from both
-					blended_rot = nlerp(sum_weights[ROT_WEIGHT] / new_weight_sum, mJointStates[joint_state_index]->getRotation(), blended_rot);
+					blended_rot = nlerp(sum_weights[ROT_WEIGHT] / new_weight_sum, jsp->getRotation(), blended_rot);
 					sum_weights[ROT_WEIGHT] = new_weight_sum;
 				} 
 				else
 				{
 					// copy rotation from current
-					blended_rot = mJointStates[joint_state_index]->getRotation();
+					blended_rot = jsp->getRotation();
 					sum_weights[ROT_WEIGHT] = current_weight;
 				}
 			}
diff --git a/indra/llcharacter/llpose.h b/indra/llcharacter/llpose.h
index 5d17bd8458a042ca51f3d3217a97140239ed2185..b3cc994960896b15430d8f27e40c7b201ec0b4be 100644
--- a/indra/llcharacter/llpose.h
+++ b/indra/llcharacter/llpose.h
@@ -61,7 +61,7 @@ class LLPose
 	S32 getNumJointStates() const;
 };
 
-const S32 JSB_NUM_JOINT_STATES = 4;
+const S32 JSB_NUM_JOINT_STATES = 6;
 
 class LLJointStateBlender
 {
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
index 993574b528923b1f5eade26039671526f21a6231..f335458f24bb439688bf068ffecca37889f8ecdb 100644
--- a/indra/llcommon/lltimer.cpp
+++ b/indra/llcommon/lltimer.cpp
@@ -492,7 +492,7 @@ void secondsToTimecodeString(F32 current_time, char *tcstring)
 std::list<LLEventTimer*> LLEventTimer::sActiveList;
 
 LLEventTimer::LLEventTimer(F32 period)
-: mTimer()
+: mEventTimer()
 {
 	mPeriod = period;
 	sActiveList.push_back(this);
@@ -508,9 +508,9 @@ void LLEventTimer::updateClass()
 	for (std::list<LLEventTimer*>::iterator iter = sActiveList.begin(); iter != sActiveList.end(); ) 
 	{
 		LLEventTimer* timer = *iter++;
-		F32 et = timer->mTimer.getElapsedTimeF32();
+		F32 et = timer->mEventTimer.getElapsedTimeF32();
 		if (et > timer->mPeriod) {
-			timer->mTimer.reset();
+			timer->mEventTimer.reset();
 			timer->tick();
 		}
 	}
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index 69e786c50a8bc5fc9bc819ee93954fbfd694bc66..575bf9fc0b1e527f244107d7f1ada4dcdbfffbb2 100644
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -131,7 +131,7 @@ class LLEventTimer
 	static void updateClass();
 
 protected:
-	LLTimer mTimer;
+	LLTimer mEventTimer;
 	F32 mPeriod;
 
 private:
diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h
index 92e415155b8fe5bb9c751ede8fd309cca01d539d..12f702a1971899dcbd97003c6b6c8f149f3f90fe 100644
--- a/indra/llmath/llrect.h
+++ b/indra/llmath/llrect.h
@@ -193,47 +193,28 @@ template <class Type> class LLRectBase
 		mBottom = llmin(mBottom, mTop);
 	}
 	
-	friend const LLRectBase& operator|=(LLRectBase &a, const LLRectBase &b)	// Return rect including a & b
+	void unionWith(const LLRectBase &other)
 	{
-		a.mLeft = llmin(a.mLeft, b.mLeft);
-		a.mRight = llmax(a.mRight, b.mRight);
-		a.mBottom = llmin(a.mBottom, b.mBottom);
-		a.mTop = llmax(a.mTop, b.mTop);
-		return a;
+		mLeft = llmin(mLeft, other.mLeft);
+		mRight = llmax(mRight, other.mRight);
+		mBottom = llmin(mBottom, other.mBottom);
+		mTop = llmax(mTop, other.mTop);
 	}
 
-	friend LLRectBase operator|(const LLRectBase &a, const LLRectBase &b)	// Return rect including a & b
+	void intersectWith(const LLRectBase &other)
 	{
-		LLRectBase<Type> result;
-		result.mLeft = llmin(a.mLeft, b.mLeft);
-		result.mRight = llmax(a.mRight, b.mRight);
-		result.mBottom = llmin(a.mBottom, b.mBottom);
-		result.mTop = llmax(a.mTop, b.mTop);
-		return result;
-	}
-
-	friend const LLRectBase& operator&=(LLRectBase &a, const LLRectBase &b)	// set a to rect where a intersects b
-	{
-		a.mLeft = llmax(a.mLeft, b.mLeft);
-		a.mRight = llmin(a.mRight, b.mRight);
-		a.mBottom = llmax(a.mBottom, b.mBottom);
-		a.mTop = llmin(a.mTop, b.mTop);
-		if (a.mLeft > a.mRight)
+		mLeft = llmax(mLeft, other.mLeft);
+		mRight = llmin(mRight, other.mRight);
+		mBottom = llmax(mBottom, other.mBottom);
+		mTop = llmin(mTop, other.mTop);
+		if (mLeft > mRight)
 		{
-			a.mLeft = a.mRight;
+			mLeft = mRight;
 		}
-		if (a.mBottom > a.mTop)
+		if (mBottom > mTop)
 		{
-			a.mBottom = a.mTop;
+			mBottom = mTop;
 		}
-		return a;
-	}
-
-	friend LLRectBase operator&(const LLRectBase &a, const LLRectBase &b)	// Return rect where a intersects b
-	{
-		LLRectBase result = a;
-		result &= b;
-		return result;
 	}
 
 	friend std::ostream &operator<<(std::ostream &s, const LLRectBase &rect)
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index d93748d069e4c1fc37047e7d0d8ac4ef76e2bb7a..bdf1c12b79a38bdb83121aaecfcb6fb806f00b82 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -197,6 +197,9 @@ void LLButton::init(void (*click_callback)(void*), void *callback_data, const LL
 	mHighlightColor = (				LLUI::sColorsGroup->getColor( "ButtonUnselectedFgColor" ) );
 	mUnselectedBgColor = (				LLUI::sColorsGroup->getColor( "ButtonUnselectedBgColor" ) );
 	mSelectedBgColor = (				LLUI::sColorsGroup->getColor( "ButtonSelectedBgColor" ) );
+
+	mImageOverlayAlignment = LLFontGL::HCENTER;
+	mImageOverlayColor = LLColor4::white;
 }
 
 LLButton::~LLButton()
@@ -607,7 +610,7 @@ void LLButton::draw()
 		// draw overlay image
 		if (mImageOverlay.notNull())
 		{
-			const S32 IMG_PAD = 4;
+			const S32 IMG_PAD = 5;
 			// get max width and height (discard level 0)
 			S32 overlay_width = mImageOverlay->getWidth(0);
 			S32 overlay_height = mImageOverlay->getHeight(0);
@@ -628,7 +631,7 @@ void LLButton::draw()
 					overlay_width, 
 					overlay_height, 
 					mImageOverlay,
-					LLColor4::white);
+					mImageOverlayColor);
 				break;
 			case LLFontGL::HCENTER:
 				gl_draw_scaled_image(
@@ -637,7 +640,7 @@ void LLButton::draw()
 					overlay_width, 
 					overlay_height, 
 					mImageOverlay,
-					LLColor4::white);
+					mImageOverlayColor);
 				break;
 			case LLFontGL::RIGHT:
 				gl_draw_scaled_image(
@@ -646,7 +649,7 @@ void LLButton::draw()
 					overlay_width, 
 					overlay_height, 
 					mImageOverlay,
-					LLColor4::white);
+					mImageOverlayColor);
 				break;
 			default:
 				// draw nothing
@@ -866,7 +869,7 @@ void LLButton::setHoverImages( const LLString& image_name, const LLString& selec
 	setImageHoverSelected(selected_name);
 }
 
-void LLButton::setImageOverlay(const LLString &image_name, LLFontGL::HAlign alignment)
+void LLButton::setImageOverlay(const LLString &image_name, LLFontGL::HAlign alignment, const LLColor4& color)
 {
 	if (image_name.empty())
 	{
@@ -877,6 +880,7 @@ void LLButton::setImageOverlay(const LLString &image_name, LLFontGL::HAlign alig
 		LLUUID overlay_image_id = LLUI::findAssetUUIDByName(image_name);
 		mImageOverlay = LLUI::sImageProvider->getUIImageByID(overlay_image_id);
 		mImageOverlayAlignment = alignment;
+		mImageOverlayColor = color;
 	}
 }
 
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index ba77220a7b30cd2fdd02d71c62ac452b45378367..a638b5be49cc53c29799f2bfc3fbe7d58af7ed1f 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -124,7 +124,7 @@ class LLButton
 
 	void			setDisabledSelectedLabelColor( const LLColor4& c )	{ mDisabledSelectedLabelColor = c; }
 
-	void			setImageOverlay(const LLString &image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER);
+	void			setImageOverlay(const LLString &image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
 	LLPointer<LLImageGL> getImageOverlay() { return mImageOverlay; }
 	
 
@@ -190,6 +190,7 @@ class LLButton
 
 	LLPointer<LLImageGL>	mImageOverlay;
 	LLFontGL::HAlign		mImageOverlayAlignment;
+	LLColor4				mImageOverlayColor;
 
 	LLPointer<LLImageGL>	mImageUnselected;
 	LLUIString				mUnselectedLabel;
diff --git a/indra/llui/llctrlselectioninterface.h b/indra/llui/llctrlselectioninterface.h
index 698d609593b2244d255a991f5a3a76dff41bed5e..9df7475a6c45dd06ad9d41efc7a68cc981b084bd 100644
--- a/indra/llui/llctrlselectioninterface.h
+++ b/indra/llui/llctrlselectioninterface.h
@@ -31,6 +31,8 @@ class LLCtrlSelectionInterface
 
 	virtual BOOL	getCanSelect() const = 0;
 
+	virtual S32		getItemCount() const = 0;
+
 	virtual BOOL	selectFirstItem() = 0;
 	virtual BOOL	selectNthItem( S32 index ) = 0;
 
@@ -56,7 +58,6 @@ class LLCtrlListInterface : public LLCtrlSelectionInterface
 public:
 	virtual ~LLCtrlListInterface();
 	
-	virtual S32 getItemCount() const = 0;
 	virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM) = 0;
 	virtual void clearColumns() = 0;
 	virtual void setColumnLabel(const LLString& column, const LLString& label) = 0;
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 22acf461250a6e5664fbdd5985a4a3f6b3b9fd38..17b4dffa3fe56acd59cbb46f3f709e53591d81a9 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -292,28 +292,32 @@ void LLFloater::init(const LLString& title,
 	{
 		// Resize bars (sides)
 		const S32 RESIZE_BAR_THICKNESS = 3;
-		mResizeBar[0] = new LLResizeBar( 
+		mResizeBar[LLResizeBar::LEFT] = new LLResizeBar( 
 			"resizebar_left",
+			this,
 			LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0), 
-			min_width, min_height, LLResizeBar::LEFT );
+			min_width, S32_MAX, LLResizeBar::LEFT );
 		addChild( mResizeBar[0] );
 
-		mResizeBar[1] = new LLResizeBar( 
+		mResizeBar[LLResizeBar::TOP] = new LLResizeBar( 
 			"resizebar_top",
+			this,
 			LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS), 
-			min_width, min_height, LLResizeBar::TOP );
+			min_height, S32_MAX, LLResizeBar::TOP );
 		addChild( mResizeBar[1] );
 
-		mResizeBar[2] = new LLResizeBar( 
+		mResizeBar[LLResizeBar::RIGHT] = new LLResizeBar( 
 			"resizebar_right",
+			this,
 			LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0), 
-			min_width, min_height, LLResizeBar::RIGHT );
+			min_width, S32_MAX, LLResizeBar::RIGHT );
 		addChild( mResizeBar[2] );
 
-		mResizeBar[3] = new LLResizeBar( 
+		mResizeBar[LLResizeBar::BOTTOM] = new LLResizeBar( 
 			"resizebar_bottom",
+			this,
 			LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0), 
-			min_width, min_height, LLResizeBar::BOTTOM );
+			min_height, S32_MAX, LLResizeBar::BOTTOM );
 		addChild( mResizeBar[3] );
 
 
@@ -601,7 +605,14 @@ void LLFloater::setResizeLimits( S32 min_width, S32 min_height )
 	{
 		if( mResizeBar[i] )
 		{
-			mResizeBar[i]->setResizeLimits( min_width, min_height );
+			if (i == LLResizeBar::LEFT || i == LLResizeBar::RIGHT)
+			{
+				mResizeBar[i]->setResizeLimits( min_width, S32_MAX );
+			}
+			else
+			{
+				mResizeBar[i]->setResizeLimits( min_height, S32_MAX );
+			}
 		}
 		if( mResizeHandle[i] )
 		{
@@ -653,6 +664,25 @@ const LLString& LLFloater::getTitle() const
 	return mDragHandle ? mDragHandle->getTitle() : LLString::null;
 }
 
+void LLFloater::setShortTitle( const LLString& short_title )
+{
+	mShortTitle = short_title;
+}
+
+LLString LLFloater::getShortTitle()
+{
+	if (mShortTitle.empty())
+	{
+		return mDragHandle ? mDragHandle->getTitle() : LLString::null;
+	}
+	else
+	{
+		return mShortTitle;
+	}
+}
+
+
+
 BOOL LLFloater::canSnapTo(LLView* other_view)
 {
 	if (NULL == other_view)
@@ -991,12 +1021,22 @@ void LLFloater::setHost(LLMultiFloater* host)
 	}
 }
 
-void LLFloater::moveResizeHandleToFront()
+void LLFloater::moveResizeHandlesToFront()
 {
-	// 0 is the bottom  right
-	if( mResizeHandle[0] )
+	for( S32 i = 0; i < 4; i++ )
 	{
-		sendChildToFront(mResizeHandle[0]);
+		if( mResizeBar[i] )
+		{
+			sendChildToFront(mResizeBar[i]);
+		}
+	}
+
+	for( S32 i = 0; i < 4; i++ )
+	{
+		if( mResizeHandle[i] )
+		{
+			sendChildToFront(mResizeHandle[i]);
+		}
 	}
 }
 
@@ -1193,6 +1233,10 @@ BOOL LLFloater::getEditModeEnabled()
 void LLFloater::show(LLFloater* floaterp)
 {
 	if (floaterp) floaterp->open();
+	if (floaterp->getHost())
+	{
+		floaterp->getHost()->open();
+	}
 }
 
 //static 
@@ -1206,7 +1250,7 @@ BOOL LLFloater::visible(LLFloater* floaterp)
 {
 	if (floaterp) 
 	{
-		return floaterp->isInVisibleChain();
+		return !floaterp->isMinimized() && floaterp->isInVisibleChain();
 	}
 	return FALSE;
 }
@@ -1233,12 +1277,15 @@ void LLFloater::onClickTearOff(void *userdata)
 		// reparent to floater view
 		gFloaterView->addChild(self);
 
-		new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - LLFLOATER_HEADER_SIZE - 5, self->mRect.getWidth(), self->mRect.getHeight());
-
 		self->open();	/* Flawfinder: ignore */
-		self->setRect(new_rect);
+		
+		// only force position for floaters that don't have that data saved
+		if (self->mRectControl.empty())
+		{
+			new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - LLFLOATER_HEADER_SIZE - 5, self->mRect.getWidth(), self->mRect.getHeight());
+			self->setRect(new_rect);
+		}
 		gFloaterView->adjustToFitScreen(self, FALSE);
-		self->setCanDrag(TRUE);
 		// give focus to new window to keep continuity for the user
 		self->setFocus(TRUE);
 	}
@@ -1248,6 +1295,8 @@ void LLFloater::onClickTearOff(void *userdata)
 		if (new_host)
 		{
 			new_host->showFloater(self);
+			// make sure host is visible
+			new_host->open();
 		}
 	}
 }
@@ -1499,26 +1548,30 @@ void	LLFloater::setCanResize(BOOL can_resize)
 		const S32 RESIZE_BAR_THICKNESS = 3;
 		mResizeBar[0] = new LLResizeBar( 
 			"resizebar_left",
+			this,
 			LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0), 
-			mMinWidth, mMinHeight, LLResizeBar::LEFT );
+			mMinWidth, S32_MAX, LLResizeBar::LEFT );
 		addChild( mResizeBar[0] );
 
 		mResizeBar[1] = new LLResizeBar( 
 			"resizebar_top",
+			this,
 			LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS), 
-			mMinWidth, mMinHeight, LLResizeBar::TOP );
+			mMinHeight, S32_MAX, LLResizeBar::TOP );
 		addChild( mResizeBar[1] );
 
 		mResizeBar[2] = new LLResizeBar( 
 			"resizebar_right",
+			this,
 			LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0), 
-			mMinWidth, mMinHeight, LLResizeBar::RIGHT );
+			mMinWidth, S32_MAX, LLResizeBar::RIGHT );
 		addChild( mResizeBar[2] );
 
 		mResizeBar[3] = new LLResizeBar( 
 			"resizebar_bottom",
+			this,
 			LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0), 
-			mMinWidth, mMinHeight, LLResizeBar::BOTTOM );
+			mMinHeight, S32_MAX, LLResizeBar::BOTTOM );
 		addChild( mResizeBar[3] );
 
 
@@ -1855,7 +1908,7 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF
 			sibling->getVisible() && 
 			expanded_base_rect.rectInRect(&sibling->getRect()))
 		{
-			base_rect |= sibling->getRect();
+			base_rect.unionWith(sibling->getRect());
 		}
 	}
 
@@ -2550,18 +2603,22 @@ BOOL LLMultiFloater::closeAllFloaters()
 	return TRUE; //else all tabs were successfully closed...
 }
 
-void LLMultiFloater::growToFit(LLFloater* floaterp, S32 width, S32 height)
+void LLMultiFloater::growToFit(S32 content_width, S32 content_height)
 {
-	floater_data_map_t::iterator found_data_it;
-	found_data_it = mFloaterDataMap.find(floaterp->getHandle());
-	if (found_data_it != mFloaterDataMap.end())
+	S32 new_width = llmax(mRect.getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2);
+	S32 new_height = llmax(mRect.getHeight(), content_height + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
+
+	if (isMinimized())
 	{
-		// store new width and height with this floater so that it will keep its size when detached
-		found_data_it->second.mWidth = width;
-		found_data_it->second.mHeight = height;
+		mPreviousRect.setLeftTopAndSize(mPreviousRect.mLeft, mPreviousRect.mTop, new_width, new_height);
+	}
+	else
+	{
+		S32 old_height = mRect.getHeight();
+		reshape(new_width, new_height);
+		// keep top left corner in same position
+		translate(0, old_height - new_height);
 	}
-
-	resizeToContents();
 }
 
 /**
@@ -2618,12 +2675,18 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater,
 	floaterp->setCanMinimize(FALSE);
 	floaterp->setCanResize(FALSE);
 	floaterp->setCanDrag(FALSE);
+	floaterp->storeRectControl();
+
+	if (mAutoResize)
+	{
+		growToFit(floater_data.mWidth, floater_data.mHeight);
+	}
 
 	//add the panel, add it to proper maps
-	mTabContainer->addTabPanel(floaterp, floaterp->getTitle(), FALSE, onTabSelected, this, 0, FALSE, insertion_point);
+	mTabContainer->addTabPanel(floaterp, floaterp->getShortTitle(), FALSE, onTabSelected, this, 0, FALSE, insertion_point);
 	mFloaterDataMap[floaterp->getHandle()] = floater_data;
 
-	resizeToContents();
+	updateResizeLimits();
 
 	if ( select_added_floater )
 	{
@@ -2673,7 +2736,6 @@ void LLMultiFloater::showFloater(LLFloater* floaterp)
 	{
 		addFloater(floaterp, TRUE);
 	}
-	setVisibleAndFrontmost();
 }
 
 void LLMultiFloater::removeFloater(LLFloater* floaterp)
@@ -2696,9 +2758,11 @@ void LLMultiFloater::removeFloater(LLFloater* floaterp)
 	}
 	mTabContainer->removeTabPanel(floaterp);
 	floaterp->setBackgroundVisible(TRUE);
+	floaterp->setCanDrag(TRUE);
 	floaterp->setHost(NULL);
+	floaterp->applyRectControl();
 
-	resizeToContents();
+	updateResizeLimits();
 
 	tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false);
 }
@@ -2840,18 +2904,8 @@ BOOL LLMultiFloater::postBuild()
 	return FALSE;
 }
 
-void LLMultiFloater::resizeToContents()
+void LLMultiFloater::updateResizeLimits()
 {
-	// we're already in the middle of a reshape, don't interrupt it
-	floater_data_map_t::iterator floater_it;
-	S32 new_width = 0;
-	S32 new_height = 0;
-	for (floater_it = mFloaterDataMap.begin(); floater_it != mFloaterDataMap.end(); ++floater_it)
-	{
-		new_width = llmax(new_width, floater_it->second.mWidth + LLPANEL_BORDER_WIDTH * 2);
-		new_height = llmax(new_height, floater_it->second.mHeight + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
-	}	
-
 	S32 new_min_width = 0;
 	S32 new_min_height = 0;
 	S32 tab_idx;
@@ -2867,21 +2921,23 @@ void LLMultiFloater::resizeToContents()
 	setResizeLimits(new_min_width, new_min_height);
 
 	S32 cur_height = mRect.getHeight();
+	S32 new_width = llmax(mRect.getWidth(), new_min_width);
+	S32 new_height = llmax(mRect.getHeight(), new_min_height);
 
-	if (mAutoResize)
+	if (isMinimized())
 	{
-		reshape(new_width, new_height);
+		mPreviousRect.setLeftTopAndSize(mPreviousRect.mLeft, mPreviousRect.mTop, llmax(mPreviousRect.getWidth(), new_width), llmax(mPreviousRect.getHeight(), new_height));
 	}
 	else
 	{
-		reshape(llmax(new_min_width, mRect.getWidth()), llmax(new_min_height, mRect.getHeight()));
-	}
+		reshape(new_width, new_height);
 
-	// make sure upper left corner doesn't move
-	translate(0, cur_height - mRect.getHeight());
+		// make sure upper left corner doesn't move
+		translate(0, cur_height - mRect.getHeight());
 
-	// Try to keep whole view onscreen, don't allow partial offscreen.
-	gFloaterView->adjustToFitScreen(this, FALSE);
+		// Try to keep whole view onscreen, don't allow partial offscreen.
+		gFloaterView->adjustToFitScreen(this, FALSE);
+	}
 }
 
 // virtual
@@ -2937,6 +2993,7 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor
 {
 	LLString name(getName());
 	LLString title(getTitle());
+	LLString short_title(getShortTitle());
 	LLString rect_control("");
 	BOOL resizable = isResizable();
 	S32 min_width = getMinWidth();
@@ -2948,6 +3005,7 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor
 
 	node->getAttributeString("name", name);
 	node->getAttributeString("title", title);
+	node->getAttributeString("short_title", short_title);
 	node->getAttributeString("rect_control", rect_control);
 	node->getAttributeBOOL("can_resize", resizable);
 	node->getAttributeBOOL("can_minimize", minimizable);
@@ -2974,6 +3032,8 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor
 			minimizable,
 			close_btn);
 
+	setShortTitle(short_title);
+
 	BOOL can_tear_off;
 	if (node->getAttributeBOOL("can_tear_off", can_tear_off))
 	{
@@ -2988,17 +3048,13 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor
 		LLFloater::setFloaterHost((LLMultiFloater*) this);
 	}
 
-	LLXMLNodePtr child;
-	for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
-	{
-		factory->createWidget(this, child);
-	}
+	initChildrenXML(node, factory);
+
 	if (node->hasName("multi_floater"))
 	{
 		LLFloater::setFloaterHost(last_host);
 	}
 
-
 	BOOL result = postBuild();
 
 	if (!result)
@@ -3011,4 +3067,6 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor
 	{
 		this->open();	/* Flawfinder: ignore */
 	}
+
+	moveResizeHandlesToFront();
 }
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 1491f756832a205404a336277e893f199c441d77..942ae34d274f76c78b2e450ef4f3c9bc44870b7b 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -123,8 +123,10 @@ friend class LLFloaterView;
 
 	void			setTitle( const LLString& title );
 	const LLString&		getTitle() const;
+	void			setShortTitle( const LLString& short_title );
+	LLString		getShortTitle();
 	virtual void	setMinimized(BOOL b);
-	void			moveResizeHandleToFront();
+	void			moveResizeHandlesToFront();
 	void			addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE);
 	void			addDependentFloater(LLViewHandle dependent_handle, BOOL reposition = TRUE);
 	LLFloater*              getDependee() { return (LLFloater*)LLFloater::getFloaterByHandle(mDependeeHandle); }
@@ -222,6 +224,7 @@ friend class LLFloaterView;
 	LLRect			mPreviousRect;
 	BOOL			mForeground;
 	LLViewHandle	mDependeeHandle;
+	LLString		mShortTitle;
 
 	BOOL			mFirstLook;			// TRUE if the _next_ time this floater is visible will be the first time in the session that it is visible.
 
@@ -351,12 +354,11 @@ class LLMultiFloater : public LLFloater
 	/*virtual*/ void draw();
 	/*virtual*/ void setVisible(BOOL visible);
 	/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
-
 	/*virtual*/ EWidgetType getWidgetType() const;
 	/*virtual*/ LLString getWidgetTag() const;
 
 	virtual void setCanResize(BOOL can_resize);
-	virtual void growToFit(LLFloater* floaterp, S32 width, S32 height);
+	virtual void growToFit(S32 content_width, S32 content_height);
 	virtual void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainerCommon::eInsertionPoint insertion_point = LLTabContainerCommon::END);
 
 	virtual void showFloater(LLFloater* floaterp);
@@ -378,7 +380,7 @@ class LLMultiFloater : public LLFloater
 	void setTabContainer(LLTabContainerCommon* tab_container) { if (!mTabContainer) mTabContainer = tab_container; }
 	static void onTabSelected(void* userdata, bool);
 
-	virtual void resizeToContents();
+	virtual void updateResizeLimits();
 
 protected:
 	struct LLFloaterData
diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
index da53e3d104908cda78f6f81f039d380b61abca1f..5c8eb8b4af854c107a52213cdadf13fc70c049c0 100644
--- a/indra/llui/llfocusmgr.cpp
+++ b/indra/llui/llfocusmgr.cpp
@@ -132,8 +132,7 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focu
 	
 	if (lock)
 	{
-		mLockedView = mKeyboardFocus; 
-		mKeyboardLockedFocusLostCallback = on_focus_lost; 
+		lockFocus();
 	}
 }
 
@@ -292,6 +291,12 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( LLUICtrl* top_view )
 	}
 }
 
+void LLFocusMgr::lockFocus()
+{
+	mLockedView = mKeyboardFocus; 
+	mKeyboardLockedFocusLostCallback = mKeyboardFocusLostCallback; 
+}
+
 void LLFocusMgr::unlockFocus()
 {
 	mLockedView = NULL; 
diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h
index dc8025d4c0d3cf8e44da703f208b227b7906e35d..e764f5459ce2f404242bf1bbc13450ee62bf3229 100644
--- a/indra/llui/llfocusmgr.h
+++ b/indra/llui/llfocusmgr.h
@@ -61,6 +61,7 @@ class LLFocusMgr
 
 	// All Three
 	void			releaseFocusIfNeeded( LLView* top_view );
+	void			lockFocus();
 	void			unlockFocus();
 	BOOL			focusLocked() { return mLockedView != NULL; }
 
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index d7ccfe1daa91bc8f17f971b697fdfce0479849fa..52fdf3cd5a6b491ee84d11bd6bba4ef679b6e718 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -1391,9 +1391,9 @@ void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
 	}
 }
 
-void LLMenuItemBranchGL::onVisibilityChange( BOOL curVisibilityIn )
+void LLMenuItemBranchGL::onVisibilityChange( BOOL new_visibility )
 {
-	if (curVisibilityIn == FALSE && mBranch->getVisible() && !mBranch->getTornOff())
+	if (new_visibility == FALSE && !mBranch->getTornOff())
 	{
 		mBranch->setVisible(FALSE);
 	}
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index 37feffd4b0f962710e901574d00477c9326402f6..c130214b9968ff9d662f8a7515ce1e95e8375d07 100644
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -31,9 +31,18 @@
 #include "llviewborder.h"
 #include "llbutton.h"
 
+// LLLayoutStack
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llresizebar.h"
+#include "llcriticaldamp.h"
+
 LLPanel::panel_map_t LLPanel::sPanelMap;
 LLPanel::alert_queue_t LLPanel::sAlertQueue;
 
+const S32 RESIZE_BAR_OVERLAP = 1;
+const S32 PANEL_STACK_GAP = RESIZE_BAR_HEIGHT;
+
 void LLPanel::init()
 {
 	// mRectControl
@@ -99,13 +108,16 @@ void LLPanel::addBorder(LLViewBorder::EBevel border_bevel,
 	addChild( mBorder );
 }
 
+void LLPanel::removeBorder()
+{
+	delete mBorder;
+	mBorder = NULL;
+}
+
 
 LLPanel::~LLPanel()
 {
-	if( !mRectControl.empty() )
-	{
-		LLUI::sConfigGroup->setRect( mRectControl, mRect );
-	}
+	storeRectControl();
 	sPanelMap.erase(mViewHandle);
 }
 
@@ -159,44 +171,41 @@ void LLPanel::setCtrlsEnabled( BOOL b )
 
 void LLPanel::draw()
 {
-	if( getVisible() )
+	// draw background
+	if( mBgVisible )
 	{
-		// draw background
-		if( mBgVisible )
-		{
-			//RN: I don't see the point of this
-			S32 left = 0;//LLPANEL_BORDER_WIDTH;
-			S32 top = mRect.getHeight();// - LLPANEL_BORDER_WIDTH;
-			S32 right = mRect.getWidth();// - LLPANEL_BORDER_WIDTH;
-			S32 bottom = 0;//LLPANEL_BORDER_WIDTH;
+		//RN: I don't see the point of this
+		S32 left = 0;//LLPANEL_BORDER_WIDTH;
+		S32 top = mRect.getHeight();// - LLPANEL_BORDER_WIDTH;
+		S32 right = mRect.getWidth();// - LLPANEL_BORDER_WIDTH;
+		S32 bottom = 0;//LLPANEL_BORDER_WIDTH;
 
-			if (mBgOpaque )
-			{
-				gl_rect_2d( left, top, right, bottom, mBgColorOpaque );
-			}
-			else
-			{
-				gl_rect_2d( left, top, right, bottom, mBgColorAlpha );
-			}
+		if (mBgOpaque )
+		{
+			gl_rect_2d( left, top, right, bottom, mBgColorOpaque );
 		}
-
-		if( mDefaultBtn)
+		else
 		{
-			if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
-			{
-				LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
-				BOOL focus_is_child_button = focus_ctrl->getWidgetType() == WIDGET_TYPE_BUTTON && static_cast<LLButton *>(focus_ctrl)->getCommitOnReturn();
-				// only enable default button when current focus is not a return-capturing button
-				mDefaultBtn->setBorderEnabled(!focus_is_child_button);
-			}
-			else
-			{
-				mDefaultBtn->setBorderEnabled(FALSE);
-			}
+			gl_rect_2d( left, top, right, bottom, mBgColorAlpha );
 		}
+	}
 
-		LLView::draw();
+	if( mDefaultBtn)
+	{
+		if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
+		{
+			LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+			BOOL focus_is_child_button = focus_ctrl->getWidgetType() == WIDGET_TYPE_BUTTON && static_cast<LLButton *>(focus_ctrl)->getCommitOnReturn();
+			// only enable default button when current focus is not a return-capturing button
+			mDefaultBtn->setBorderEnabled(!focus_is_child_button);
+		}
+		else
+		{
+			mDefaultBtn->setBorderEnabled(FALSE);
+		}
 	}
+
+	LLView::draw();
 }
 
 void LLPanel::refresh()
@@ -552,7 +561,7 @@ LLXMLNodePtr LLPanel::getXML(bool save_children) const
 	return node;
 }
 
-LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parentp, LLUICtrlFactory *factory)
+LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *factory)
 {
 	LLString name("panel");
 	node->getAttributeString("name", name);
@@ -561,11 +570,21 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parentp, LLUICtrlFactory *fa
 	// Fall back on a default panel, if there was no special factory.
 	if (!panelp)
 	{
-		panelp = new LLPanel("tab panel");
+		LLRect rect;
+		createRect(node, rect, parent, LLRect());
+		panelp = new LLPanel(name, rect);
+		panelp->initPanelXML(node, parent, factory);
+		// preserve panel's width and height, but override the location
+		const LLRect& panelrect = panelp->getRect();
+		S32 w = panelrect.getWidth();
+		S32 h = panelrect.getHeight();
+		rect.setLeftTopAndSize(rect.mLeft, rect.mTop, w, h);
+		panelp->setRect(rect);
+	}
+	else
+	{
+		panelp->initPanelXML(node, parent, factory);
 	}
-
-	panelp->initPanelXML(node, parentp, factory);
-
 	return panelp;
 }
 
@@ -577,11 +596,7 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f
 
 	setPanelParameters(node, parent);
 
-	LLXMLNodePtr child;
-	for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
-	{
-		factory->createWidget(this, child);
-	}
+	initChildrenXML(node, factory);
 
 	LLString xml_filename;
 	node->getAttributeString("filename", xml_filename);
@@ -590,8 +605,16 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f
 
 	if (!xml_filename.empty())
 	{
+		// Preserve postion of embedded panel but allow panel to dictate width/height
+		LLRect rect(getRect());
 		didPost = factory->buildPanel(this, xml_filename, NULL);
-	} else {
+		S32 w = getRect().getWidth();
+		S32 h = getRect().getHeight();
+		rect.setLeftTopAndSize(rect.mLeft, rect.mTop, w, h);
+		setRect(rect);
+	}
+	else
+	{
 		didPost = FALSE;
 	}
 	
@@ -604,10 +627,32 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f
 	return didPost;
 }
 
-void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parentp)
+void LLPanel::initChildrenXML(LLXMLNodePtr node, LLUICtrlFactory* factory)
+{
+	LLXMLNodePtr child;
+	for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+	{
+		// look for string declarations for programmatic text
+		if (child->hasName("string"))
+		{
+			LLString string_name;
+			child->getAttributeString("name", string_name);
+			if (!string_name.empty())
+			{
+				mUIStrings[string_name] = LLUIString(child->getTextContents());
+			}
+		}
+		else
+		{
+			factory->createWidget(this, child);
+		}
+	}
+}
+
+void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parent)
 {
 	/////// Rect, follows, tool_tip, enabled, visible attributes ///////
-	initFromXML(node, parentp);
+	initFromXML(node, parent);
 
 	/////// Border attributes ///////
 	BOOL border = FALSE;
@@ -632,6 +677,10 @@ void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parentp)
 
 		addBorder(bevel_style, border_style, border_thickness);
 	}
+	else
+	{
+		removeBorder();
+	}
 
 	/////// Background attributes ///////
 	BOOL background_visible = FALSE;
@@ -656,6 +705,30 @@ void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parentp)
 	setLabel(label);
 }
 
+LLString LLPanel::getFormattedUIString(const LLString& name, const LLString::format_map_t& args) const
+{
+	ui_string_map_t::const_iterator found_it = mUIStrings.find(name);
+	if (found_it != mUIStrings.end())
+	{
+		// make a copy as format works in place
+		LLUIString formatted_string = found_it->second;
+		formatted_string.setArgList(args);
+		return formatted_string.getString();
+	}
+	return LLString::null;
+}
+
+LLUIString LLPanel::getUIString(const LLString& name) const
+{
+	ui_string_map_t::const_iterator found_it = mUIStrings.find(name);
+	if (found_it != mUIStrings.end())
+	{
+		return found_it->second;
+	}
+	return LLUIString(LLString::null);
+}
+
+
 void LLPanel::childSetVisible(const LLString& id, bool visible)
 {
 	LLView* child = getChildByName(id, true);
@@ -1045,3 +1118,493 @@ void LLPanel::childDisplayNotFound()
 	LLAlertDialog::showXml("FloaterNotFound", args);
 }
 
+void LLPanel::storeRectControl()
+{
+	if( !mRectControl.empty() )
+	{
+		LLUI::sConfigGroup->setRect( mRectControl, mRect );
+	}
+}
+
+
+//
+// LLLayoutStack
+//
+struct LLLayoutStack::LLEmbeddedPanel
+{
+	LLEmbeddedPanel(LLPanel* panelp, eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize) : 
+			mPanel(panelp), 
+			mMinWidth(min_width), 
+			mMinHeight(min_height),
+			mAutoResize(auto_resize),
+			mOrientation(orientation),
+			mVisibleAmt(1.f) // default to fully visible
+	{
+		LLResizeBar::Side side = (orientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM;
+		LLRect resize_bar_rect = panelp->getRect();
+
+		S32 min_dim;
+		if (orientation == HORIZONTAL)
+		{
+			min_dim = mMinHeight;
+		}
+		else
+		{
+			min_dim = mMinWidth;
+		}
+		mResizeBar = new LLResizeBar("resizer", mPanel, LLRect(), min_dim, S32_MAX, side);
+		mResizeBar->setEnableSnapping(FALSE);
+		// panels initialized as hidden should not start out partially visible
+		if (!mPanel->getVisible())
+		{
+			mVisibleAmt = 0.f;
+		}
+	}
+
+	LLPanel* mPanel;
+	S32 mMinWidth;
+	S32 mMinHeight;
+	BOOL mAutoResize;
+	LLResizeBar* mResizeBar;
+	eLayoutOrientation mOrientation;
+	F32 mVisibleAmt;
+};
+
+LLLayoutStack::LLLayoutStack(eLayoutOrientation orientation) : 
+		mOrientation(orientation),
+		mMinWidth(0),
+		mMinHeight(0)
+{
+}
+
+LLLayoutStack::~LLLayoutStack()
+{
+}
+
+void LLLayoutStack::draw()
+{
+	updateLayout();
+	{
+		// clip if outside nominal bounds
+		LLLocalClipRect clip(getLocalRect(), mRect.getWidth() > mMinWidth || mRect.getHeight() > mMinHeight);
+		e_panel_list_t::iterator panel_it;
+		for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+		{
+			LLRect clip_rect = (*panel_it)->mPanel->getRect();
+			// scale clipping rectangle by visible amount
+			if (mOrientation == HORIZONTAL)
+			{
+				clip_rect.mRight = clip_rect.mLeft + llround(clip_rect.getWidth() * (*panel_it)->mVisibleAmt);
+			}
+			else
+			{
+				clip_rect.mBottom = clip_rect.mTop - llround(clip_rect.getHeight() * (*panel_it)->mVisibleAmt);
+			}
+			LLLocalClipRect clip(clip_rect, (*panel_it)->mVisibleAmt < 1.f);
+			// only force drawing invisible children if visible amount is non-zero
+			drawChild((*panel_it)->mPanel, 0, 0, (*panel_it)->mVisibleAmt > 0.f);
+		}
+	}
+}
+
+void LLLayoutStack::removeCtrl(LLUICtrl* ctrl)
+{
+	LLEmbeddedPanel* embedded_panelp = findEmbeddedPanel((LLPanel*)ctrl);
+
+	if (embedded_panelp)
+	{
+		mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp));
+		delete embedded_panelp;
+	}
+
+	calcMinExtents();
+
+	LLView::removeCtrl(ctrl);
+}
+
+void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+	LLView::reshape(width, height, called_from_parent);
+	//updateLayout();
+}
+
+LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const
+{
+	LLXMLNodePtr node = LLView::getXML();
+	return node;
+}
+
+//static 
+LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+	LLString orientation_string("vertical");
+	node->getAttributeString("orientation", orientation_string);
+
+	eLayoutOrientation orientation = VERTICAL;
+
+	if (orientation_string == "horizontal")
+	{
+		orientation = HORIZONTAL;
+	}
+	else if (orientation_string == "vertical")
+	{
+		orientation = VERTICAL;
+	}
+	else
+	{
+		llwarns << "Unknown orientation " << orientation_string << ", using vertical" << llendl;
+	}
+
+	LLLayoutStack* layout_stackp = new LLLayoutStack(orientation);
+
+	layout_stackp->initFromXML(node, parent);
+
+	LLXMLNodePtr child;
+	for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+	{
+		if (child->hasName("layout_panel"))
+		{
+			S32 min_width = 0;
+			S32 min_height = 0;
+			BOOL auto_resize = TRUE;
+
+			child->getAttributeS32("min_width", min_width);
+			child->getAttributeS32("min_height", min_height);
+			child->getAttributeBOOL("auto_resize", auto_resize);
+
+			LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child, layout_stackp, factory);
+			panelp->setFollowsNone();
+			if (panelp)
+			{
+				layout_stackp->addPanel(panelp, min_width, min_height, auto_resize);
+			}
+		}
+	}
+
+	return layout_stackp;
+}
+
+S32 LLLayoutStack::getMinWidth()
+{
+	return mMinWidth;
+}
+
+S32 LLLayoutStack::getMinHeight()
+{
+	return mMinHeight;
+}
+
+void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, S32 index)
+{
+	LLEmbeddedPanel* embedded_panel = new LLEmbeddedPanel(panel, mOrientation, min_width, min_height, auto_resize);
+	
+	mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel);
+	addChild(panel);
+	addChild(embedded_panel->mResizeBar);
+
+	// bring all resize bars to the front so that they are clickable even over the panels
+	// with a bit of overlap
+	for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+	{
+		e_panel_list_t::iterator next_it = panel_it;
+		++next_it;
+
+		LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
+		sendChildToFront(resize_barp);
+		// last resize bar is disabled, since its not between any two panels
+		if ( next_it == mPanels.end() )
+		{
+			resize_barp->setEnabled(FALSE);
+		}
+		else
+		{
+			resize_barp->setEnabled(TRUE);
+		}
+	}
+
+	//updateLayout();
+}
+
+void LLLayoutStack::removePanel(LLPanel* panel)
+{
+	removeChild(panel);
+	//updateLayout();
+}
+
+void LLLayoutStack::updateLayout(BOOL force_resize)
+{
+	calcMinExtents();
+
+	// calculate current extents
+	S32 cur_width = 0;
+	S32 cur_height = 0;
+
+	const F32 ANIM_OPEN_TIME = 0.02f;
+	const F32 ANIM_CLOSE_TIME = 0.02f;
+
+	e_panel_list_t::iterator panel_it;
+	for (panel_it = mPanels.begin(); panel_it != mPanels.end();	++panel_it)
+	{
+		LLPanel* panelp = (*panel_it)->mPanel;
+		if (panelp->getVisible()) 
+		{
+			(*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME));
+			if ((*panel_it)->mVisibleAmt > 0.99f)
+			{
+				(*panel_it)->mVisibleAmt = 1.f;
+			}
+		}
+		else // not visible
+		{
+			(*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME));
+			if ((*panel_it)->mVisibleAmt < 0.001f)
+			{
+				(*panel_it)->mVisibleAmt = 0.f;
+			}
+		}
+		if (mOrientation == HORIZONTAL)
+		{
+ 			// all panels get expanded to max of all the minimum dimensions
+			cur_height = llmax(mMinHeight, panelp->getRect().getHeight());
+        	cur_width += llround(panelp->getRect().getWidth() * (*panel_it)->mVisibleAmt);
+			if (panel_it != mPanels.end())
+			{
+				cur_width += PANEL_STACK_GAP;
+			}
+		}
+		else //VERTICAL
+		{
+            cur_width = llmax(mMinWidth, panelp->getRect().getWidth());
+			cur_height += llround(panelp->getRect().getHeight() * (*panel_it)->mVisibleAmt);
+			if (panel_it != mPanels.end())
+			{
+				cur_height += PANEL_STACK_GAP;
+			}
+		}
+	}
+
+	S32 num_resizable_panels = 0;
+	S32 shrink_headroom_available = 0;
+	S32 shrink_headroom_total = 0;
+	for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+	{
+		// panels that are not fully visible do not count towards shrink headroom
+		if ((*panel_it)->mVisibleAmt < 1.f) 
+			continue;
+		// if currently resizing a panel or the panel is flagged as not automatically resizing
+		// only track total available headroom, but don't use it for automatic resize logic
+		if ((*panel_it)->mResizeBar->hasMouseCapture() || (!(*panel_it)->mAutoResize && !force_resize))
+		{
+			if (mOrientation == HORIZONTAL)
+			{
+				shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth;
+			}
+			else //VERTICAL
+			{
+				shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight;
+			}
+		}
+		else
+		{
+			num_resizable_panels++;
+			if (mOrientation == HORIZONTAL)
+			{
+				shrink_headroom_available += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth;
+				shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth;
+			}
+			else //VERTICAL
+			{
+				shrink_headroom_available += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight;
+				shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight;
+			}
+		}
+	}
+
+	// positive means panels need to grow, negative means shrink
+	S32 pixels_to_distribute;
+	if (mOrientation == HORIZONTAL)
+	{
+		pixels_to_distribute = mRect.getWidth() - cur_width;
+	}
+	else //VERTICAL
+	{
+		pixels_to_distribute = mRect.getHeight() - cur_height;
+	}
+
+	S32 cur_x = 0;
+	S32 cur_y = mRect.getHeight();
+
+	for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+	{
+		LLPanel* panelp = (*panel_it)->mPanel;
+
+		S32 cur_width = panelp->getRect().getWidth();
+		S32 cur_height = panelp->getRect().getHeight();
+		S32 new_width = llmax((*panel_it)->mMinWidth, cur_width);
+		S32 new_height = llmax((*panel_it)->mMinHeight, cur_height);
+
+		S32 delta_size = 0;
+
+		// if panel can automatically resize (not animating, and resize flag set)...
+		if ((*panel_it)->mVisibleAmt == 1.f && (force_resize || (*panel_it)->mAutoResize) && !(*panel_it)->mResizeBar->hasMouseCapture()) 
+		{
+			if (mOrientation == HORIZONTAL)
+			{
+				// if we're shrinking
+				if (pixels_to_distribute < 0)
+				{
+					// shrink proportionally to amount over minimum
+					delta_size = llround((F32)pixels_to_distribute * (F32)(cur_width - (*panel_it)->mMinWidth) / (F32)shrink_headroom_available);
+				}
+				else
+				{
+					// grow all elements equally
+					delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels);
+				}
+				new_width = llmax((*panel_it)->mMinWidth, panelp->getRect().getWidth() + delta_size);
+			}
+			else
+			{
+				new_width = llmax(mMinWidth, mRect.getWidth());
+			}
+
+			if (mOrientation == VERTICAL)
+			{
+				if (pixels_to_distribute < 0)
+				{
+					// shrink proportionally to amount over minimum
+					delta_size = llround((F32)pixels_to_distribute * (F32)(cur_height - (*panel_it)->mMinHeight) / (F32)shrink_headroom_available);
+				}
+				else
+				{
+					delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels);
+				}
+				new_height = llmax((*panel_it)->mMinHeight, panelp->getRect().getHeight() + delta_size);
+			}
+			else
+			{
+				new_height = llmax(mMinHeight, mRect.getHeight());
+			}
+		}
+		else // don't resize
+		{
+			if (mOrientation == HORIZONTAL)
+			{
+				new_height = llmax(mMinHeight, mRect.getHeight());
+			}
+			else // VERTICAL
+			{
+				new_width = llmax(mMinWidth, mRect.getWidth());
+			}
+		}
+
+		// adjust running headroom count based on new sizes
+		shrink_headroom_total += delta_size;
+
+		panelp->reshape(new_width, new_height);
+		panelp->setOrigin(cur_x, cur_y - new_height);
+
+		LLRect panel_rect = panelp->getRect();
+		LLRect resize_bar_rect = panel_rect;
+		if (mOrientation == HORIZONTAL)
+		{
+			resize_bar_rect.mLeft = panel_rect.mRight - RESIZE_BAR_OVERLAP;
+			resize_bar_rect.mRight = panel_rect.mRight + PANEL_STACK_GAP + RESIZE_BAR_OVERLAP;
+		}
+		else
+		{
+			resize_bar_rect.mTop = panel_rect.mBottom + RESIZE_BAR_OVERLAP;
+			resize_bar_rect.mBottom = panel_rect.mBottom - PANEL_STACK_GAP - RESIZE_BAR_OVERLAP;
+		}
+		(*panel_it)->mResizeBar->setRect(resize_bar_rect);
+
+		if (mOrientation == HORIZONTAL)
+		{
+			cur_x += llround(new_width * (*panel_it)->mVisibleAmt) + PANEL_STACK_GAP;
+		}
+		else //VERTICAL
+		{
+			cur_y -= llround(new_height * (*panel_it)->mVisibleAmt) + PANEL_STACK_GAP;
+		}
+	}
+
+	// update resize bars with new limits
+	LLResizeBar* last_resize_bar = NULL;
+	for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+	{
+		LLPanel* panelp = (*panel_it)->mPanel;
+
+		if (mOrientation == HORIZONTAL)
+		{
+			(*panel_it)->mResizeBar->setResizeLimits((*panel_it)->mMinWidth, (*panel_it)->mMinWidth + shrink_headroom_total);
+		}
+		else //VERTICAL
+		{
+			(*panel_it)->mResizeBar->setResizeLimits((*panel_it)->mMinHeight, (*panel_it)->mMinHeight + shrink_headroom_total);
+		}
+		// hide resize bars for invisible panels
+		(*panel_it)->mResizeBar->setVisible(panelp->getVisible());
+		if (panelp->getVisible())
+		{
+			last_resize_bar = (*panel_it)->mResizeBar;
+		}
+	}
+
+	// hide last resize bar as there is nothing past it
+	if (last_resize_bar)
+	{
+		last_resize_bar->setVisible(FALSE);
+	}
+
+	// not enough room to fit existing contents
+	if (!force_resize && 
+		((cur_y != -PANEL_STACK_GAP) || (cur_x != mRect.getWidth() + PANEL_STACK_GAP)))
+	{
+		// do another layout pass with all stacked elements contributing
+		// even those that don't usually resize
+		llassert_always(force_resize == FALSE);
+		updateLayout(TRUE);
+	}
+}
+
+LLLayoutStack::LLEmbeddedPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp)
+{
+	e_panel_list_t::iterator panel_it;
+	for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+	{
+		if ((*panel_it)->mPanel == panelp)
+		{
+			return *panel_it;
+		}
+	}
+	return NULL;
+}
+
+void LLLayoutStack::calcMinExtents()
+{
+	mMinWidth = 0;
+	mMinHeight = 0;
+
+	e_panel_list_t::iterator panel_it;
+	for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+	{
+		if (mOrientation == HORIZONTAL)
+		{
+			mMinHeight = llmax(mMinHeight, (*panel_it)->mMinHeight);
+            mMinWidth += (*panel_it)->mMinWidth;
+			if (panel_it != mPanels.begin())
+			{
+				mMinWidth += PANEL_STACK_GAP;
+			}
+		}
+		else //VERTICAL
+		{
+            mMinWidth = llmax(mMinWidth, (*panel_it)->mMinWidth);
+			mMinHeight += (*panel_it)->mMinHeight;
+			if (panel_it != mPanels.begin())
+			{
+				mMinHeight += PANEL_STACK_GAP;
+			}
+		}
+	}
+}
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index f06797a7f861e65b4ce24651f3ff52bdccd73982..aa5f6e314fa8efecf6416647d6437bf58f11685c 100644
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -15,6 +15,7 @@
 #include "llcallbackmap.h"
 #include "lluictrl.h"
 #include "llviewborder.h"
+#include "lluistring.h"
 #include "v4color.h"
 #include <list>
 #include <queue>
@@ -71,6 +72,8 @@ class LLPanel : public LLUICtrl
 					LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE,
 					S32 border_thickness = LLPANEL_BORDER_WIDTH );
 
+	void removeBorder();
+
 	virtual ~LLPanel();
 	virtual void	draw();	
 	virtual void 	refresh();	// called in setFocus()
@@ -97,6 +100,7 @@ class LLPanel : public LLUICtrl
 	LLString		getLabel() const { return mLabel; }
 	
 	void            setRectControl(const LLString& rect_control) { mRectControl.assign(rect_control); }
+	void			storeRectControl();
 	
 	void			setBorderVisible( BOOL b );
 
@@ -116,8 +120,12 @@ class LLPanel : public LLUICtrl
 	virtual LLXMLNodePtr getXML(bool save_children = true) const;
 	static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
 	BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+	void initChildrenXML(LLXMLNodePtr node, LLUICtrlFactory* factory);
 	void setPanelParameters(LLXMLNodePtr node, LLView *parentp);
 
+	LLString getFormattedUIString(const LLString& name, const LLString::format_map_t& args = LLUIString::sNullArgs) const;
+	LLUIString getUIString(const LLString& name) const;
+
 	// ** Wrappers for setting child properties by name ** -TomY
 
 	// Override to set not found list
@@ -196,6 +204,8 @@ class LLPanel : public LLUICtrl
 	typedef std::queue<LLAlertInfo> alert_queue_t;
 	static alert_queue_t sAlertQueue;
 
+	typedef std::map<LLString, LLUIString> ui_string_map_t;
+
 private:
 	// common constructor
 	void init();
@@ -221,6 +231,8 @@ class LLPanel : public LLUICtrl
 	LLString		mLabel;
 	S32				mLastTabGroup;
 
+	ui_string_map_t	mUIStrings;
+
 	typedef std::map<LLString, EWidgetType> requirements_map_t;
 	requirements_map_t mRequirements;
 
@@ -228,4 +240,50 @@ class LLPanel : public LLUICtrl
 	static panel_map_t sPanelMap;
 };
 
+class LLLayoutStack : public LLView
+{
+public:
+	typedef enum e_layout_orientation
+	{
+		HORIZONTAL,
+		VERTICAL
+	} eLayoutOrientation;
+
+	LLLayoutStack(eLayoutOrientation orientation);
+	virtual ~LLLayoutStack();
+
+	/*virtual*/ void draw();
+	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+	/*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const;
+	/*virtual*/ void removeCtrl(LLUICtrl* ctrl);
+	virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_LAYOUT_STACK; }
+	virtual LLString getWidgetTag() const { return LL_LAYOUT_STACK_TAG; }
+
+	static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+	S32 getMinWidth();
+	S32 getMinHeight();
+	
+	void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, S32 index = S32_MAX);
+	void removePanel(LLPanel* panel);
+	void updateLayout(BOOL force_resize = FALSE);
+
+protected:
+	struct LLEmbeddedPanel;
+
+	LLEmbeddedPanel* findEmbeddedPanel(LLPanel* panelp);
+	void calcMinExtents();
+	S32 getMinStackSize();
+	S32 getCurStackSize();
+
+protected:
+	eLayoutOrientation mOrientation;
+
+	typedef std::vector<LLEmbeddedPanel*> e_panel_list_t;
+	e_panel_list_t mPanels;
+
+	S32 mMinWidth;
+	S32 mMinHeight;
+};
+
 #endif
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index 69c0da69336f74787739ccc48a1ad377508dd1bf..b58ae09b5d78613d48bd374b79043f0ff32c2ef6 100644
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -149,7 +149,7 @@ BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
 {
 	BOOL handled = FALSE;
 	// do any of the tab buttons have keyboard focus?
-	if (getEnabled() && !called_from_parent)
+	if (getEnabled() && !called_from_parent && mask == MASK_NONE)
 	{
 		switch(key)
 		{
@@ -421,6 +421,69 @@ LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory
 	return radio_group;
 }
 
+// LLCtrlSelectionInterface functions
+BOOL	LLRadioGroup::setCurrentByID( const LLUUID& id )
+{
+	return FALSE;
+}
+
+LLUUID	LLRadioGroup::getCurrentID()
+{
+	return LLUUID::null;
+}
+
+BOOL	LLRadioGroup::setSelectedByValue(LLSD value, BOOL selected)
+{
+	S32 idx = 0;
+	std::string value_string = value.asString();
+	for (button_list_t::const_iterator iter = mRadioButtons.begin();
+		 iter != mRadioButtons.end(); ++iter)
+	{
+		if((*iter)->getName() == value_string)
+		{
+			setSelectedIndex(idx);
+			return TRUE;
+		}
+		idx++;
+	}
+
+	return FALSE;
+}
+
+LLSD	LLRadioGroup::getSimpleSelectedValue()
+{
+	return getValue();	
+}
+
+BOOL	LLRadioGroup::isSelected(LLSD value)
+{
+	S32 idx = 0;
+	std::string value_string = value.asString();
+	for (button_list_t::const_iterator iter = mRadioButtons.begin();
+		 iter != mRadioButtons.end(); ++iter)
+	{
+		if((*iter)->getName() == value_string)
+		{
+			if (idx == mSelectedIndex) 
+			{
+				return TRUE;
+			}
+		}
+		idx++;
+	}
+	return FALSE;
+}
+
+BOOL	LLRadioGroup::operateOnSelection(EOperation op)
+{
+	return FALSE;
+}
+
+BOOL	LLRadioGroup::operateOnAll(EOperation op)
+{
+	return FALSE;
+}
+
 
 LLRadioCtrl::LLRadioCtrl(const LLString& name, const LLRect& rect, const LLString& label,	
 						 const LLFontGL* font, void (*commit_callback)(LLUICtrl*, void*), void* callback_userdata) :
@@ -438,3 +501,4 @@ void LLRadioCtrl::setValue(const LLSD& value)
 	LLCheckBoxCtrl::setValue(value);
 	mButton->setTabStop(value.asBoolean());
 }
+
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
index 01b4a61b82dcbeb11cf56ea2bfd7e52310c5cb49..2a856ee6cf1749c6e7748020ddf7c7bf86fa04a5 100644
--- a/indra/llui/llradiogroup.h
+++ b/indra/llui/llradiogroup.h
@@ -15,6 +15,7 @@
 
 #include "lluictrl.h"
 #include "llcheckboxctrl.h"
+#include "llctrlselectioninterface.h"
 
 class LLFontGL;
 
@@ -32,7 +33,7 @@ class LLRadioCtrl : public LLCheckBoxCtrl
 };
 
 class LLRadioGroup
-:	public LLUICtrl
+:	public LLUICtrl, public LLCtrlSelectionInterface
 {
 public:
 	// Build a radio group.  The number (0...n-1) of the currently selected
@@ -63,7 +64,6 @@ class LLRadioGroup
 	static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
 	void setIndexEnabled(S32 index, BOOL enabled);
 	
-	S32 getItemCount() { return mRadioButtons.size(); }
 	// return the index value of the selected item
 	S32 getSelectedIndex() const;
 	
@@ -87,6 +87,23 @@ class LLRadioGroup
 	// button.
 	static void onClickButton(LLUICtrl* radio, void* userdata);
 	
+	//========================================================================
+	LLCtrlSelectionInterface* getSelectionInterface()	{ return (LLCtrlSelectionInterface*)this; };
+
+	// LLCtrlSelectionInterface functions
+	/*virtual*/ S32		getItemCount() const  				{ return mRadioButtons.size(); }
+	/*virtual*/ BOOL	getCanSelect() const				{ return TRUE; }
+	/*virtual*/ BOOL	selectFirstItem()					{ return setSelectedIndex(0); }
+	/*virtual*/ BOOL	selectNthItem( S32 index )			{ return setSelectedIndex(index); }
+	/*virtual*/ S32		getFirstSelectedIndex()				{ return getSelectedIndex(); }
+	/*virtual*/ BOOL	setCurrentByID( const LLUUID& id );
+	/*virtual*/ LLUUID	getCurrentID();				// LLUUID::null if no items in menu
+	/*virtual*/ BOOL	setSelectedByValue(LLSD value, BOOL selected);
+	/*virtual*/ LLSD	getSimpleSelectedValue();
+	/*virtual*/ BOOL	isSelected(LLSD value);
+	/*virtual*/ BOOL	operateOnSelection(EOperation op);
+	/*virtual*/ BOOL	operateOnAll(EOperation op);
+
 protected:
 	// protected function shared by the two constructors.
 	void init(BOOL border);
diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp
index 0128b4ebbca2722da4699d807bdf03411939b3c7..b3bce755b14c1e7918f022fbaa6a555b0f7a2e49 100644
--- a/indra/llui/llresizebar.cpp
+++ b/indra/llui/llresizebar.cpp
@@ -18,16 +18,18 @@
 #include "llfocusmgr.h"
 #include "llwindow.h"
 
-LLResizeBar::LLResizeBar( const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, Side side )
+LLResizeBar::LLResizeBar( const LLString& name, LLView* resizing_view, const LLRect& rect, S32 min_size, S32 max_size, Side side )
 	:
 	LLView( name, rect, TRUE ),
 	mDragLastScreenX( 0 ),
 	mDragLastScreenY( 0 ),
 	mLastMouseScreenX( 0 ),
 	mLastMouseScreenY( 0 ),
-	mMinWidth( min_width ),
-	mMinHeight( min_height ),
-	mSide( side )
+	mMinSize( min_size ),
+	mMaxSize( max_size ),
+	mSide( side ),
+	mSnappingEnabled(TRUE),
+	mResizingView(resizing_view)
 {
 	// set up some generically good follow code.
 	switch( side )
@@ -129,12 +131,11 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
 		// Make sure the mouse in still over the application.  We don't want to make the parent
 		// so big that we can't see the resize handle any more.
 		LLRect valid_rect = getRootView()->getRect();
-		LLView* resizing_view = getParent();
 		
-		if( valid_rect.localPointInRect( screen_x, screen_y ) && resizing_view )
+		if( valid_rect.localPointInRect( screen_x, screen_y ) && mResizingView )
 		{
 			// Resize the parent
-			LLRect orig_rect = resizing_view->getRect();
+			LLRect orig_rect = mResizingView->getRect();
 			LLRect scaled_rect = orig_rect;
 				
 			S32 new_width = orig_rect.getWidth();
@@ -143,76 +144,63 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
 			switch( mSide )
 			{
 			case LEFT:
-				new_width = orig_rect.getWidth() - delta_x;
-				if( new_width < mMinWidth )
-				{
-					new_width = mMinWidth;
-					delta_x = orig_rect.getWidth() - mMinWidth;
-				}
+				new_width = llclamp(orig_rect.getWidth() - delta_x, mMinSize, mMaxSize);
+				delta_x = orig_rect.getWidth() - new_width;
 				scaled_rect.translate(delta_x, 0);
 				break;
 
 			case TOP:
-				new_height = orig_rect.getHeight() + delta_y;
-				if( new_height < mMinHeight )
-				{
-					new_height = mMinHeight;
-					delta_y = mMinHeight - orig_rect.getHeight();
-				}
+				new_height = llclamp(orig_rect.getHeight() + delta_y, mMinSize, mMaxSize);
+				delta_y = new_height - orig_rect.getHeight();
 				break;
 			
 			case RIGHT:
-				new_width = orig_rect.getWidth() + delta_x;
-				if( new_width < mMinWidth )
-				{
-					new_width = mMinWidth;
-					delta_x = mMinWidth - orig_rect.getWidth();
-				}
+				new_width = llclamp(orig_rect.getWidth() + delta_x, mMinSize, mMaxSize);
+				delta_x = new_width - orig_rect.getWidth();
 				break;
 		
 			case BOTTOM:
-				new_height = orig_rect.getHeight() - delta_y;
-				if( new_height < mMinHeight )
-				{
-					new_height = mMinHeight;
-					delta_y = orig_rect.getHeight() - mMinHeight;
-				}
+				new_height = llclamp(orig_rect.getHeight() - delta_y, mMinSize, mMaxSize);
+				delta_y = orig_rect.getHeight() - new_height;
 				scaled_rect.translate(0, delta_y);
 				break;
 			}
 
 			scaled_rect.mTop = scaled_rect.mBottom + new_height;
 			scaled_rect.mRight = scaled_rect.mLeft + new_width;
-			resizing_view->setRect(scaled_rect);
+			mResizingView->setRect(scaled_rect);
 
 			LLView* snap_view = NULL;
 
-			switch( mSide )
+			if (mSnappingEnabled)
 			{
-			case LEFT:
-				snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
-				break;
-			case TOP:
-				snap_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
-				break;
-			case RIGHT:
-				snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
-				break;
-			case BOTTOM:
-				snap_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
-				break;
+				switch( mSide )
+				{
+				case LEFT:
+					snap_view = mResizingView->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+					break;
+				case TOP:
+					snap_view = mResizingView->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+					break;
+				case RIGHT:
+					snap_view = mResizingView->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+					break;
+				case BOTTOM:
+					snap_view = mResizingView->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+					break;
+				}
 			}
 
 			// register "snap" behavior with snapped view
-			resizing_view->snappedTo(snap_view);
+			mResizingView->snappedTo(snap_view);
 
 			// restore original rectangle so the appropriate changes are detected
-			resizing_view->setRect(orig_rect);
+			mResizingView->setRect(orig_rect);
 			// change view shape as user operation
-			resizing_view->userSetShape(scaled_rect);
+			mResizingView->userSetShape(scaled_rect);
 
 			// update last valid mouse cursor position based on resized view's actual size
-			LLRect new_rect = resizing_view->getRect();
+			LLRect new_rect = mResizingView->getRect();
 			switch(mSide)
 			{
 			case LEFT:
diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h
index 7a77cce8a6985d88c4639248aba6954556e4e88c..cf78879cbaf38e8ecabb5a2a5240d5ef8edc5b7c 100644
--- a/indra/llui/llresizebar.h
+++ b/indra/llui/llresizebar.h
@@ -17,7 +17,7 @@ class LLResizeBar : public LLView
 public:
 	enum Side { LEFT, TOP, RIGHT, BOTTOM };
 
-	LLResizeBar(const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, Side side );
+	LLResizeBar(const LLString& name, LLView* resizing_view, const LLRect& rect, S32 min_size, S32 max_size, Side side );
 
 	virtual EWidgetType getWidgetType() const;
 	virtual LLString getWidgetTag() const;
@@ -27,7 +27,8 @@ class LLResizeBar : public LLView
 	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
 
-	void			setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; }
+	void			setResizeLimits( S32 min_size, S32 max_size ) { mMinSize = min_size; mMaxSize = max_size; }
+	void			setEnableSnapping(BOOL enable) { mSnappingEnabled = enable; }
 
 protected:
 	S32				mDragLastScreenX;
@@ -35,9 +36,11 @@ class LLResizeBar : public LLView
 	S32				mLastMouseScreenX;
 	S32				mLastMouseScreenY;
 	LLCoordGL		mLastMouseDir;
-	S32				mMinWidth;
-	S32				mMinHeight;
+	S32				mMinSize;
+	S32				mMaxSize;
 	Side			mSide;
+	BOOL			mSnappingEnabled;
+	LLView*			mResizingView;
 };
 
 const S32 RESIZE_BAR_HEIGHT = 3;
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index 0e3c73f633ee759d79b5ec2ac9dd3129ea681027..589afb114aacc561a7c833aa441995250ebba77e 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -485,8 +485,7 @@ void LLScrollableContainerView::draw()
 				BOOL show_h_scrollbar = FALSE;
 				calcVisibleSize( mScrolledView->getRect(), &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
 
-				LLGLEnable scissor_test(GL_SCISSOR_TEST);
-				LLUI::setScissorRegionLocal(LLRect(mInnerRect.mLeft, 
+				LLLocalClipRect clip(LLRect(mInnerRect.mLeft, 
 						mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0) + visible_height,
 						visible_width,
 						mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0)
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 1d07d3f36bcbebb33bddfde6da199e3b6f46e0ac..e802b3426bd9bd5d71c7131dbe1a29a68c622101 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -31,7 +31,7 @@
 #include "llkeyboard.h"
 #include "llresizebar.h"
 
-const S32 LIST_BORDER_PAD = 2;		// white space inside the border and to the left of the scrollbar
+const S32 LIST_BORDER_PAD = 0;		// white space inside the border and to the left of the scrollbar
 const S32 MIN_COLUMN_WIDTH = 20;
 const S32 LIST_SNAP_PADDING = 5;
 
@@ -397,6 +397,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect,
 	mCommitOnKeyboardMovement(TRUE),
 	mCommitOnSelectionChange(FALSE),
 	mSelectionChanged(FALSE),
+	mNeedsScroll(FALSE),
 	mCanSelect(TRUE),
 	mDisplayColumnHeaders(FALSE),
 	mCollapseEmptyColumns(FALSE),
@@ -1419,14 +1420,16 @@ void LLScrollListCtrl::drawItems()
 	S32 x = mItemListRect.mLeft;
 	S32 y = mItemListRect.mTop - mLineHeight;
 
-	S32 num_page_lines = mPageLines;
+	// allow for partial line at bottom
+	S32 num_page_lines = mPageLines + 1;
 
 	LLRect item_rect;
 
 	LLGLSUIDefault gls_ui;
 	
 	{
-	
+		LLLocalClipRect clip(mItemListRect);
+
 		S32 cur_x = x;
 		S32 cur_y = y;
 		
@@ -1538,6 +1541,11 @@ void LLScrollListCtrl::draw()
 {
 	if( getVisible() )
 	{
+		if (mNeedsScroll)
+		{
+			scrollToShowSelected();
+			mNeedsScroll = FALSE;
+		}
 		LLRect background(0, mRect.getHeight(), mRect.getWidth(), 0);
 		// Draw background
 		if (mBackgroundVisible)
@@ -1690,6 +1698,7 @@ BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
 
 		gFocusMgr.setMouseCapture(this);
 		selectItemAt(x, y, mask);
+		mNeedsScroll = TRUE;
 	}
 
 	return TRUE;
@@ -1699,17 +1708,16 @@ BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
 {
 	if (hasMouseCapture())
 	{
+		// release mouse capture immediately so 
+		// scroll to show selected logic will work
+		gFocusMgr.setMouseCapture(NULL);
 		if(mask == MASK_NONE)
 		{
 			selectItemAt(x, y, mask);
+			mNeedsScroll = TRUE;
 		}
 	}
 
-	if (hasMouseCapture())
-	{
-		gFocusMgr.setMouseCapture(NULL);
-	}
-
 	// always commit when mouse operation is completed inside list
 	if (mItemListRect.pointInRect(x,y))
 	{
@@ -1750,7 +1758,8 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
 		mItemListRect.getWidth(),
 		mLineHeight );
 
-	int num_page_lines = mPageLines;
+	// allow for partial line at bottom
+	S32 num_page_lines = mPageLines + 1;
 
 	S32 line = 0;
 	item_list::iterator iter;
@@ -1783,6 +1792,7 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
 		if(mask == MASK_NONE)
 		{
 			selectItemAt(x, y, mask);
+			mNeedsScroll = TRUE;
 		}
 	}
 	else if (mCanSelect)
@@ -1830,7 +1840,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent
 				{
 					// commit implicit in call
 					selectPrevItem(FALSE);
-					scrollToShowSelected();
+					mNeedsScroll = TRUE;
 					handled = TRUE;
 				}
 				break;
@@ -1839,7 +1849,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent
 				{
 					// commit implicit in call
 					selectNextItem(FALSE);
-					scrollToShowSelected();
+					mNeedsScroll = TRUE;
 					handled = TRUE;
 				}
 				break;
@@ -1847,7 +1857,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent
 				if (mAllowKeyboardMovement || hasFocus())
 				{
 					selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1));
-					scrollToShowSelected();
+					mNeedsScroll = TRUE;
 					if (mCommitOnKeyboardMovement
 						&& !mCommitOnSelectionChange) 
 					{
@@ -1860,7 +1870,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent
 				if (mAllowKeyboardMovement || hasFocus())
 				{
 					selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1));
-					scrollToShowSelected();
+					mNeedsScroll = TRUE;
 					if (mCommitOnKeyboardMovement
 						&& !mCommitOnSelectionChange) 
 					{
@@ -1873,7 +1883,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent
 				if (mAllowKeyboardMovement || hasFocus())
 				{
 					selectFirstItem();
-					scrollToShowSelected();
+					mNeedsScroll = TRUE;
 					if (mCommitOnKeyboardMovement
 						&& !mCommitOnSelectionChange) 
 					{
@@ -1886,7 +1896,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent
 				if (mAllowKeyboardMovement || hasFocus())
 				{
 					selectNthItem(getItemCount() - 1);
-					scrollToShowSelected();
+					mNeedsScroll = TRUE;
 					if (mCommitOnKeyboardMovement
 						&& !mCommitOnSelectionChange) 
 					{
@@ -1925,6 +1935,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent
 				}
 				else if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString), FALSE))
 				{
+					mNeedsScroll = TRUE;
 					// update search string only on successful match
 					mSearchTimer.reset();
 
@@ -1964,6 +1975,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_
 	if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE))
 	{
 		// update search string only on successful match
+		mNeedsScroll = TRUE;
 		mSearchString += uni_char;
 		mSearchTimer.reset();
 
@@ -2009,6 +2021,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_
 				if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char)
 				{
 					selectItem(item);
+					mNeedsScroll = TRUE;
 					cellp->highlightText(0, 1);
 					mSearchTimer.reset();
 
@@ -2030,8 +2043,6 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_
 		}
 	}
 
-	// make sure selected item is on screen
-	scrollToShowSelected();
 	return TRUE;
 }
 
@@ -2183,6 +2194,13 @@ void LLScrollListCtrl::setScrollPos( S32 pos )
 
 void LLScrollListCtrl::scrollToShowSelected()
 {
+	// don't scroll automatically when capturing mouse input
+	// as that will change what is currently under the mouse cursor
+	if (hasMouseCapture())
+	{
+		return;
+	}
+
 	S32 index = getFirstSelectedIndex();
 	if (index < 0)
 	{
@@ -3013,8 +3031,9 @@ LLColumnHeader::LLColumnHeader(const LLString& label, const LLRect &rect, LLScro
 	const S32 RESIZE_BAR_THICKNESS = 3;
 	mResizeBar = new LLResizeBar( 
 		"resizebar",
+		this,
 		LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0), 
-		MIN_COLUMN_WIDTH, mRect.getHeight(), LLResizeBar::RIGHT );
+		MIN_COLUMN_WIDTH, S32_MAX, LLResizeBar::RIGHT );
 	addChild(mResizeBar);
 
 	mResizeBar->setEnabled(FALSE);
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 793f97dc64bf6877a598965586b7aaa002d1ce71..9604b0569ce097e237bcd87d054d23f3cd024bec 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -585,6 +585,7 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	BOOL			mCommitOnKeyboardMovement;
 	BOOL			mCommitOnSelectionChange;
 	BOOL			mSelectionChanged;
+	BOOL			mNeedsScroll;
 	BOOL			mCanSelect;
 	BOOL			mDisplayColumnHeaders;
 	BOOL			mCollapseEmptyColumns;
diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp
index 3a010139435e41fcc40418cfc36329089f888cfe..06fd2830a35515c3106506e5054d13744a507fd8 100644
--- a/indra/llui/llslider.cpp
+++ b/indra/llui/llslider.cpp
@@ -30,6 +30,7 @@ LLSlider::LLSlider(
 	F32 min_value,
 	F32 max_value,
 	F32 increment,
+	BOOL volume,
 	const LLString& control_name)
 	:
 	LLUICtrl( name, rect, TRUE,	on_commit_callback, callback_userdata, 
@@ -39,6 +40,7 @@ LLSlider::LLSlider(
 	mMinValue( min_value ),
 	mMaxValue( max_value ),
 	mIncrement( increment ),
+	mVolumeSlider( volume ),
 	mMouseOffset( 0 ),
 	mDragStartThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ),
 	mThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ),
@@ -49,7 +51,7 @@ LLSlider::LLSlider(
 	mMouseDownCallback( NULL ),
 	mMouseUpCallback( NULL )
 {
-	// prperly handle setting the starting thumb rect
+	// properly handle setting the starting thumb rect
 	// do it this way to handle both the operating-on-settings
 	// and standalone ways of using this
 	setControlName(control_name, NULL);
@@ -74,13 +76,15 @@ void LLSlider::setValue(F32 value, BOOL from_event)
 	value -= mMinValue;
 	value += mIncrement/2.0001f;
 	value -= fmod(value, mIncrement);
-	mValue = mMinValue + value;
+	value += mMinValue;
 
-	if (!from_event)
+	if (!from_event && mValue != value)
 	{
-		setControlValue(mValue);
+		setControlValue(value);
 	}
-	
+
+	mValue = value;
+
 	F32 t = (mValue - mMinValue) / (mMaxValue - mMinValue);
 
 	S32 left_edge = THUMB_WIDTH/2;
@@ -91,6 +95,18 @@ void LLSlider::setValue(F32 value, BOOL from_event)
 	mThumbRect.mRight = x + (THUMB_WIDTH/2);
 }
 
+void LLSlider::setValueAndCommit(F32 value)
+{
+	F32 old_value = mValue;
+	setValue(value);
+
+	if (mValue != old_value)
+	{
+		onCommit();
+	}
+}
+
+
 F32 LLSlider::getValueF32() const
 {
 	return mValue;
@@ -107,8 +123,7 @@ BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask)
 		x = llclamp( x, left_edge, right_edge );
 
 		F32 t = F32(x - left_edge) / (right_edge - left_edge);
-		setValue(t * (mMaxValue - mMinValue) + mMinValue );
-		onCommit();
+		setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
 
 		getWindow()->setCursor(UI_CURSOR_ARROW);
 		lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;		
@@ -158,8 +173,7 @@ BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask)
 
 	if (MASK_CONTROL & mask) // if CTRL is modifying
 	{
-		setValue(mInitialValue);
-		onCommit();
+		setValueAndCommit(mInitialValue);
 	}
 	else
 	{
@@ -196,13 +210,11 @@ BOOL	LLSlider::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
 			handled = TRUE;
 			break;
 		case KEY_LEFT:
-			setValue(getValueF32() - getIncrement());
-			onCommit();
+			setValueAndCommit(getValueF32() - getIncrement());
 			handled = TRUE;
 			break;
 		case KEY_RIGHT:
-			setValue(getValueF32() + getIncrement());
-			onCommit();
+			setValueAndCommit(getValueF32() + getIncrement());
 			handled = TRUE;
 			break;
 		default:
@@ -224,33 +236,93 @@ void LLSlider::draw()
 		LLRect rect(mDragStartThumbRect);
 
 		F32 opacity = mEnabled ? 1.f : 0.3f;
+		LLColor4 center_color = (mThumbCenterColor % opacity);
+		LLColor4 outline_color = (mThumbOutlineColor % opacity);
+		LLColor4 track_color = (mTrackColor % opacity);
 
+		LLImageGL* thumb_imagep = NULL;
+		
 		// Track
+		if (mVolumeSlider)
+		{
+			LLRect track(0, mRect.getHeight(), mRect.getWidth(), 0);
+
+			track.mBottom += 3;
+			track.mTop -= 1;
+			track.mRight -= 1;
+
+			gl_triangle_2d(track.mLeft, track.mBottom,
+						   track.mRight, track.mBottom,
+						   track.mRight, track.mTop,
+						   center_color,
+						   TRUE);
+			gl_triangle_2d(track.mLeft, track.mBottom,
+						   track.mRight, track.mBottom,
+						   track.mRight, track.mTop,
+						   outline_color,
+						   FALSE);
+		}
+		else
+		{
+			LLUUID thumb_image_id;
+			thumb_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga"));
+			thumb_imagep = LLUI::sImageProvider->getUIImageByID(thumb_image_id);
 
-		LLUUID thumb_image_id;
-		thumb_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga"));
-		LLImageGL* thumb_imagep = LLUI::sImageProvider->getUIImageByID(thumb_image_id);
-
-		S32 height_offset = (mRect.getHeight() - TRACK_HEIGHT) / 2;
-		LLRect track_rect(0, mRect.getHeight() - height_offset, mRect.getWidth(), height_offset );
+			S32 height_offset = (mRect.getHeight() - TRACK_HEIGHT) / 2;
+			LLRect track_rect(0, mRect.getHeight() - height_offset, mRect.getWidth(), height_offset );
 
-		track_rect.stretch(-1);
-		gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 16, 16, track_rect.getWidth(), track_rect.getHeight(),
-			thumb_imagep, mTrackColor % opacity);
-		//gl_rect_2d( track_rect, mThumbOutlineColor % opacity );
+			track_rect.stretch(-1);
+			gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 16, 16, track_rect.getWidth(), track_rect.getHeight(),
+											 thumb_imagep, track_color);
+		}
 
+		// Thumb
 		if (!thumb_imagep)
 		{
-			gl_rect_2d(mThumbRect, mThumbCenterColor, TRUE);
-			if (hasMouseCapture())
+			if (mVolumeSlider)
+			{
+				if (hasMouseCapture())
+				{
+					LLRect rect(mDragStartThumbRect);
+					gl_rect_2d( rect, outline_color );
+					rect.stretch(-1);
+					gl_rect_2d( rect, mThumbCenterColor % 0.3f );
+
+					if (hasFocus())
+					{
+						LLRect thumb_rect = mThumbRect;
+						thumb_rect.stretch(llround(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt())));
+						gl_rect_2d(thumb_rect, gFocusMgr.getFocusColor());
+					}
+					gl_rect_2d( mThumbRect, mThumbOutlineColor );
+				}
+				else
+				{ 
+					if (hasFocus())
+					{
+						LLRect thumb_rect = mThumbRect;
+						thumb_rect.stretch(llround(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt())));
+						gl_rect_2d(thumb_rect, gFocusMgr.getFocusColor());
+					}
+					LLRect rect(mThumbRect);
+					gl_rect_2d(rect, outline_color);
+					rect.stretch(-1);
+					gl_rect_2d( rect, center_color);
+				}
+			}
+			else
 			{
-				gl_rect_2d(mDragStartThumbRect, mThumbCenterColor % opacity, FALSE);
+				gl_rect_2d(mThumbRect, mThumbCenterColor, TRUE);
+				if (hasMouseCapture())
+				{
+					gl_rect_2d(mDragStartThumbRect, center_color, FALSE);
+				}
 			}
 		}
 		else if( hasMouseCapture() )
 		{
 			gl_draw_scaled_image_with_border(mDragStartThumbRect.mLeft, mDragStartThumbRect.mBottom, 16, 16, mDragStartThumbRect.getWidth(), mDragStartThumbRect.getHeight(), 
-				thumb_imagep, mThumbCenterColor % 0.3f, TRUE);
+											 thumb_imagep, mThumbCenterColor % 0.3f, TRUE);
 
 			if (hasFocus())
 			{
@@ -258,20 +330,12 @@ void LLSlider::draw()
 				LLRect highlight_rect = mThumbRect;
 				highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt)));
 				gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(),
-					thumb_imagep, gFocusMgr.getFocusColor());
+												 thumb_imagep, gFocusMgr.getFocusColor());
 			}
 
-
 			gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(), 
-				thumb_imagep, mThumbOutlineColor, TRUE);
-
-			//// Start Thumb
-			//gl_rect_2d( mDragStartThumbRect, mThumbOutlineColor % 0.3f );
-			//rect.stretch(-1);
-			//gl_rect_2d( rect, mThumbCenterColor % 0.3f );
+											 thumb_imagep, mThumbOutlineColor, TRUE);
 
-			//// Thumb
-			//gl_rect_2d( mThumbRect, mThumbOutlineColor );
 		}
 		else
 		{ 
@@ -281,22 +345,12 @@ void LLSlider::draw()
 				LLRect highlight_rect = mThumbRect;
 				highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt)));
 				gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(),
-					thumb_imagep, gFocusMgr.getFocusColor());
+												 thumb_imagep, gFocusMgr.getFocusColor());
 			}
 
 			gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(), 
-				thumb_imagep, mThumbCenterColor % opacity, TRUE);
-			//rect = mThumbRect;
-
-			//gl_rect_2d( mThumbRect, mThumbOutlineColor % opacity );
-			//  
-			//rect.stretch(-1);
-
-			//// Thumb
-			//gl_rect_2d( rect, mThumbCenterColor % opacity );
-
+											 thumb_imagep, center_color, TRUE);
 		}
-
 		LLUICtrl::draw();
 	}
 }
@@ -310,6 +364,7 @@ LLXMLNodePtr LLSlider::getXML(bool save_children) const
 	node->createChild("min_val", TRUE)->setFloatValue(getMinValue());
 	node->createChild("max_val", TRUE)->setFloatValue(getMaxValue());
 	node->createChild("increment", TRUE)->setFloatValue(getIncrement());
+	node->createChild("volume", TRUE)->setBoolValue(getVolumeSlider());
 
 	return node;
 }
@@ -336,6 +391,8 @@ LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa
 	F32 increment = 0.1f;
 	node->getAttributeF32("increment", increment);
 
+	BOOL volume = node->hasName("volume_slider") ? TRUE : FALSE;
+	node->getAttributeBOOL("volume", volume);
 
 	LLSlider* slider = new LLSlider(name,
 							rect,
@@ -344,7 +401,8 @@ LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa
 							initial_value,
 							min_value,
 							max_value,
-							increment);
+							increment,
+							volume);
 
 	slider->initFromXML(node, parent);
 
diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h
index d88da420646d5253708b7d5b5b0fa1eef11d22ef..d9cdf11d5dbdf5a5198f3346c18cff9636149325 100644
--- a/indra/llui/llslider.h
+++ b/indra/llui/llslider.h
@@ -26,6 +26,7 @@ class LLSlider : public LLUICtrl
 		F32 min_value,
 		F32 max_value,
 		F32 increment,
+		BOOL volume,
 		const LLString& control_name = LLString::null );
 
 	virtual EWidgetType getWidgetType() const;
@@ -46,6 +47,7 @@ class LLSlider : public LLUICtrl
 	F32				getMinValue() const		{ return mMinValue; }
 	F32				getMaxValue() const		{ return mMaxValue; }
 	F32				getIncrement() const	{ return mIncrement; }
+	BOOL			getVolumeSlider() const	{ return mVolumeSlider; }
 	void			setMinValue(F32 min_value) {mMinValue = min_value;}
 	void			setMaxValue(F32 max_value) {mMaxValue = max_value;}
 	void			setIncrement(F32 increment) {mIncrement = increment;}
@@ -58,6 +60,9 @@ class LLSlider : public LLUICtrl
 	virtual BOOL	handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
 	virtual void	draw();
 
+protected:
+	void			setValueAndCommit(F32 value);
+
 protected:
 	F32				mValue;
 	F32				mInitialValue;
@@ -65,6 +70,7 @@ class LLSlider : public LLUICtrl
 	F32				mMaxValue;
 	F32				mIncrement;
 
+	BOOL			mVolumeSlider;
 	S32				mMouseOffset;
 	LLRect			mDragStartThumbRect;
 
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index 8b5cd4690e0e61bd7a0c385bec9552f490baf892..4ae8c5d222e3c558fcc58a022f303a8a615950d2 100644
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -37,6 +37,7 @@ LLSliderCtrl::LLSliderCtrl(const LLString& name, const LLRect& rect,
 						   S32 text_left,
 						   BOOL show_text,
 						   BOOL can_edit_text,
+						   BOOL volume,
 						   void (*commit_callback)(LLUICtrl*, void*),
 						   void* callback_user_data,
 						   F32 initial_value, F32 min_value, F32 max_value, F32 increment,
@@ -45,6 +46,7 @@ LLSliderCtrl::LLSliderCtrl(const LLString& name, const LLRect& rect,
 	  mFont(font),
 	  mShowText( show_text ),
 	  mCanEditText( can_edit_text ),
+	  mVolumeSlider( volume ),
 	  mPrecision( 3 ),
 	  mLabelBox( NULL ),
 	  mLabelWidth( label_width ),
@@ -84,7 +86,7 @@ LLSliderCtrl::LLSliderCtrl(const LLString& name, const LLRect& rect,
 		"slider",
 		slider_rect, 
 		LLSliderCtrl::onSliderCommit, this, 
-		initial_value, min_value, max_value, increment,
+		initial_value, min_value, max_value, increment, volume,
 		control_which );
 	addChild( mSlider );
 	
@@ -423,6 +425,8 @@ LLXMLNodePtr LLSliderCtrl::getXML(bool save_children) const
 
 	node->createChild("can_edit_text", TRUE)->setBoolValue(mCanEditText);
 
+	node->createChild("volume", TRUE)->setBoolValue(mVolumeSlider);
+	
 	node->createChild("decimal_digits", TRUE)->setIntValue(mPrecision);
 
 	if (mLabelBox)
@@ -474,6 +478,9 @@ LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory
 	BOOL can_edit_text = FALSE;
 	node->getAttributeBOOL("can_edit_text", can_edit_text);
 
+	BOOL volume = FALSE;
+	node->getAttributeBOOL("volume", volume);
+
 	F32 initial_value = 0.f;
 	node->getAttributeF32("initial_val", initial_value);
 
@@ -521,6 +528,7 @@ LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory
 							rect.getWidth() - text_left,
 							show_text,
 							can_edit_text,
+							volume,
 							callback,
 							NULL,
 							initial_value, 
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
index 2185e42eb1cdb43df24e94ce7fe62723769bb5d1..078035046294f812271dc52880c4ef39e24d6465 100644
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -40,6 +40,7 @@ class LLSliderCtrl : public LLUICtrl
 		S32 text_left,
 		BOOL show_text,
 		BOOL can_edit_text,
+		BOOL volume,		 
 		void (*commit_callback)(LLUICtrl*, void*),
 		void* callback_userdata,
 		F32 initial_value, F32 min_value, F32 max_value, F32 increment,
@@ -104,7 +105,8 @@ class LLSliderCtrl : public LLUICtrl
 	const LLFontGL*	mFont;
 	BOOL			mShowText;
 	BOOL			mCanEditText;
-
+	BOOL			mVolumeSlider;
+	
 	S32				mPrecision;
 	LLTextBox*		mLabelBox;
 	S32				mLabelWidth;
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index ef527b32c2714e3ed2017787536e2fe40869c73b..65551e5c484e26aebc485a0d1bfecfa694dc718f 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -50,6 +50,7 @@ LLTabContainerCommon::LLTabContainerCommon(
 	: 
 	LLPanel(name, rect, bordered),
 	mCurrentTabIdx(-1),
+	mTabsHidden(FALSE),
 	mScrolled(FALSE),
 	mScrollPos(0),
 	mScrollPosPixels(0),
@@ -75,6 +76,7 @@ LLTabContainerCommon::LLTabContainerCommon(
 	: 
 	LLPanel(name, rect_control, bordered),
 	mCurrentTabIdx(-1),
+	mTabsHidden(FALSE),
 	mScrolled(FALSE),
 	mScrollPos(0),
 	mScrollPosPixels(0),
@@ -127,11 +129,11 @@ void LLTabContainerCommon::addPlaceholder(LLPanel* child, const LLString& label)
 	addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE);
 }
 
-void LLTabContainerCommon::lockTabs()
+void LLTabContainerCommon::lockTabs(S32 num_tabs)
 {
-	// count current tabs and ensure no new tabs get
+	// count current tabs or use supplied value and ensure no new tabs get
 	// inserted between them
-	mLockedTabCount = getTabCount();
+	mLockedTabCount = num_tabs > 0 ? num_tabs : getTabCount();
 }
 
 void LLTabContainerCommon::removeTabPanel(LLPanel* child)
@@ -522,12 +524,12 @@ void LLTabContainerCommon::setTabPanelFlashing(LLPanel* child, BOOL state )
 	}
 }
 
-void LLTabContainerCommon::setTabImage(LLPanel* child, std::string img_name)
+void LLTabContainerCommon::setTabImage(LLPanel* child, std::string img_name, const LLColor4& color)
 {
 	LLTabTuple* tuple = getTabByPanel(child);
 	if( tuple )
 	{
-		tuple->mButton->setImageOverlay(img_name, LLFontGL::RIGHT);
+		tuple->mButton->setImageOverlay(img_name, LLFontGL::RIGHT, color);
 	}
 }
 
@@ -647,6 +649,8 @@ LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtr
 		}
 	}
 	
+	node->getAttributeBOOL("hide_tabs", tab_container->mTabsHidden);
+
 	tab_container->setPanelParameters(node, parent);
 
 	if (LLFloater::getFloaterHost())
@@ -1016,10 +1020,11 @@ void LLTabContainer::setPanelTitle(S32 index, const LLString& title)
 {
 	if (index >= 0 && index < (S32)mTabList.size())
 	{
-		LLButton* tab_button = mTabList[index]->mButton;
+		LLTabTuple* tuple = mTabList[index];
+		LLButton* tab_button = tuple->mButton;
 		const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
 		mTotalTabWidth -= tab_button->getRect().getWidth();
-		tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
+		tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
 		mTotalTabWidth += tab_button->getRect().getWidth();
 		tab_button->setLabelSelected(title);
 		tab_button->setLabelUnselected(title);
@@ -1225,63 +1230,60 @@ void LLTabContainer::draw()
 
 		LLPanel::draw();
 
-		// Show all the buttons
-		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+		// if tabs are hidden, don't draw them and leave them in the invisible state
+		if (!mTabsHidden)
 		{
-			LLTabTuple* tuple = *iter;
-			tuple->mButton->setVisible( TRUE );
-		}
-
-		// Draw some of the buttons...
+			// Show all the buttons
+			for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+			{
+				LLTabTuple* tuple = *iter;
+				tuple->mButton->setVisible( TRUE );
+			}
 
-		LLGLEnable scissor_test(has_scroll_arrows ? GL_SCISSOR_TEST : GL_FALSE);
-		if( has_scroll_arrows )
-		{
-			// ...but clip them.
-			S32 x1 = mLeftArrowBtn->getRect().mRight;
-			S32 y1 = 0;
-			S32 x2 = mRightArrowBtn->getRect().mLeft;
-			S32 y2 = 1;
-			if (mTabList.size() > 0)
+			// Draw some of the buttons...
+			LLRect clip_rect = getLocalRect();
+			if (has_scroll_arrows)
 			{
-				y2 = mTabList[0]->mButton->getRect().mTop;
+				// ...but clip them.
+				clip_rect.mLeft = mLeftArrowBtn->getRect().mRight;
+				clip_rect.mRight = mRightArrowBtn->getRect().mLeft;
 			}
-			LLUI::setScissorRegionLocal(LLRect(x1, y2, x2, y1));
-		}
+			LLLocalClipRect clip(clip_rect);
 
-		S32 max_scroll_visible = mTabList.size() - mMaxScrollPos + mScrollPos;
-		S32 idx = 0;
-		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
-		{
-			LLTabTuple* tuple = *iter;
+			S32 max_scroll_visible = mTabList.size() - mMaxScrollPos + mScrollPos;
+			S32 idx = 0;
+			for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+			{
+				LLTabTuple* tuple = *iter;
 
-			tuple->mButton->translate( left - tuple->mButton->getRect().mLeft, 0 );
-			left += tuple->mButton->getRect().getWidth();
+				tuple->mButton->translate( left - tuple->mButton->getRect().mLeft, 0 );
+				left += tuple->mButton->getRect().getWidth();
 
-			if( idx < mScrollPos )
-			{
-				if( tuple->mButton->getFlashing() )
+				if( idx < mScrollPos )
 				{
-					mLeftArrowBtn->setFlashing( TRUE );
+					if( tuple->mButton->getFlashing() )
+					{
+						mLeftArrowBtn->setFlashing( TRUE );
+					}
 				}
-			}
-			else
-			if( max_scroll_visible < idx )
-			{
-				if( tuple->mButton->getFlashing() )
+				else
+				if( max_scroll_visible < idx )
 				{
-					mRightArrowBtn->setFlashing( TRUE );
+					if( tuple->mButton->getFlashing() )
+					{
+						mRightArrowBtn->setFlashing( TRUE );
+					}
 				}
-			}
 
-			LLUI::pushMatrix();
-			{
-				LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
-				tuple->mButton->draw();
+				LLUI::pushMatrix();
+				{
+					LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
+					tuple->mButton->draw();
+				}
+				LLUI::popMatrix();
+			
+				idx++;
 			}
-			LLUI::popMatrix();
-		
-			idx++;
 		}
 
 		mLeftArrowBtn->setFlashing(FALSE);
@@ -1608,12 +1610,12 @@ BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,	BOOL drop,	EDrag
 	return LLView::handleDragAndDrop(x,	y, mask, drop, type, cargo_data,  accept, tooltip);
 }
 
-void LLTabContainer::setTabImage(LLPanel* child, std::string image_name)
+void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
 {
 	LLTabTuple* tuple = getTabByPanel(child);
 	if( tuple )
 	{
-		tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT);
+		tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT, color);
 
 		const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
 		// remove current width from total tab strip width
@@ -1622,7 +1624,11 @@ void LLTabContainer::setTabImage(LLPanel* child, std::string image_name)
 		S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? 
 			tuple->mButton->getImageOverlay()->getWidth(0) :
 			0;
-		tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + image_overlay_width, mMinTabWidth, mMaxTabWidth), 
+
+		tuple->mPadding = image_overlay_width;
+
+		tuple->mButton->setRightHPad(tuple->mPadding + LLBUTTON_H_PAD);
+		tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), 
 								tuple->mButton->getRect().getHeight());
 		// add back in button width to total tab strip width
 		mTotalTabWidth += tuple->mButton->getRect().getWidth();
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index a395fd94aff5c0b823f967581cfd8b372388f9a7..46659834029365730ce41c221541d1723d693c4b 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -67,7 +67,7 @@ class LLTabContainerCommon : public LLPanel
 							 BOOL placeholder = FALSE,
 							 eInsertionPoint insertion_point = END) = 0;
 	virtual void		addPlaceholder(LLPanel* child, const LLString& label);
-	virtual void		lockTabs();
+	virtual void		lockTabs(S32 num_tabs = 0);
 
 	virtual void		enableTabButton(S32 which, BOOL enable);
 
@@ -94,7 +94,7 @@ class LLTabContainerCommon : public LLPanel
 
 	BOOL        getTabPanelFlashing(LLPanel* child);
 	void		setTabPanelFlashing(LLPanel* child, BOOL state);
-	virtual void setTabImage(LLPanel* child, std::string img_name);
+	virtual void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white);
 	void		setTitle( const LLString& title );
 	const LLString getPanelTitle(S32 index);
 
@@ -135,7 +135,8 @@ class LLTabContainerCommon : public LLPanel
 			mOnChangeCallback( cb ),
 			mUserData( userdata ),
 			mOldState(FALSE),
-			mPlaceholderText(placeholder)
+			mPlaceholderText(placeholder),
+			mPadding(0)
 			{}
 
 		LLTabContainerCommon*  mTabContainer;
@@ -145,11 +146,13 @@ class LLTabContainerCommon : public LLPanel
 		void*			 mUserData;
 		BOOL			 mOldState;
 		LLTextBox*		 mPlaceholderText;
+		S32				 mPadding;
 	};
 
 	typedef std::vector<LLTabTuple*> tuple_list_t;
 	tuple_list_t					mTabList;
 	S32								mCurrentTabIdx;
+	BOOL							mTabsHidden;
 
 	BOOL							mScrolled;
 	LLFrameTimer					mScrollTimer;
@@ -208,7 +211,7 @@ class LLTabContainer : public LLTabContainerCommon
 	/*virtual*/ void removeTabPanel( LLPanel* child );
 
 	/*virtual*/ void		setPanelTitle(S32 index, const LLString& title);
-	/*virtual*/ void		setTabImage(LLPanel* child, std::string img_name);
+	/*virtual*/ void		setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white);
 	/*virtual*/ void		setRightTabBtnOffset( S32 offset );
 	 
 	/*virtual*/ void		setMinTabWidth(S32 width);
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index eb59765927ecdc3a91661860d3bc1bdecfc04b44..ea5897e28e69bfb3e5a2792f9fbc2787d701d952 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -2993,8 +2993,7 @@ void LLTextEditor::draw()
 	if( getVisible() )
 	{
 		{
-			LLGLEnable scissor_test(GL_SCISSOR_TEST);
-			LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth() - (mScrollbar->getVisible() ? SCROLLBAR_SIZE : 0), 0));
+			LLLocalClipRect clip(LLRect(0, mRect.getHeight(), mRect.getWidth() - (mScrollbar->getVisible() ? SCROLLBAR_SIZE : 0), 0));
 
 			bindEmbeddedChars( mGLFont );
 
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index c65500b56c7e01440dd736244b90600786932777..696be050ce3efafd12d121d78cb546f66e33def2 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -57,6 +57,8 @@ LLVector2		LLUI::sGLScaleFactor(1.f, 1.f);
 LLWindow*		LLUI::sWindow = NULL;
 LLHtmlHelp*		LLUI::sHtmlHelp = NULL;
 BOOL            LLUI::sShowXUINames = FALSE;
+std::stack<LLRect> LLUI::sClipRectStack;
+
 //
 // Functions
 //
@@ -90,7 +92,7 @@ void make_ui_sound(const LLString& name)
 			{
 				llinfos << "ui sound name: " << name << llendl;	
 			}
-			LLUI::sAudioCallback(uuid, LLUI::sConfigGroup->getF32("AudioLevelUI"));
+			LLUI::sAudioCallback(uuid);
 		}
 	}
 }
@@ -1791,3 +1793,59 @@ void LLUI::setHtmlHelp(LLHtmlHelp* html_help)
 {
 	LLUI::sHtmlHelp = html_help;
 }
+
+//static 
+void LLUI::pushClipRect(const LLRect& rect)
+{
+	LLRect combined_clip_rect = rect;
+	if (!sClipRectStack.empty())
+	{
+		combined_clip_rect.intersectWith(sClipRectStack.top());
+	}
+	sClipRectStack.push(combined_clip_rect);
+	setScissorRegionScreen(combined_clip_rect);
+}
+
+//static 
+void LLUI::popClipRect()
+{
+	sClipRectStack.pop();
+	if (!sClipRectStack.empty())
+	{
+		setScissorRegionScreen(sClipRectStack.top());
+	}
+}
+
+LLClipRect::LLClipRect(const LLRect& rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST, enabled), mEnabled(enabled)
+{
+	if (mEnabled)
+	{
+		LLUI::pushClipRect(rect);
+	}
+}
+
+LLClipRect::~LLClipRect()
+{
+	if (mEnabled)
+	{
+		LLUI::popClipRect();
+	}
+}
+
+LLLocalClipRect::LLLocalClipRect(const LLRect &rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST, enabled), mEnabled(enabled)
+{
+	if (mEnabled)
+	{
+		LLRect scissor_rect = rect;
+		scissor_rect.translate(LLFontGL::sCurOrigin.mX, LLFontGL::sCurOrigin.mY);
+		LLUI::pushClipRect(scissor_rect);
+	}
+}
+
+LLLocalClipRect::~LLLocalClipRect()
+{
+	if (mEnabled)
+	{
+		LLUI::popClipRect();
+	}
+}
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index dbe79338e5c86d50e8e3fee7c5ce31a69b947216..fefe75f43ec48211b4ea95d020aab31bfcd1421d 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -16,6 +16,8 @@
 #include "llrect.h"
 #include "llcoord.h"
 #include "llhtmlhelp.h"
+#include "llgl.h"
+#include <stack>
 
 class LLColor4;
 class LLVector3;
@@ -123,7 +125,7 @@ extern BOOL gShowTextEditCursor;
 extern LLString gLanguage;
 
 class LLImageProviderInterface;
-typedef	void (*LLUIAudioCallback)(const LLUUID& uuid, F32 volume);
+typedef	void (*LLUIAudioCallback)(const LLUUID& uuid);
 
 class LLUI
 {
@@ -144,8 +146,8 @@ class LLUI
 
 	//helper functions (should probably move free standing rendering helper functions here)
 	static LLString locateSkin(const LLString& filename);
-	static void setScissorRegionScreen(const LLRect& rect);
-	static void setScissorRegionLocal(const LLRect& rect); // works assuming LLUI::translate has been called
+	static void pushClipRect(const LLRect& rect);
+	static void popClipRect();
 	static void setCursorPositionScreen(S32 x, S32 y);
 	static void setCursorPositionLocal(LLView* viewp, S32 x, S32 y);
 	static void setScaleFactor(const LLVector2& scale_factor);
@@ -153,6 +155,11 @@ class LLUI
 	static LLUUID findAssetUUIDByName(const LLString&	name);
 	static LLVector2 getWindowSize();
 	static void setHtmlHelp(LLHtmlHelp* html_help);
+
+private:
+	static void setScissorRegionScreen(const LLRect& rect);
+	static void setScissorRegionLocal(const LLRect& rect); // works assuming LLUI::translate has been called
+
 public:
 	static LLControlGroup* sConfigGroup;
 	static LLControlGroup* sColorsGroup;
@@ -163,6 +170,8 @@ class LLUI
 	static LLWindow*		sWindow;
 	static BOOL             sShowXUINames;
 	static LLHtmlHelp*		sHtmlHelp;
+	static std::stack<LLRect> sClipRectStack;
+
 };
 
 // UI widgets
@@ -251,6 +260,7 @@ typedef enum e_widget_type
 	WIDGET_TYPE_TEXTURE_VIEW,
 	WIDGET_TYPE_MEMORY_VIEW,
 	WIDGET_TYPE_FRAME_STAT_VIEW,
+	WIDGET_TYPE_LAYOUT_STACK,
 	WIDGET_TYPE_DONTCARE,
 	WIDGET_TYPE_COUNT
 } EWidgetType;
@@ -272,38 +282,38 @@ class LLUIInstanceMgr
 	}
 
 	// default show and hide methods
-	static T* showInstance(const LLSD& seed) 
+	static T* showInstance(const LLSD& seed = LLSD()) 
 	{ 
 		T* instance = INSTANCE_ADAPTOR::getInstance(seed); 
 		INSTANCE_ADAPTOR::show(instance);
 		return instance;
 	}
 
-	static void hideInstance(const LLSD& seed) 
+	static void hideInstance(const LLSD& seed = LLSD()) 
 	{ 
 		T* instance = INSTANCE_ADAPTOR::getInstance(seed); 
 		INSTANCE_ADAPTOR::hide(instance);
 	}
 
-	static void toggleInstance(const LLSD& seed)
+	static void toggleInstance(const LLSD& seed = LLSD())
 	{
-		if (!INSTANCE_ADAPTOR::instanceVisible(seed))
+		if (INSTANCE_ADAPTOR::instanceVisible(seed))
 		{
-			INSTANCE_ADAPTOR::showInstance(seed);
+			INSTANCE_ADAPTOR::hideInstance(seed);
 		}
 		else
 		{
-			INSTANCE_ADAPTOR::hideInstance(seed);
+			INSTANCE_ADAPTOR::showInstance(seed);
 		}
 	}
 
-	static BOOL instanceVisible(const LLSD& seed)
+	static BOOL instanceVisible(const LLSD& seed = LLSD())
 	{
 		T* instance = INSTANCE_ADAPTOR::findInstance(seed);
 		return instance != NULL && INSTANCE_ADAPTOR::visible(instance);
 	}
 
-	static T* getInstance(const LLSD& seed) 
+	static T* getInstance(const LLSD& seed = LLSD()) 
 	{
 		T* instance = INSTANCE_ADAPTOR::findInstance(seed);
 		if (instance == NULL)
@@ -312,6 +322,7 @@ class LLUIInstanceMgr
 		}
 		return instance;
 	}
+
 };
 
 // Creates a UI singleton by ignoring the identifying parameter
@@ -326,12 +337,12 @@ class LLUISingleton: public LLUIInstanceMgr<T, INSTANCE_ADAPTOR>
 	LLUISingleton() : LLUIInstanceMgr<T, INSTANCE_ADAPTOR>() { sInstance = (T*)this; }
 	~LLUISingleton() { sInstance = NULL; }
 
-	static T* findInstance(const LLSD& seed)
+	static T* findInstance(const LLSD& seed = LLSD())
 	{
 		return sInstance;
 	}
 
-	static T* createInstance(const LLSD& seed)
+	static T* createInstance(const LLSD& seed = LLSD())
 	{
 		if (sInstance == NULL)
 		{
@@ -346,4 +357,24 @@ class LLUISingleton: public LLUIInstanceMgr<T, INSTANCE_ADAPTOR>
 
 template <class T, class U> T* LLUISingleton<T,U>::sInstance = NULL;
 
+class LLClipRect
+{
+public:
+	LLClipRect(const LLRect& rect, BOOL enabled = TRUE);
+	virtual ~LLClipRect();
+protected:
+	LLGLState		mScissorState;
+	BOOL			mEnabled;
+};
+
+class LLLocalClipRect
+{
+public:
+	LLLocalClipRect(const LLRect& rect, BOOL enabled = TRUE);
+	virtual ~LLLocalClipRect();
+protected:
+	LLGLState		mScissorState;
+	BOOL			mEnabled;
+};
+
 #endif
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index abf796fde0bee5eb34d2fbc07af1c38e33510af3..46bb977f7ea9e456c57b116f0d28a5d98b51c3e8 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -236,7 +236,7 @@ class DefaultTabGroupFirstSorter : public LLQuerySorter, public LLSingleton<Defa
 	/*virtual*/ void operator() (LLView * parent, viewList_t &children) const
 	{
 		children.sort(CompareByDefaultTabGroup(parent->getCtrlOrder(), parent->getDefaultTabGroup()));
-	}		
+	}
 };
 
 BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields)
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index 1d149b63e2a137b4a5a416b790eea4efa4e71179..b4551c1852d88f2770d07ca77ca6202eb2453a1f 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -69,7 +69,7 @@ const LLString LLUICtrlFactory::sUICtrlNames[WIDGET_TYPE_COUNT] =
 	LLString("web_browser"),	//WIDGET_TYPE_WEBBROWSER
 	LLString("slider"),		//WIDGET_TYPE_SLIDER, actually LLSliderCtrl
 	LLString("slider_bar"), //WIDGET_TYPE_SLIDER_BAR, actually LLSlider
-	LLString("volume_slider"),	//WIDGET_TYPE_VOLUME_SLIDER, actually LLVolumeSliderCtrl
+	LLString("volume_slider"),	//WIDGET_TYPE_VOLUME_SLIDER, actually LLSlider + "volume" param
 	LLString("spinner"),		//WIDGET_TYPE_SPINNER, actually LLSpinCtrl
 	LLString("text_editor"),	//WIDGET_TYPE_TEXT_EDITOR
 	LLString("texture_picker"),//WIDGET_TYPE_TEXTURE_PICKER
@@ -135,6 +135,7 @@ const LLString LLUICtrlFactory::sUICtrlNames[WIDGET_TYPE_COUNT] =
 	LLString("texture_view"), //WIDGET_TYPE_TEXTURE_VIEW
 	LLString("memory_view"), //WIDGET_TYPE_MEMORY_VIEW
 	LLString("frame_stat_view"), //WIDGET_TYPE_FRAME_STAT_VIEW
+	LLString("layout_stack"), //WIDGET_TYPE_LAYOUT_STACK
 	LLString("DONT_CARE"),	//WIDGET_TYPE_DONTCARE
 };
 
@@ -177,6 +178,7 @@ LLUICtrlFactory::LLUICtrlFactory()
 	LLUICtrlCreator<LLScrollListCtrl>::registerCreator(LL_SCROLL_LIST_CTRL_TAG, this);
 	LLUICtrlCreator<LLSliderCtrl>::registerCreator(LL_SLIDER_CTRL_TAG, this);
 	LLUICtrlCreator<LLSlider>::registerCreator(LL_SLIDER_TAG, this);
+	LLUICtrlCreator<LLSlider>::registerCreator(LL_VOLUME_SLIDER_CTRL_TAG, this);
 	LLUICtrlCreator<LLSpinCtrl>::registerCreator(LL_SPIN_CTRL_TAG, this);
 	LLUICtrlCreator<LLTextBox>::registerCreator(LL_TEXT_BOX_TAG, this);
 	LLUICtrlCreator<LLRadioGroup>::registerCreator(LL_RADIO_GROUP_TAG, this);
@@ -190,6 +192,7 @@ LLUICtrlFactory::LLUICtrlFactory()
 	LLUICtrlCreator<LLMenuGL>::registerCreator(LL_MENU_GL_TAG, this);
 	LLUICtrlCreator<LLMenuBarGL>::registerCreator(LL_MENU_BAR_GL_TAG, this);
 	LLUICtrlCreator<LLScrollingPanelList>::registerCreator(LL_SCROLLING_PANEL_LIST_TAG, this);
+	LLUICtrlCreator<LLLayoutStack>::registerCreator(LL_LAYOUT_STACK_TAG, this);
 
 	setupPaths();
 
@@ -745,6 +748,37 @@ LLScrollingPanelList* LLUICtrlFactory::getScrollingPanelList(LLPanel* panelp, co
 	return (LLScrollingPanelList*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_SCROLLING_PANEL_LIST); 
 }
 
+
+LLCtrlListInterface* LLUICtrlFactory::getListInterfaceByName(LLPanel* panelp, const LLString& name)
+{
+	LLView* viewp = panelp->getCtrlByNameAndType(name, WIDGET_TYPE_DONTCARE);
+	if (viewp && viewp->isCtrl())
+	{
+		return ((LLUICtrl*)viewp)->getListInterface();
+	}
+	return NULL;
+}
+
+LLCtrlSelectionInterface* LLUICtrlFactory::getSelectionInterfaceByName(LLPanel* panelp, const LLString& name)
+{
+	LLView* viewp = panelp->getCtrlByNameAndType(name, WIDGET_TYPE_DONTCARE);
+	if (viewp && viewp->isCtrl())
+	{
+		return ((LLUICtrl*)viewp)->getSelectionInterface();
+	}
+	return NULL;
+}
+
+LLCtrlScrollInterface* LLUICtrlFactory::getScrollInterfaceByName(LLPanel* panelp, const LLString& name)
+{
+	LLView* viewp = panelp->getCtrlByNameAndType(name, WIDGET_TYPE_DONTCARE);
+	if (viewp && viewp->isCtrl())
+	{
+		return ((LLUICtrl*)viewp)->getScrollInterface();
+	}
+	return NULL;
+}
+
 void LLUICtrlFactory::registerCreator(LLString ctrlname, creator_function_t function)
 {
 	LLString::toLower(ctrlname);
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index c7280aa4a2d2c74dab856d80b9d3fad6a9f5fc9a..04a8a83cfabf85e38a15b3cd1fdf8306b5fc8d73 100644
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -45,6 +45,9 @@ class LLWebBrowserCtrl;
 class LLViewBorder;
 class LLColorSwatchCtrl;
 class LLScrollingPanelList;
+class LLCtrlListInterface;
+class LLCtrlSelectionInterface;
+class LLCtrlScrollInterface;
 
 // Widget 
 
@@ -103,6 +106,11 @@ class LLUICtrlFactory
 	static LLMenuItemCallGL*	getMenuItemCallByName(LLPanel* panelp, const LLString& name);
 	static LLScrollingPanelList* getScrollingPanelList(LLPanel* panelp, const LLString& name);
 
+	// interface getters
+	static LLCtrlListInterface* getListInterfaceByName(LLPanel* panelp, const LLString& name);
+	static LLCtrlSelectionInterface* getSelectionInterfaceByName(LLPanel* panelp, const LLString& name);
+	static LLCtrlScrollInterface* getScrollInterfaceByName(LLPanel* panelp, const LLString& name);
+
 	LLPanel* createFactoryPanel(LLString name);
 
 	virtual LLView* createCtrlWidget(LLPanel *parent, LLXMLNodePtr node);
diff --git a/indra/llui/lluistring.cpp b/indra/llui/lluistring.cpp
index 8c5b58715862e80fa21b4d0043fab6fd32b7cd26..900a867164300201e6faaef1da029d1d7c4f23ef 100644
--- a/indra/llui/lluistring.cpp
+++ b/indra/llui/lluistring.cpp
@@ -10,6 +10,9 @@
 
 #include "lluistring.h"
 
+const LLString::format_map_t LLUIString::sNullArgs;
+
+
 // public
 
 LLUIString::LLUIString(const LLString& instring, const LLString::format_map_t& args)
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
index 8c2e3c481c336bb3225bf443033bae1fa97ba8d5..c3113cbe7484243a175acc2a1daccdd5a6267f0e 100644
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -75,6 +75,8 @@ class LLUIString
 	void insert(S32 charidx, const LLWString& wchars);
 	void replace(S32 charidx, llwchar wc);
 	
+	static const LLString::format_map_t sNullArgs;
+
 private:
 	void format();	
 	
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index dbc635830ba78fac34e9433cfd729e8908a0c69a..b87d82653aaa91a93b97f158f1a112b24b7f4f6b 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -305,6 +305,10 @@ void LLView::moveChildToFrontOfTabGroup(LLUICtrl* child)
 
 void LLView::addChild(LLView* child, S32 tab_group)
 {
+	if (mParentView == child) 
+	{
+		llerrs << "Adding view " << child->getName() << " as child of itself" << llendl;
+	}
 	// remove from current parent
 	if (child->mParentView) 
 	{
@@ -328,6 +332,10 @@ void LLView::addChild(LLView* child, S32 tab_group)
 
 void LLView::addChildAtEnd(LLView* child, S32 tab_group)
 {
+	if (mParentView == child) 
+	{
+		llerrs << "Adding view " << child->getName() << " as child of itself" << llendl;
+	}
 	// remove from current parent
 	if (child->mParentView) 
 	{
@@ -732,18 +740,22 @@ void LLView::setEnabled(BOOL enabled)
 // virtual
 void LLView::setVisible(BOOL visible)
 {
-	if( !visible && (gFocusMgr.getTopCtrl() == this) )
-	{
-		gFocusMgr.setTopCtrl( NULL );
-	}
-
 	if ( mVisible != visible )
 	{
-		// tell all children of this view that the visibility may have changed
-		onVisibilityChange ( visible );
-	}
+		if( !visible && (gFocusMgr.getTopCtrl() == this) )
+		{
+			gFocusMgr.setTopCtrl( NULL );
+		}
 
-	mVisible = visible;
+		mVisible = visible;
+
+		// notify children of visibility change if root, or part of visible hierarchy
+		if (!getParent() || getParent()->isInVisibleChain())
+		{
+			// tell all children of this view that the visibility may have changed
+			onVisibilityChange( visible );
+		}
+	}
 }
 
 // virtual
@@ -758,7 +770,7 @@ BOOL LLView::setLabelArg(const LLString& key, const LLString& text)
 	return FALSE;
 }
 
-void LLView::onVisibilityChange ( BOOL curVisibilityIn )
+void LLView::onVisibilityChange ( BOOL new_visibility )
 {
 	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
 	{
@@ -766,7 +778,7 @@ void LLView::onVisibilityChange ( BOOL curVisibilityIn )
 		// only views that are themselves visible will have their overall visibility affected by their ancestors
 		if (viewp->getVisible())
 		{
-			viewp->onVisibilityChange ( curVisibilityIn );
+			viewp->onVisibilityChange ( new_visibility );
 		}
 	}
 }
@@ -1370,64 +1382,61 @@ LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
 
 void LLView::draw()
 {
-	if (getVisible())
+	if (sDebugRects)
 	{
-		if (sDebugRects)
-		{
-			drawDebugRect();
+		drawDebugRect();
 
-			// Check for bogus rectangle
-			if (mRect.mRight <= mRect.mLeft
-				|| mRect.mTop <= mRect.mBottom)
-			{
-				llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
-			}
+		// Check for bogus rectangle
+		if (mRect.mRight <= mRect.mLeft
+			|| mRect.mTop <= mRect.mBottom)
+		{
+			llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
 		}
+	}
 
-		LLRect rootRect = getRootView()->getRect();
-		LLRect screenRect;
+	LLRect rootRect = getRootView()->getRect();
+	LLRect screenRect;
 
-		// draw focused control on top of everything else
-		LLView* focus_view = gFocusMgr.getKeyboardFocus();
-		if (focus_view && focus_view->getParent() != this)
-		{
-			focus_view = NULL;
-		}
+	// draw focused control on top of everything else
+	LLView* focus_view = gFocusMgr.getKeyboardFocus();
+	if (focus_view && focus_view->getParent() != this)
+	{
+		focus_view = NULL;
+	}
 
-		for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter)
-		{
-			LLView *viewp = *child_iter;
-			++sDepth;
+	for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter)
+	{
+		LLView *viewp = *child_iter;
+		++sDepth;
 
-			if (viewp->getVisible() && viewp != focus_view)
+		if (viewp->getVisible() && viewp != focus_view)
+		{
+			// Only draw views that are within the root view
+			localRectToScreen(viewp->getRect(),&screenRect);
+			if ( rootRect.rectInRect(&screenRect) )
 			{
-				// Only draw views that are within the root view
-				localRectToScreen(viewp->getRect(),&screenRect);
-				if ( rootRect.rectInRect(&screenRect) )
+				glMatrixMode(GL_MODELVIEW);
+				LLUI::pushMatrix();
 				{
-					glMatrixMode(GL_MODELVIEW);
-					LLUI::pushMatrix();
-					{
-						LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
-						viewp->draw();
-					}
-					LLUI::popMatrix();
+					LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
+					viewp->draw();
 				}
+				LLUI::popMatrix();
 			}
-
-			--sDepth;
 		}
 
-		if (focus_view && focus_view->getVisible())
-		{
-			drawChild(focus_view);
-		}
+		--sDepth;
+	}
 
-		// HACK
-		if (sEditingUI && this == sEditingUIView)
-		{
-			drawDebugRect();
-		}
+	if (focus_view && focus_view->getVisible())
+	{
+		drawChild(focus_view);
+	}
+
+	// HACK
+	if (sEditingUI && this == sEditingUIView)
+	{
+		drawDebugRect();
 	}
 }
 
@@ -1480,13 +1489,13 @@ void LLView::drawDebugRect()
 	}
 }
 
-void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset)
+void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_draw)
 {
 	if (childp && childp->getParent() == this)
 	{
 		++sDepth;
 
-		if (childp->getVisible())
+		if (childp->getVisible() || force_draw)
 		{
 			glMatrixMode(GL_MODELVIEW);
 			LLUI::pushMatrix();
@@ -1616,7 +1625,7 @@ void LLView::updateRect()
 			LLView* viewp = *child_it;
 			if (viewp->getVisible())
 			{
-				child_spanning_rect |=  viewp->mRect;
+				child_spanning_rect.unionWith(viewp->mRect);
 			}
 		}
 
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index f9875e8ccaf61b36e7cb877a7d67d774d66091b9..18f453f62171fc6b7459a519a34ba5320fbc61e4 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -359,7 +359,7 @@ class LLView : public LLMouseHandler, public LLMortician, public LLSimpleListene
 	virtual void	draw();
 
 	void			drawDebugRect();
-	void			drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0);
+	void			drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE);
 
 	virtual const LLString&	getName() const;
 
diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp
index 91bf24ec11afeba22b85464701083fa24a50033f..ecef6e6b77fb17e7393b8f77d5a3c691febc6b6e 100644
--- a/indra/mac_updater/mac_updater.cpp
+++ b/indra/mac_updater/mac_updater.cpp
@@ -971,10 +971,12 @@ void *updatethreadproc(void*)
 			if(len < sizeof(temp)-1)
 			{
 				// End of file or error.
-				if(pclose(mounter) != 0)
+				int result = pclose(mounter);
+				if(result != 0)
 				{
-					llinfos << "Failed to mount disk image, exiting."<< llendl;
-					throw 0;
+					// NOTE: We used to abort here, but pclose() started returning 
+					// -1, possibly when the size of the DMG passed a certain point 
+					llinfos << "Unexpected result closing pipe: " << result << llendl; 
 				}
 				mounter = NULL;
 			}
@@ -1000,6 +1002,7 @@ void *updatethreadproc(void*)
 		else
 		{
 			llinfos << "Disk image device node not found!" << llendl;
+			throw 0; 
 		}
 		
 		// Get an FSRef to the new application on the disk image
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
index 5991f9150f31b8aeabaa69bdb83f562205dd1f24..aa9b6c054a3b5d6e0b29d0c7160d055517fec9e4 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 9bef82e0bcfeb6088018fa3a5f6c42e31ccdb5bf..91d1849688dc1ab8333cc00f8c6e09b4d63b4a91 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 45f0b448e291e6f097c5b8065fce2b4096165360..7ed11c11540d680669792446fc84c726d3215583 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 c6d11b5ae555a9faa50f70911615b9b73ef1aab1..438239e7175aefe77a4fe6491176b865f18cd5d3 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -170,7 +170,7 @@ class LLAgent : public LLObservable
 	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 86cc1e206e8ff5208714dda27c33e73dd6138109..9d83e5d80eb9b57c3a43722a1e4bb04e7f2931f4 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 e93ce8bdff9df0955fbcdac4b8ffc1fab43661a8..37bdbbfee5ef176989282db710d13c1e0b2baf7e 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 1a65903dcdf6b56fd7bc304a761f78b90fe9929a..f5f0691dc6efe5a2191560c25148ed065f265099 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 @@ class LLChatBarGestureObserver : public LLGestureManagerObserver
 // 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 65724a1f4570528ce28085772bd7603501bd8d52..33198f5fe671a7a08ccb45ce6c189f243b08d905 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 @@ class LLChatBar
 	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 @@ class LLChatBar
 	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 @@ class LLChatBar
 	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 41f8f1d714389c620adf3be2cd4c2f713ba37c00..7973e4d952a50276a0bac600b0e4eb070163a0b1 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 999dce12967b1c4b250f196e97c80027799ca556..4b44c5aef9827249630f45b44dcb58368b7b7a06 100644
--- a/indra/newview/llcolorswatch.h
+++ b/indra/newview/llcolorswatch.h
@@ -68,8 +68,6 @@ class LLColorSwatchCtrl
 	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 c62459f4616c0129257f8e1c1227b2af7d1ece13..d3e7f08a95a1d3237ed658620dfdebe80668920f 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 d20c431987c78bcec9a6a35f0bdb045237928fdc..4e0cc94a262e3f3cdf696e10938b301c716b835e 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 c330d0bfc0a4433134a960c1d4c0233a24863f17..ede07b6db19b3b24f2bdc4edf6586b30ba58ad08 100644
--- a/indra/newview/llemote.h
+++ b/indra/newview/llemote.h
@@ -88,6 +88,8 @@ class LLEmote :
 	// 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 f5e6545369b2b3c6bb5f2216abbeadce2363e0d1..7441d646efd0b549a35700cf20edb2f5d8393328 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 9f05e59ac8aabf2d8a5ce16a3a044626d274f18b..2982dba7b201c5815c0ff1711b37e40f57c5c26f 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 134699d1ec12ae17a225e73005a739681b218cc6..977ff7ddeb4041d3ef9a49baca1b57785221ae8d 100644
--- a/indra/newview/llfirstuse.h
+++ b/indra/newview/llfirstuse.h
@@ -81,6 +81,7 @@ class LLFirstUse
 	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 708540799942ad43f1755fe7abd7999cdf758b97..edbfa4244c1230ae463092f33c654d4b0a3516f1 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 b541b279d0374179dc83677682b997d0504a1dea..0ba0ab89b1111244d46d2980d4091cc4f2e4a3c8 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 f4769cc18ff9c6568868ced3359bbb08716092fd..6e76db4270c8b7c7bdff5a3efbbfe1ff65b1736f 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)
@@ -278,30 +246,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)
 {
@@ -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 bf93afb455ef0ab2391083a6fb3cdd548c4ac43d..6da3b9bb1bd611c185de81caeeb8a4682a9de2ae 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 @@ class LLFloaterChat
 	// 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 0000000000000000000000000000000000000000..ab992bdb4563f2fd911df3a9312c49b546a52072
--- /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 0000000000000000000000000000000000000000..38cc7d6e4b40ddde9d89f748e7fe4db57201c0b9
--- /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 7e0a64c42005dd80f5b59d8e307b03f786835218..592cb4186c67d35aec9b5bb34b40795fc92e545d 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 c6ef365517c4ff3b15cccc759dea6c51367beddd..6895e469e514282dc29711ab2d1eb6401570769b 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 @@ class LLFloaterFriends : public LLFloater, public LLEventTimer
 
 	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 @@ class LLFloaterFriends : public LLFloater, public LLEventTimer
 	};
 
 	// 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 @@ class LLFloaterFriends : public LLFloater, public LLEventTimer
 	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 @@ class LLFloaterFriends : public LLFloater, public LLEventTimer
 	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 4535a11ec38501032c02092cc7519fbd3f114b3e..60cfa3b809d181882814ab12e06fcc9acec70ee7 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 6f73c975531d4117f57f908b32c112cf6e32badb..65bed95ea84c94632a647f5393d7bacbea013376 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 4b18c0c91a8b1e035549a8f17d3e588ce9046817..421066688ebf65cbb8f0c32aa2b81072d4a02940 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 6c0fb7319f97e12a689fbecaec36de1b9a7ee76f..ab7ff3921cf6213ff29f956b3c79147721ff8a1b 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 @@ class LLPanelLandMedia
 
 protected:
 	LLCheckBoxCtrl* mCheckSoundLocal;
+	LLRadioGroup*	mRadioVoiceChat;
 	LLLineEditor*	mMusicURLEdit;
 	LLLineEditor*	mMediaURLEdit;
 	LLTextureCtrl*	mMediaTextureCtrl;
diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp
index 602a198a03584e70c5ecc9346574cee75c46923c..2553c36f660c962a1f548ab699e537601a3a1f34 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 3eaff0101c1eb735d2464e803caa550d824be372..e44dfe7d85d8efaaf661b6887da30d3b3e57b533 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 8a1a0dcaaae8321429e086efc94607f405c780c7..ef68e5bec2f58ed0eccc38e74e3fee26085dcb9d 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 dcfa123137cc78b45c586f77acbd5b28a7a6c720..1de960c64047e30c169a65b2de67992800c83a38 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 @@ class LLPreferenceCore
 	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 1c287a187ad6b6a41fd6d1255803e3855f227392..efbf8a7826a6146506f31588e94917a7bc9d03b1 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 0ba75b71f645e79aaeb03837a5bdf784eb8eebaf..4d2f01239657c16625c004facf1a93bf3691dc61 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 80e32f9c00de2c0498951df757f6b184b589a518..5feb8b2f0dd6839308be4f03f52dac5a635e67eb 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 b708a4261f5c106d0d1d40db969fcf1c6334394d..0da0958c9b79f9d3bac4aa4e1dccbf29313b5490 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 7c854445788ccf26016197c8813581e149a1da0a..156f76845942e06a2202a8d943c985be1ad388d7 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 @@ class LLFloaterTools
 	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 be1c26381b1dd247384298f7c35f1321faa384b9..3c51d7dcf19f86d03590cdae3fbc3442ebdcce9d 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 b79dc0687fad8f5ed4d31e7360fb09fa12905628..c68ca4f265c5e8d2a9a2891a0d03d8045267e2fa 100644
--- a/indra/newview/llgesturemgr.h
+++ b/indra/newview/llgesturemgr.h
@@ -85,7 +85,7 @@ class LLGestureManager
 	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 1c8c45a078dcc591a6f3deb18a071d538eb2788d..0bcbbbb140b2e918b28f96c2d0801f335b0e3b0f 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 9bd14796d57e64f00f65cf3c0a19f119764d70c2..3b5589c05b5e5cdad081c3122525547e12c75be3 100644
--- a/indra/newview/llhudobject.h
+++ b/indra/newview/llhudobject.h
@@ -68,7 +68,8 @@ class LLHUDObject : public LLRefCount
 		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 aba3401d3ce383e7fcf81e16bcc0901a0cd666e3..70065c48d6fba6cf45734e648bb9b7cd4f044a5c 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 015f90a1801e582c81dd083672b67fe1e7f131b2..3a873bc1a80fc476682c56bf75423ecae04d7af0 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 @@ class LLSessionInviteResponder : public LLHTTPClient::Responder
 
 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) );
 }
 
 
@@ -599,6 +1398,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 )
 {
@@ -609,6 +1417,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 )
 {
@@ -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 cb8042e0108902207ed0119bb69e5977d852322d..dbe09fb396f7e80d09374635058cf63a75e4286f 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 @@ class LLFloaterIMPanel : public LLFloater
 					 const LLUUID& target_id,
 					 const LLDynamicArray<LLUUID>& ids,
 					 EInstantMessage dialog);
-
+	virtual ~LLFloaterIMPanel();
 
 	/*virtual*/ BOOL postBuild();
 
@@ -53,6 +160,10 @@ class LLFloaterIMPanel : public LLFloater
 	// 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 @@ class LLFloaterIMPanel : public LLFloater
 	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 @@ class LLFloaterIMPanel : public LLFloater
 	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 @@ class LLFloaterIMPanel : public LLFloater
 	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 @@ class LLFloaterIMPanel : public LLFloater
 	//   911 ==> Gaurdian_Angel_Group_ID ^ gAgent.getID()
 	LLUUID mSessionUUID;
 
-	BOOL mSessionInitRequested;
+	LLVoiceChannel*	mVoiceChannel;
+
 	BOOL mSessionInitialized;
 	LLSD mQueuedMsgsForInit;
 
@@ -150,10 +279,17 @@ class LLFloaterIMPanel : public LLFloater
 
 	// 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 5a6cbb045dc51b9b758bb894b8109fd049f37df9..d8643ab9c17cb79bcdbc5f6eb9fac1c6e3ca6798 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 @@ class LLIMViewFriendObserver : public LLFriendObserver
 		}
 	}
 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 @@ class LLIMViewFriendObserver : public LLFriendObserver
 // 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 @@ class LLViewerIMSessionStartReply : public LLHTTPNode
 		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 @@ class LLViewerIMSessionStartReply : public LLHTTPNode
 					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 @@ class LLViewerIMSessionEventReply : public LLHTTPNode
 		{
 			//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 @@ class LLViewerIMSessionEventReply : public LLHTTPNode
 					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 @@ class LLViewerForceCloseIMSession: public LLHTTPNode
 		reason = input["body"]["reason"].asString();
 
 		LLFloaterIMPanel* floater =
-			gIMView ->findFloaterBySession(session_id);
+			gIMMgr ->findFloaterBySession(session_id);
 
 		if ( floater )
 		{
@@ -957,7 +1146,7 @@ class LLViewerForceCloseIMSession: public LLHTTPNode
 			args["[NAME]"] = floater->getTitle();
 			args["[REASON]"] = sForceCloseSessionMap[reason];
 
-			gViewerWindow->alertXml("ForceCloseIMSession",
+			gViewerWindow->alertXml("ForceCloseChatterBoxSession",
 									args,
 									onConfirmForceCloseError,
 									floater);
@@ -965,28 +1154,6 @@ class LLViewerForceCloseIMSession: public LLHTTPNode
 	}
 };
 
-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 @@ class LLViewerChatterBoxSessionAgentListUpdates : public LLHTTPNode
 		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 @@ class LLViewerChatterBoxInvitation : public LLHTTPNode
 				(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 @@ class LLViewerChatterBoxInvitation : public LLHTTPNode
 			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 @@ class LLViewerChatterBoxInvitation : public LLHTTPNode
 			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 @@ class LLViewerChatterBoxInvitation : public LLHTTPNode
 			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 @@ class LLViewerChatterBoxInvitation : public LLHTTPNode
 				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 aac6fd63cefea28f0a6db9818645daf6297fc9fe..f8a36107d690352f7edbecf67b5eea5bcfff2d26 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 @@ class LLIMView : public LLView
 					  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 @@ class LLIMView : public LLView
 	void refresh();
 
 	void notifyNewIM();
+	void clearNewIMNotification();
 
 	// IM received that you haven't seen yet
 	BOOL getIMReceived() const;
@@ -87,10 +101,7 @@ class LLIMView : public LLView
 	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 @@ class LLIMView : public LLView
 	// 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 @@ class LLIMView : public LLView
 
 	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 @@ class LLIMView : public LLView
 	// 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 4a1d496d6f2e6fe0c6f82ca724c908bf53f84664..8a08bd2a4b78a2e14a858ed634e61d514d996352 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 f7aa4d4251f045e8d6fdd5a20b4f45f1b6147847..793571e111c810682bfe702445ded433af0966c7 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 8b07dfbfeb6dda17cc85d5245620f1df98b1e174..62c7d40a90fe8a1cb3794c71744f8cbb0d29e088 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 7f9b1b3bf67585235f503837724349cc764ee514..f035e4c2b1bf8fbcb94fcca0a90d892427783a3b 100644
--- a/indra/newview/llmutelist.h
+++ b/indra/newview/llmutelist.h
@@ -23,8 +23,22 @@ class LLMute
 	// 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 @@ class LLMute
 	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 @@ class LLMuteList
 	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 4fde8988f586a32103017376c3a8cf96b15a55ae..754c0962969d318f906d51b3173f2fed9670e314 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 8e36297c937f0a95f5564384c7884ac9c1d8996d..0847864be028b69d1553766104576b2f0ddf11b2 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 78f544df570eddb3869267b9e4dfab696af29435..d7fff6ba1ed4ed68127cee7501b759c0691ab98d 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 @@ class LLOverlayBar
 	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 @@ class LLOverlayBar
 	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 2567684fa9fe968c7a303de34de57a204ccfd932..ab2e30014d2a54a1a0d20b8b3c8aef21fa9e9cc5 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 23f7a4aba60648dda57346119f20a80fffa3e509..7a288a173048a2f88c4efe1f031903c211557242 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 c3d4551032fbbf4dc61e3e88b28cd05e5a7ce462..e32a64eb8e086b2aa4ad94b3086234b7218f6d92 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 da7fc16cef5ede6a589c146a263d3065c8203229..d0061e281e9edd84cf5023c0b169951166dda80d 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 c4f958420b9c86df4d673837ed80dc40aca71c2d..c1885ad898bc305120310c8a9654a6e94700d412 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 2d4a6d11d94338bdf00f8b2b435418de3c026b98..0c112267109be20a5af5ee96e6a39b3c528cf5a2 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 7df0e48762cb0d759cb5e2ba7142baf55a633ec7..22b47eb8ca253c10a173a963d54523c3b13ff32f 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 974ee274768767260e0087fc95442fe7a30ac7d0..2462bb7bdf3e55e801695df21ac7894f58c411d4 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 117f0207932528e2e7a801e8785a777a0651f594..20761ded1188d2fa425a236052ef533d52408f71 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 bc27d9af9f8d0aec7fec86a9c9b496b2cdb927f1..3df559dee5890934062e3a1de9b5b595c63ba43e 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 94f758f054c49cb69821c2c09f3b943684cc771b..2bbf2bab2dcd6583aebcb2d70218c444a4255913 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -61,19 +61,7 @@ class LLStatusBar
 	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 @@ class LLStatusBar
 	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 e6cfefa62d935e3dfc4952c1baaa73311c84641b..879e99973dd9408a36c7214203979317eb60c312 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 f9eee1d4fbe5beaa5bbbb094c78bca27e4d6b717..b6e6a073e4a324e3f38c35ac0faaac71a62db812 100644
--- a/indra/newview/lltoolbar.h
+++ b/indra/newview/lltoolbar.h
@@ -47,9 +47,8 @@ class LLToolBar
 	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 930d6fa5f24a28ca65aa4e6f0d6455d760f72ca0..72a8cefdbd6e34095ead2ef23b34bcad70006851 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 2203df428788f93dd93c87df4e7c5ccc0756d1bf..c93b62c75fe9bc60a778866545aacbc1a6189c43 100644
--- a/indra/newview/llviewercontrol.h
+++ b/indra/newview/llviewercontrol.h
@@ -34,9 +34,11 @@ class LLFloaterSettingsDebug : public LLFloater
 	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 bc0be1b5a2616b66afd52a276e34723d88d05400..abcdb8cdbd5e2ef3f3e5b395c5135102e82976d2 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 6a9b5a8031ba0469f19207b136f0fef69a95d719..7cf9ca956861024c4903047da82812bf6e380859 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 e53f7794739781f33d9ee6088eeb0d6a9c7079d8..bbb69594da90d8e74e1255ad4b825238bc3c619c 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 f6aaadd40bf59538af4ae842eb2055ff81cf1188..f52466cc2d6e896ee5d9609e7ec37f16c808cfe9 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -544,8 +544,9 @@ class LLViewerObject : public LLPrimitive, public LLRefCount
 	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 ed97cd3ec24b9b8d1fdfc08e7182542b4841c1d3..017641b2788b892c88686aee60c7d4d0cf2096a5 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 4040a6d21b4cc91251da685b04e2fe30887c7f00..c94aec880113d69677675f4982eeb59b0767d3be 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 39692301977631640db5e9c770620881c9f1559e..2fff820602562a1eb5fa82d41b7ea87a45bbebf3 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 dd2c1c6d76dc9bc7f7ea6404ec6df2dc525458e7..dbce01246f0603944a9136d1e28e92d4159cbff7 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 91bf0d9918c186db712b7eea9c21d3c2e8dd8ed8..bb60c8283efd92b62df70048ee26940a80d8f952 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -65,6 +65,8 @@ class LLViewerWindow : public LLWindowCallbacks
 	/*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 1af8efaf793cd74ab43f2df5a7a82faf24042b86..60cce34b72cf1ab133e28b99c54896673449f38a 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 @@ class LLBreatheMotionRot :
 	// 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 @@ class LLBreatheMotionRot :
 		{
 			return STATUS_FAILURE;
 		}
-		//end Ventrella
 	}
 
 	// called when a motion is activated
@@ -579,37 +473,9 @@ class LLBreatheMotionRot :
 
 		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 @@ class LLBreatheMotionRot :
 	//-------------------------------------------------------------------------
 	// 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 c98826d15b43557b46a9f1081ec3c5f5ad87726e..0eac13206bdcbc752dfd335a5e521cc167bcfd36 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 @@ class LLVOAvatar :
 	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 0000000000000000000000000000000000000000..0dba3588e5683e84f55611f24f84d86f39328568
--- /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 0000000000000000000000000000000000000000..d45b113e630fc21ddd21cea9d8e4eb24ff541cb5
--- /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 0000000000000000000000000000000000000000..fb3fd8af15b6fde9f4ae9f4dd626e881699885de
--- /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 0000000000000000000000000000000000000000..bd54bd435b5c40483e8002d44c4116855289f006
--- /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 a126237e00c498c35facc44fd59284fb27f8ffee..57b046c08253c0cb6a33862d7aa412a0d97d8042 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 6c99d997327f641d6fe3a6bba523591dc4670f17..ab95b9eda6dfe90ca2ce3c5f6413fd05193f7e3e 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 c93b302312f792f3e7bbf2904927562957a9021e..9136a06c24557cccf4940779eff57ecbda67071c 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -69,25 +69,42 @@ def construct(self):
                 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 @@ def replace_login_url(self):
 
 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 @@ def construct(self):
                         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 @@ def wpath(path):
                 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 @@ def construct(self):
 
 
         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 @@ def package_finish(self):
                 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))