summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llcharacter/llmotion.cpp11
-rw-r--r--indra/llcharacter/llmotion.h8
-rw-r--r--indra/llcharacter/llmotioncontroller.cpp157
-rw-r--r--indra/llcharacter/llmotioncontroller.h15
-rw-r--r--indra/llcharacter/llpose.cpp20
-rw-r--r--indra/llcharacter/llpose.h2
-rw-r--r--indra/llcommon/lltimer.cpp6
-rw-r--r--indra/llcommon/lltimer.h2
-rw-r--r--indra/llmath/llrect.h47
-rw-r--r--indra/llui/llbutton.cpp14
-rw-r--r--indra/llui/llbutton.h3
-rw-r--r--indra/llui/llctrlselectioninterface.h3
-rw-r--r--indra/llui/llfloater.cpp180
-rw-r--r--indra/llui/llfloater.h10
-rw-r--r--indra/llui/llfocusmgr.cpp9
-rw-r--r--indra/llui/llfocusmgr.h1
-rw-r--r--indra/llui/llmenugl.cpp4
-rw-r--r--indra/llui/llpanel.cpp659
-rw-r--r--indra/llui/llpanel.h58
-rw-r--r--indra/llui/llradiogroup.cpp66
-rw-r--r--indra/llui/llradiogroup.h21
-rw-r--r--indra/llui/llresizebar.cpp86
-rw-r--r--indra/llui/llresizebar.h11
-rw-r--r--indra/llui/llscrollcontainer.cpp3
-rw-r--r--indra/llui/llscrolllistctrl.cpp55
-rw-r--r--indra/llui/llscrolllistctrl.h1
-rw-r--r--indra/llui/llslider.cpp158
-rw-r--r--indra/llui/llslider.h6
-rw-r--r--indra/llui/llsliderctrl.cpp10
-rw-r--r--indra/llui/llsliderctrl.h4
-rw-r--r--indra/llui/lltabcontainer.cpp114
-rw-r--r--indra/llui/lltabcontainer.h11
-rw-r--r--indra/llui/lltexteditor.cpp3
-rw-r--r--indra/llui/llui.cpp60
-rw-r--r--indra/llui/llui.h57
-rw-r--r--indra/llui/lluictrl.cpp2
-rw-r--r--indra/llui/lluictrlfactory.cpp36
-rw-r--r--indra/llui/lluictrlfactory.h8
-rw-r--r--indra/llui/lluistring.cpp3
-rw-r--r--indra/llui/lluistring.h2
-rw-r--r--indra/llui/llview.cpp125
-rw-r--r--indra/llui/llview.h2
-rw-r--r--indra/mac_updater/mac_updater.cpp9
-rw-r--r--indra/newview/English.lproj/InfoPlist.strings4
-rw-r--r--indra/newview/Info-SecondLife.plist2
-rw-r--r--indra/newview/llagent.cpp60
-rw-r--r--indra/newview/llagent.h2
-rw-r--r--indra/newview/llaudiosourcevo.cpp2
-rw-r--r--indra/newview/llcallingcard.cpp1
-rw-r--r--indra/newview/llchatbar.cpp155
-rw-r--r--indra/newview/llchatbar.h21
-rw-r--r--indra/newview/llcolorswatch.cpp15
-rw-r--r--indra/newview/llcolorswatch.h2
-rw-r--r--indra/newview/lldebugmessagebox.cpp10
-rw-r--r--indra/newview/lldebugview.cpp14
-rw-r--r--indra/newview/llemote.h2
-rw-r--r--indra/newview/llfasttimerview.cpp3
-rw-r--r--indra/newview/llfirstuse.cpp13
-rw-r--r--indra/newview/llfirstuse.h1
-rw-r--r--indra/newview/llfloateranimpreview.cpp2
-rw-r--r--indra/newview/llfloateravatarpicker.cpp7
-rw-r--r--indra/newview/llfloaterchat.cpp213
-rw-r--r--indra/newview/llfloaterchat.h25
-rw-r--r--indra/newview/llfloaterchatterbox.cpp322
-rw-r--r--indra/newview/llfloaterchatterbox.h67
-rw-r--r--indra/newview/llfloaterfriends.cpp397
-rw-r--r--indra/newview/llfloaterfriends.h26
-rw-r--r--indra/newview/llfloatergroups.cpp477
-rw-r--r--indra/newview/llfloatergroups.h79
-rw-r--r--indra/newview/llfloaterland.cpp69
-rw-r--r--indra/newview/llfloaterland.h2
-rw-r--r--indra/newview/llfloaternamedesc.cpp4
-rw-r--r--indra/newview/llfloaterpostcard.cpp2
-rw-r--r--indra/newview/llfloaterpreference.cpp10
-rw-r--r--indra/newview/llfloaterpreference.h2
-rw-r--r--indra/newview/llfloaterregioninfo.cpp26
-rw-r--r--indra/newview/llfloaterscriptdebug.cpp2
-rw-r--r--indra/newview/llfloatersnapshot.cpp3
-rw-r--r--indra/newview/llfloatertools.cpp11
-rw-r--r--indra/newview/llfloatertools.h2
-rw-r--r--indra/newview/llgesturemgr.cpp63
-rw-r--r--indra/newview/llgesturemgr.h2
-rw-r--r--indra/newview/llhudobject.cpp4
-rw-r--r--indra/newview/llhudobject.h3
-rw-r--r--indra/newview/llhudtext.cpp3
-rw-r--r--indra/newview/llimpanel.cpp990
-rw-r--r--indra/newview/llimpanel.h150
-rw-r--r--indra/newview/llimview.cpp822
-rw-r--r--indra/newview/llimview.h57
-rw-r--r--indra/newview/llinventorybridge.cpp9
-rw-r--r--indra/newview/llinventorymodel.cpp9
-rw-r--r--indra/newview/llmutelist.cpp124
-rw-r--r--indra/newview/llmutelist.h32
-rw-r--r--indra/newview/llnetmap.cpp2
-rw-r--r--indra/newview/lloverlaybar.cpp457
-rw-r--r--indra/newview/lloverlaybar.h45
-rw-r--r--indra/newview/llpanelavatar.cpp9
-rw-r--r--indra/newview/llpanelgroup.cpp11
-rw-r--r--indra/newview/llpanellogin.cpp32
-rw-r--r--indra/newview/llpanelpermissions.cpp16
-rw-r--r--indra/newview/llpreview.cpp3
-rw-r--r--indra/newview/llpreviewscript.cpp5
-rw-r--r--indra/newview/llpreviewsound.cpp15
-rw-r--r--indra/newview/llpreviewtexture.cpp2
-rw-r--r--indra/newview/llstartup.cpp30
-rw-r--r--indra/newview/llstatusbar.cpp110
-rw-r--r--indra/newview/llstatusbar.h23
-rw-r--r--indra/newview/lltoolbar.cpp35
-rw-r--r--indra/newview/lltoolbar.h3
-rw-r--r--indra/newview/lltooldraganddrop.cpp2
-rw-r--r--indra/newview/llviewercontrol.h2
-rw-r--r--indra/newview/llviewermenu.cpp43
-rw-r--r--indra/newview/llviewermessage.cpp71
-rw-r--r--indra/newview/llviewerobject.cpp23
-rw-r--r--indra/newview/llviewerobject.h5
-rw-r--r--indra/newview/llviewerparcelmgr.cpp16
-rw-r--r--indra/newview/llviewerregion.cpp3
-rw-r--r--indra/newview/llviewertexteditor.cpp17
-rw-r--r--indra/newview/llviewerwindow.cpp69
-rw-r--r--indra/newview/llviewerwindow.h2
-rw-r--r--indra/newview/llvoavatar.cpp313
-rw-r--r--indra/newview/llvoavatar.h16
-rw-r--r--indra/newview/llvoiceclient.cpp4056
-rw-r--r--indra/newview/llvoiceclient.h503
-rw-r--r--indra/newview/llvoicevisualizer.cpp439
-rw-r--r--indra/newview/llvoicevisualizer.h119
-rw-r--r--indra/newview/llvovolume.cpp4
-rw-r--r--indra/newview/llworldmapview.cpp3
-rwxr-xr-xindra/newview/viewer_manifest.py185
129 files changed, 10606 insertions, 2468 deletions
diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp
index a53956223c..73aefa6640 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 66882a2c11..77edba667f 100644
--- a/indra/llcharacter/llmotion.h
+++ b/indra/llcharacter/llmotion.h
@@ -79,13 +79,14 @@ public:
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 @@ public:
// 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 592a6bae2c..bef5ea5dbb 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 19bab09a11..d5ea568479 100644
--- a/indra/llcharacter/llmotioncontroller.h
+++ b/indra/llcharacter/llmotioncontroller.h
@@ -159,16 +159,21 @@ public:
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 @@ protected:
// 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 e1b24d62cb..1f6d208b84 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 5d17bd8458..b3cc994960 100644
--- a/indra/llcharacter/llpose.h
+++ b/indra/llcharacter/llpose.h
@@ -61,7 +61,7 @@ public:
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 993574b528..f335458f24 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 69e786c50a..575bf9fc0b 100644
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -131,7 +131,7 @@ public:
static void updateClass();
protected:
- LLTimer mTimer;
+ LLTimer mEventTimer;
F32 mPeriod;
private:
diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h
index 92e415155b..12f702a197 100644
--- a/indra/llmath/llrect.h
+++ b/indra/llmath/llrect.h
@@ -193,47 +193,28 @@ public:
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 d93748d069..bdf1c12b79 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 ba77220a7b..a638b5be49 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -124,7 +124,7 @@ public:
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 @@ protected:
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 698d609593..9df7475a6c 100644
--- a/indra/llui/llctrlselectioninterface.h
+++ b/indra/llui/llctrlselectioninterface.h
@@ -31,6 +31,8 @@ public:
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 22acf46125..17b4dffa3f 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 1491f75683..942ae34d27 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -123,8 +123,10 @@ public:
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 @@ protected:
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 @@ public:
/*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 @@ public:
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 da53e3d104..5c8eb8b4af 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 dc8025d4c0..e764f5459c 100644
--- a/indra/llui/llfocusmgr.h
+++ b/indra/llui/llfocusmgr.h
@@ -61,6 +61,7 @@ public:
// 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 d7ccfe1daa..52fdf3cd5a 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 37feffd4b0..c130214b99 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 f06797a7f8..aa5f6e314f 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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ protected:
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 @@ protected:
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 69c0da6933..b58ae09b5d 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 01b4a61b82..2a856ee6cf 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 @@ public:
};
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 @@ public:
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 @@ public:
// 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 0128b4ebbc..b3bce755b1 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 7a77cce8a6..cf78879cba 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 @@ public:
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 @@ protected:
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 0e3c73f633..589afb114a 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 1d07d3f36b..e802b3426b 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 793f97dc64..9604b0569c 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -585,6 +585,7 @@ protected:
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 3a01013943..06fd2830a3 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 d88da42064..d9cdf11d5d 100644
--- a/indra/llui/llslider.h
+++ b/indra/llui/llslider.h
@@ -26,6 +26,7 @@ public:
F32 min_value,
F32 max_value,
F32 increment,
+ BOOL volume,
const LLString& control_name = LLString::null );
virtual EWidgetType getWidgetType() const;
@@ -46,6 +47,7 @@ public:
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;}
@@ -59,12 +61,16 @@ public:
virtual void draw();
protected:
+ void setValueAndCommit(F32 value);
+
+protected:
F32 mValue;
F32 mInitialValue;
F32 mMinValue;
F32 mMaxValue;
F32 mIncrement;
+ BOOL mVolumeSlider;
S32 mMouseOffset;
LLRect mDragStartThumbRect;
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index 8b5cd4690e..4ae8c5d222 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 2185e42eb1..0780350462 100644
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -40,6 +40,7 @@ public:
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 @@ private:
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 ef527b32c2..65551e5c48 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 a395fd94af..4665983402 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -67,7 +67,7 @@ public:
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 @@ public:
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 @@ protected:
mOnChangeCallback( cb ),
mUserData( userdata ),
mOldState(FALSE),
- mPlaceholderText(placeholder)
+ mPlaceholderText(placeholder),
+ mPadding(0)
{}
LLTabContainerCommon* mTabContainer;
@@ -145,11 +146,13 @@ protected:
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 @@ public:
/*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 eb59765927..ea5897e28e 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 c65500b56c..696be050ce 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 dbe79338e5..fefe75f43e 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 @@ public:
//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 @@ public:
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 @@ public:
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 @@ public:
}
// 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 @@ public:
}
return instance;
}
+
};
// Creates a UI singleton by ignoring the identifying parameter
@@ -326,12 +337,12 @@ public:
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 @@ protected:
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 abf796fde0..46bb977f7e 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -236,7 +236,7 @@ public:
/*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 1d149b63e2..b4551c1852 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 c7280aa4a2..04a8a83cfa 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 @@ public:
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 8c5b587158..900a867164 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 8c2e3c481c..c3113cbe74 100644
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -75,6 +75,8 @@ public:
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 dbc635830b..b87d82653a 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 f9875e8cca..18f453f621 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -359,7 +359,7 @@ public:
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 91bf24ec11..ecef6e6b77 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 5991f9150f..aa9b6c054a 100644
--- a/indra/newview/English.lproj/InfoPlist.strings
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -1,5 +1,5 @@
/* Localized versions of Info.plist keys */
CFBundleName = "Second Life";
-CFBundleShortVersionString = "Second Life version 1.18.1.1";
-CFBundleGetInfoString = "Second Life version 1.18.1.1, Copyright 2004-2007 Linden Research, Inc.";
+CFBundleShortVersionString = "Second Life version 1.18.1.2";
+CFBundleGetInfoString = "Second Life version 1.18.1.2, Copyright 2004-2007 Linden Research, Inc.";
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index 9bef82e0bc..91d1849688 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
- <string>1.18.1.1</string>
+ <string>1.18.1.2</string>
<key>CSResourcesFileMapped</key>
<true/>
</dict>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 45f0b448e2..7ed11c1154 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -41,6 +41,7 @@
#include "llface.h"
#include "llfirstuse.h"
#include "llfloater.h"
+#include "llfloateractivespeakers.h"
#include "llfloateravatarinfo.h"
#include "llfloaterbuildoptions.h"
#include "llfloaterchat.h"
@@ -97,6 +98,7 @@
#include "pipeline.h"
#include "roles_constants.h"
#include "viewer.h"
+#include "llvoiceclient.h"
// Ventrella
#include "llfollowcam.h"
@@ -326,7 +328,7 @@ LLAgent::LLAgent()
mEffectColor(0.f, 1.f, 1.f, 1.f),
mHaveHomePosition(FALSE),
mHomeRegionHandle( 0 ),
- mNearChatRadius(10.f),
+ mNearChatRadius(CHAT_NORMAL_RADIUS / 2.f),
mGodLevel( GOD_NOT ),
@@ -2824,7 +2826,7 @@ void LLAgent::endAnimationUpdateUI()
gMorphView->setVisible(FALSE);
}
- gIMView->setFloaterOpen( FALSE );
+ gIMMgr->setFloaterOpen( FALSE );
gConsole->setVisible( TRUE );
if (mAvatarObject)
@@ -3239,6 +3241,19 @@ void LLAgent::updateCamera()
setLookAt(LOOKAT_TARGET_FOCUS, NULL, mCameraPositionAgent);
}
+ // Send the camera position to the spatialized voice system.
+ if(gVoiceClient && getRegion())
+ {
+ LLMatrix3 rot;
+ rot.setRows(gCamera->getAtAxis(), gCamera->getLeftAxis (), gCamera->getUpAxis());
+
+ // MBW -- XXX -- Setting velocity to 0 for now. May figure it out later...
+ gVoiceClient->setCameraPosition(
+ getRegion()->getPosGlobalFromRegion(gCamera->getOrigin()),// position
+ LLVector3::zero, // velocity
+ rot); // rotation matrix
+ }
+
// update the travel distance stat
// this isn't directly related to the camera
// but this seemed like the best place to do this
@@ -3249,7 +3264,7 @@ void LLAgent::updateCamera()
mDistanceTraveled += delta.magVec();
}
mLastPositionGlobal = global_pos;
-
+
if (LLVOAvatar::sVisibleInFirstPerson && mAvatarObject.notNull() && !mAvatarObject->mIsSitting && cameraMouselook())
{
LLVector3 head_pos = mAvatarObject->mHeadp->getWorldPosition() +
@@ -4445,18 +4460,23 @@ void LLAgent::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate)
//-----------------------------------------------------------------------------
// heardChat()
//-----------------------------------------------------------------------------
-void LLAgent::heardChat(const LLChat& chat)
+void LLAgent::heardChat(const LLUUID& id)
{
- if (chat.mChatType == CHAT_TYPE_START
- || chat.mChatType == CHAT_TYPE_STOP)
+ // log text and voice chat to speaker mgr
+ // for keeping track of active speakers, etc.
+ gLocalSpeakerMgr->speakerChatted(id);
+
+ // don't respond to your own voice
+ if (id == getID()) return;
+
+ if (ll_rand(2) == 0)
{
- return;
- }
+ LLViewerObject *chatter = gObjectList.findObject(mLastChatterID);
+ setLookAt(LOOKAT_TARGET_AUTO_LISTEN, chatter, LLVector3::zero);
+ }
- mLastChatterID = chat.mFromID;
+ mLastChatterID = id;
mChatTimer.reset();
-
- mNearChatRadius = CHAT_NORMAL_RADIUS / 2.f;
}
//-----------------------------------------------------------------------------
@@ -5048,14 +5068,6 @@ void LLAgent::initOriginGlobal(const LLVector3d &origin_global)
void update_group_floaters(const LLUUID& group_id)
{
- // *HACK: added to do a live update of the groups floater if it is
- // open.
- LLFloaterGroups* fg = LLFloaterGroups::getInstance(gAgent.getID());
- if(fg)
- {
- fg->reset();
- }
-
LLFloaterGroupInfo::refreshGroup(group_id);
// update avatar info
@@ -5065,10 +5077,10 @@ void update_group_floaters(const LLUUID& group_id)
fa->resetGroupList();
}
- if (gIMView)
+ if (gIMMgr)
{
// update the talk view
- gIMView->refresh();
+ gIMMgr->refresh();
}
}
@@ -5159,6 +5171,7 @@ void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **)
if (need_floater_update)
{
update_group_floaters(group.mID);
+ gAgent.fireEvent(new LLEvent(&gAgent, "new group"), "");
}
}
@@ -5488,6 +5501,11 @@ bool LLAgent::teleportCore(bool is_local)
gAgent.setTeleportState( LLAgent::TELEPORT_START );
}
make_ui_sound("UISndTeleportOut");
+
+ // MBW -- Let the voice client know a teleport has begun so it can leave the existing channel.
+ // This was breaking the case of teleporting within a single sim. Backing it out for now.
+// gVoiceClient->leaveChannel();
+
return true;
}
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index c6d11b5ae5..438239e717 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -170,7 +170,7 @@ public:
void setObjectTracking(BOOL track) { mTrackFocusObject = track; }
// void setLookingAtAvatar(BOOL looking);
- void heardChat(const LLChat& chat);
+ void heardChat(const LLUUID& id);
void lookAtLastChat();
LLUUID getLastChatter() { return mLastChatterID; }
F32 getTypingTime() { return mTypingTimer.getElapsedTimeF32(); }
diff --git a/indra/newview/llaudiosourcevo.cpp b/indra/newview/llaudiosourcevo.cpp
index 86cc1e206e..9d83e5d80e 100644
--- a/indra/newview/llaudiosourcevo.cpp
+++ b/indra/newview/llaudiosourcevo.cpp
@@ -63,7 +63,7 @@ void LLAudioSourceVO::updateGain()
{
mute = TRUE;
}
- else if (gMuteListp->isMuted(mOwnerID))
+ else if (gMuteListp->isMuted(mOwnerID, LLMute::flagObjectSounds))
{
mute = TRUE;
}
diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
index e93ce8bdff..37bdbbfee5 100644
--- a/indra/newview/llcallingcard.cpp
+++ b/indra/newview/llcallingcard.cpp
@@ -274,6 +274,7 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds)
<< "]" << llendl;
}
}
+ notifyObservers();
return new_buddy_count;
}
diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
index 1a65903dcd..f5f0691dc6 100644
--- a/indra/newview/llchatbar.cpp
+++ b/indra/newview/llchatbar.cpp
@@ -27,6 +27,7 @@
#include "llgesturemgr.h"
#include "llkeyboard.h"
#include "lllineeditor.h"
+#include "llstatusbar.h"
#include "lltextbox.h"
#include "lluiconstants.h"
#include "llviewergesture.h" // for triggering gestures
@@ -50,15 +51,18 @@ const F32 AGENT_TYPING_TIMEOUT = 5.f; // seconds
LLChatBar *gChatBar = NULL;
-LLChatBarGestureObserver* LLChatBar::sObserver = NULL;
+// legacy calllback glue
+void toggleChatHistory(void* user_data);
class LLChatBarGestureObserver : public LLGestureManagerObserver
{
public:
- LLChatBarGestureObserver() {}
+ LLChatBarGestureObserver(LLChatBar* chat_barp) : mChatBar(chat_barp){}
virtual ~LLChatBarGestureObserver() {}
- virtual void changed() { gChatBar->refreshGestures(); }
+ virtual void changed() { mChatBar->refreshGestures(); }
+private:
+ LLChatBar* mChatBar;
};
@@ -66,12 +70,29 @@ public:
// Functions
//
-LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
+//inline constructor
+// for chat bars embedded in floaters, etc
+LLChatBar::LLChatBar(const std::string& name)
+: LLPanel(name, LLRect(), BORDER_NO),
+ mInputEditor(NULL),
+ mGestureLabelTimer(),
+ mLastSpecialChatChannel(0),
+ mIsBuilt(FALSE),
+ mDynamicLayout(FALSE),
+ mGestureCombo(NULL),
+ mObserver(NULL)
+{
+}
+
+LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
: LLPanel(name, rect, BORDER_NO),
mInputEditor(NULL),
mGestureLabelTimer(),
mLastSpecialChatChannel(0),
- mIsBuilt(FALSE)
+ mIsBuilt(FALSE),
+ mDynamicLayout(TRUE),
+ mGestureCombo(NULL),
+ mObserver(NULL)
{
setIsChrome(TRUE);
@@ -87,29 +108,6 @@ LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
// Start visible if we left the app while chatting.
setVisible( gSavedSettings.getBOOL("ChatVisible") );
- mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor");
- if (mInputEditor)
- {
- mInputEditor->setCallbackUserData(this);
- mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
- mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);
- mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );
- mInputEditor->setCommitOnFocusLost( FALSE );
- mInputEditor->setRevertOnEsc( FALSE );
- mInputEditor->setIgnoreTab(TRUE);
- mInputEditor->setPassDelete(TRUE);
- mInputEditor->setMaxTextLength(1023);
- mInputEditor->setEnableLineHistory(TRUE);
- }
-
- // Build the list of gestures
- refreshGestures();
-
- sObserver = new LLChatBarGestureObserver;
- gGestureManager.addObserver(sObserver);
-
- mIsBuilt = TRUE;
-
// Apply custom layout.
layout();
@@ -122,23 +120,43 @@ LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
LLChatBar::~LLChatBar()
{
- delete sObserver;
- sObserver = NULL;
+ delete mObserver;
+ mObserver = NULL;
// LLView destructor cleans up children
}
BOOL LLChatBar::postBuild()
{
- childSetAction("History", LLFloaterChat::toggle, this);
+ childSetAction("History", toggleChatHistory, this);
childSetAction("Say", onClickSay, this);
childSetAction("Shout", onClickShout, this);
- childSetCommitCallback("Gesture", onCommitGesture, this);
- LLButton * sayp = static_cast<LLButton*>(getChildByName("Say"));
+
+ // attempt to bind to an existing combo box named gesture
+ setGestureCombo(LLUICtrlFactory::getComboBoxByName(this, "Gesture"));
+
+ LLButton * sayp = static_cast<LLButton*>(getChildByName("Say", TRUE));
if(sayp)
{
setDefaultBtn(sayp);
}
+ mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor");
+ if (mInputEditor)
+ {
+ mInputEditor->setCallbackUserData(this);
+ mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
+ mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);
+ mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );
+ mInputEditor->setCommitOnFocusLost( FALSE );
+ mInputEditor->setRevertOnEsc( FALSE );
+ mInputEditor->setIgnoreTab(TRUE);
+ mInputEditor->setPassDelete(TRUE);
+
+ mInputEditor->setMaxTextLength(1023);
+ }
+
+ mIsBuilt = TRUE;
+
return TRUE;
}
@@ -186,7 +204,8 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
handled = TRUE;
}
}
- else if ( KEY_ESCAPE == key )
+ // only do this in main chatbar
+ else if ( KEY_ESCAPE == key && gChatBar == this)
{
stopChat();
@@ -199,6 +218,8 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
void LLChatBar::layout()
{
+ if (!mDynamicLayout) return;
+
S32 rect_width = mRect.getWidth();
S32 count = 9; // number of elements in LLToolBar
S32 pad = 4;
@@ -261,13 +282,16 @@ void LLChatBar::refresh()
// hide in mouselook, but keep previous visibility state
//BOOL mouselook = gAgent.cameraMouselook();
// call superclass setVisible so that we don't overwrite the saved setting
- LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible"));
+ if (mDynamicLayout)
+ {
+ LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible"));
+ }
// HACK: Leave the name of the gesture in place for a few seconds.
const F32 SHOW_GESTURE_NAME_TIME = 2.f;
if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
{
- LLCtrlListInterface* gestures = childGetListInterface("Gesture");
+ LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
if (gestures) gestures->selectFirstItem();
mGestureLabelTimer.stop();
}
@@ -277,6 +301,8 @@ void LLChatBar::refresh()
gAgent.stopTyping();
}
+ childSetValue("History", LLFloaterChat::instanceVisible(LLSD()));
+
childSetEnabled("Say", mInputEditor->getText().size() > 0);
childSetEnabled("Shout", mInputEditor->getText().size() > 0);
@@ -284,16 +310,18 @@ void LLChatBar::refresh()
void LLChatBar::refreshGestures()
{
- LLCtrlListInterface* gestures = childGetListInterface("Gesture");
- if (gestures)
+ LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
+ if (mGestureCombo && gestures)
{
//store current selection so we can maintain it
- LLString cur_gesture = childGetValue("Gesture").asString();
+ LLString cur_gesture = mGestureCombo->getValue().asString();
gestures->selectFirstItem();
- LLString label = childGetValue("Gesture").asString();
+ LLString label = mGestureCombo->getValue().asString();;
// clear
gestures->clearRows();
- // add gestures
+
+ // collect list of unique gestures
+ std::map <std::string, BOOL> unique;
LLGestureManager::item_map_t::iterator it;
for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
{
@@ -302,10 +330,18 @@ void LLChatBar::refreshGestures()
{
if (!gesture->mTrigger.empty())
{
- gestures->addSimpleElement(gesture->mTrigger);
+ unique[gesture->mTrigger] = TRUE;
}
}
}
+
+ // ad unique gestures
+ std::map <std::string, BOOL>::iterator it2;
+ for (it2 = unique.begin(); it2 != unique.end(); ++it2)
+ {
+ gestures->addSimpleElement((*it2).first);
+ }
+
gestures->sortByColumn(0, TRUE);
// Insert label after sorting
gestures->addSimpleElement(label, ADD_TOP);
@@ -362,6 +398,23 @@ LLString LLChatBar::getCurrentChat()
return mInputEditor ? mInputEditor->getText() : LLString::null;
}
+void LLChatBar::setGestureCombo(LLComboBox* combo)
+{
+ mGestureCombo = combo;
+ if (mGestureCombo)
+ {
+ mGestureCombo->setCommitCallback(onCommitGesture);
+ mGestureCombo->setCallbackUserData(this);
+
+ // now register observer since we have a place to put the results
+ mObserver = new LLChatBarGestureObserver(this);
+ gGestureManager.addObserver(mObserver);
+
+ // refresh list from current active gestures
+ refreshGestures();
+ }
+}
+
//-----------------------------------------------------------------------
// Internal functions
//-----------------------------------------------------------------------
@@ -453,13 +506,13 @@ void LLChatBar::sendChat( EChatType type )
sendChatFromViewer(utf8_revised_text, type, TRUE);
}
}
- childSetValue("Chat Editor", LLSD(LLString::null));
+ childSetValue("Chat Editor", LLString::null);
gAgent.stopTyping();
// If the user wants to stop chatting on hitting return, lose focus
// and go out of chat mode.
- if (gSavedSettings.getBOOL("CloseChatOnReturn"))
+ if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn"))
{
stopChat();
}
@@ -684,7 +737,7 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL
void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
{
LLChatBar* self = (LLChatBar*)data;
- LLCtrlListInterface* gestures = self->childGetListInterface("Gesture");
+ LLCtrlListInterface* gestures = self->mGestureCombo ? self->mGestureCombo->getListInterface() : NULL;
if (gestures)
{
S32 index = gestures->getFirstSelectedIndex();
@@ -708,6 +761,14 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
}
}
self->mGestureLabelTimer.start();
- // free focus back to chat bar
- self->childSetFocus("Gesture", FALSE);
+ if (self->mGestureCombo != NULL)
+ {
+ // free focus back to chat bar
+ self->mGestureCombo->setFocus(FALSE);
+ }
+}
+
+void toggleChatHistory(void* user_data)
+{
+ LLFloaterChat::toggleInstance(LLSD());
}
diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h
index 65724a1f45..33198f5fe6 100644
--- a/indra/newview/llchatbar.h
+++ b/indra/newview/llchatbar.h
@@ -13,23 +13,21 @@
#include "llframetimer.h"
#include "llchat.h"
-class LLButton;
-class LLComboBox;
class LLLineEditor;
class LLMessageSystem;
-class LLTextBox;
-class LLTextEditor;
class LLUICtrl;
class LLUUID;
class LLFrameTimer;
-class LLStatGraph;
class LLChatBarGestureObserver;
+class LLComboBox;
class LLChatBar
: public LLPanel
{
public:
- LLChatBar(const std::string& name, const LLRect& rect );
+ // constructor for inline chat-bars (e.g. hosted in chat history window)
+ LLChatBar(const std::string& name);
+ LLChatBar(const std::string& name, const LLRect& rect);
~LLChatBar();
virtual BOOL postBuild();
@@ -51,6 +49,10 @@ public:
BOOL inputEditorHasFocus();
LLString getCurrentChat();
+ // since chat bar logic is reused for chat history
+ // gesture combo box might not be a direct child
+ void setGestureCombo(LLComboBox* combo);
+
// Send a chat (after stripping /20foo channel chats).
// "Animate" means the nodding animation for regular text.
void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate);
@@ -61,7 +63,6 @@ public:
LLWString stripChannelNumber(const LLWString &mesg, S32* channel);
// callbacks
- static void onClickHistory( void* userdata );
static void onClickSay( void* userdata );
static void onClickShout( void* userdata );
@@ -89,8 +90,10 @@ protected:
S32 mLastSpecialChatChannel;
BOOL mIsBuilt;
-
- static LLChatBarGestureObserver* sObserver;
+ BOOL mDynamicLayout;
+ LLComboBox* mGestureCombo;
+
+ LLChatBarGestureObserver* mObserver;
};
extern LLChatBar *gChatBar;
diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp
index 41f8f1d714..7973e4d952 100644
--- a/indra/newview/llcolorswatch.cpp
+++ b/indra/newview/llcolorswatch.cpp
@@ -240,21 +240,6 @@ void LLColorSwatchCtrl::setEnabled( BOOL enabled )
}
-//////////////////////////////////////////////////////////////////////////////
-// called when parent filters down a visibility changed message
-void LLColorSwatchCtrl::onVisibilityChange ( BOOL curVisibilityIn )
-{
- // visibility changed - moved away to different tab for instance - cancel selection
- //if ( ! curVisibilityIn)
- //{
- // LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
- // if (pickerp)
- // {
- // pickerp->cancelSelection();
- // }
- //}
-}
-
void LLColorSwatchCtrl::setValue(const LLSD& value)
{
set(LLColor4(value), TRUE, TRUE);
diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h
index 999dce1296..4b44c5aef9 100644
--- a/indra/newview/llcolorswatch.h
+++ b/indra/newview/llcolorswatch.h
@@ -68,8 +68,6 @@ public:
virtual LLXMLNodePtr getXML(bool save_children = true) const;
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
- virtual void onVisibilityChange ( BOOL curVisibilityIn );
-
static void onColorChanged ( void* data, EColorPickOp pick_op = COLOR_CHANGE );
protected:
diff --git a/indra/newview/lldebugmessagebox.cpp b/indra/newview/lldebugmessagebox.cpp
index c62459f461..d3e7f08a95 100644
--- a/indra/newview/lldebugmessagebox.cpp
+++ b/indra/newview/lldebugmessagebox.cpp
@@ -33,25 +33,25 @@ LLDebugVarMessageBox::LLDebugVarMessageBox(const std::string& title, EDebugVarTy
switch(var_type)
{
case VAR_TYPE_F32:
- mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, *((F32*)var), -100.f, 100.f, 0.1f, NULL);
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, *((F32*)var), -100.f, 100.f, 0.1f, NULL);
mSlider1->setPrecision(3);
addChild(mSlider1);
mSlider2 = NULL;
mSlider3 = NULL;
break;
case VAR_TYPE_S32:
- mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, NULL);
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, NULL);
mSlider1->setPrecision(0);
addChild(mSlider1);
mSlider2 = NULL;
mSlider3 = NULL;
break;
case VAR_TYPE_VEC3:
- mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), "x: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, NULL);
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), "x: ", NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, NULL);
mSlider1->setPrecision(3);
- mSlider2 = new LLSliderCtrl("slider 2", LLRect(20,100,190,80), "y: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, NULL);
+ mSlider2 = new LLSliderCtrl("slider 2", LLRect(20,100,190,80), "y: ", NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, NULL);
mSlider2->setPrecision(3);
- mSlider3 = new LLSliderCtrl("slider 3", LLRect(20,70,190,50), "z: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, NULL);
+ mSlider3 = new LLSliderCtrl("slider 3", LLRect(20,70,190,50), "z: ", NULL, 70, 130, TRUE, TRUE, FALSE, NULL, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, NULL);
mSlider3->setPrecision(3);
addChild(mSlider1);
addChild(mSlider2);
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
index d20c431987..4e0cc94a26 100644
--- a/indra/newview/lldebugview.cpp
+++ b/indra/newview/lldebugview.cpp
@@ -17,7 +17,6 @@
#include "llconsole.h"
#include "lltextureview.h"
#include "llresmgr.h"
-#include "llaudiostatus.h"
#include "imageids.h"
#include "llvelocitybar.h"
#include "llviewerwindow.h"
@@ -89,19 +88,6 @@ LLDebugView::LLDebugView(const std::string& name, const LLRect &rect)
mStatViewp->setVisible(FALSE);
addChild(mStatViewp);
- //
- // Audio debugging stuff
- //
- const S32 AUDIO_STATUS_LEFT = rect.getWidth()/2-100;
- const S32 AUDIO_STATUS_WIDTH = 320;
- const S32 AUDIO_STATUS_TOP = (rect.getHeight()/2)+400;
- const S32 AUDIO_STATUS_HEIGHT = 320;
- r.setLeftTopAndSize( AUDIO_STATUS_LEFT, AUDIO_STATUS_TOP, AUDIO_STATUS_WIDTH, AUDIO_STATUS_HEIGHT );
- LLAudiostatus* gAudioStatus = new LLAudiostatus("AudioStatus", r);
- gAudioStatus->setFollowsTop();
- gAudioStatus->setFollowsRight();
- addChild(gAudioStatus);
-
const S32 VELOCITY_LEFT = 10; // 370;
const S32 VELOCITY_WIDTH = 500;
const S32 VELOCITY_TOP = 140;
diff --git a/indra/newview/llemote.h b/indra/newview/llemote.h
index c330d0bfc0..ede07b6db1 100644
--- a/indra/newview/llemote.h
+++ b/indra/newview/llemote.h
@@ -88,6 +88,8 @@ public:
// called when a motion is deactivated
virtual void onDeactivate();
+ virtual BOOL canDeprecate() { return FALSE; }
+
static BOOL getIndexFromName( const char* name, U32* index );
protected:
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index f5e6545369..7441d646ef 100644
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -911,8 +911,7 @@ void LLFastTimerView::draw()
//draw line graph history
{
LLGLSNoTexture no_texture;
- LLGLEnable scissor(GL_SCISSOR_TEST);
- LLUI::setScissorRegionLocal(graph_rect);
+ LLLocalClipRect clip(graph_rect);
//normalize based on last frame's maximum
static U64 last_max = 0;
diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp
index 9f05e59ac8..2982dba7b2 100644
--- a/indra/newview/llfirstuse.cpp
+++ b/indra/newview/llfirstuse.cpp
@@ -15,6 +15,7 @@
// viewer includes
#include "llnotify.h"
+#include "llfloatervoicewizard.h"
#include "llviewercontrol.h"
#include "llui.h"
#include "viewer.h"
@@ -228,3 +229,15 @@ void LLFirstUse::useSculptedPrim()
}
}
+
+// static
+void LLFirstUse::useVoice()
+{
+ if (gDisableVoice) return;
+ if (gSavedSettings.getWarning("FirstVoice"))
+ {
+ gSavedSettings.setWarning("FirstVoice", FALSE);
+
+ LLFloaterVoiceWizard::showInstance();
+ }
+}
diff --git a/indra/newview/llfirstuse.h b/indra/newview/llfirstuse.h
index 134699d1ec..977ff7ddeb 100644
--- a/indra/newview/llfirstuse.h
+++ b/indra/newview/llfirstuse.h
@@ -81,6 +81,7 @@ public:
static void useFlexible();
static void useDebugMenus();
static void useSculptedPrim();
+ static void useVoice();
protected:
static std::set<LLString> sConfigVariables;
diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp
index 7085407999..edbfa4244c 100644
--- a/indra/newview/llfloateranimpreview.cpp
+++ b/indra/newview/llfloateranimpreview.cpp
@@ -973,7 +973,7 @@ void LLFloaterAnimPreview::onBtnOK(void* userdata)
LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID);
}
- floaterp->onClose(false);
+ floaterp->close(false);
}
//-----------------------------------------------------------------------------
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
index b541b279d0..0ba0ab89b1 100644
--- a/indra/newview/llfloateravatarpicker.cpp
+++ b/indra/newview/llfloateravatarpicker.cpp
@@ -188,7 +188,12 @@ void LLFloaterAvatarPicker::onSelectionChange(const std::deque<LLFolderViewItem*
self->mAvatarIDs.clear();
self->mAvatarNames.clear();
- self->childSetEnabled("Select", FALSE);
+ // if we have calling cards, disable select button until
+ // the inventory picks a valid calling card
+ if (!items.empty())
+ {
+ self->childSetEnabled("Select", FALSE);
+ }
if (!self->mListNames)
{
diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp
index f4769cc18f..6e76db4270 100644
--- a/indra/newview/llfloaterchat.cpp
+++ b/indra/newview/llfloaterchat.cpp
@@ -14,6 +14,7 @@
#include "llviewerprecompiledheaders.h"
#include "llfloaterchat.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterscriptdebug.h"
#include "llchat.h"
@@ -29,11 +30,13 @@
#include "llcheckboxctrl.h"
#include "llcombobox.h"
#include "llconsole.h"
+#include "llfloaterchatterbox.h"
#include "llfloatermute.h"
#include "llkeyboard.h"
//#include "lllineeditor.h"
#include "llmutelist.h"
//#include "llresizehandle.h"
+#include "llchatbar.h"
#include "llstatusbar.h"
#include "llviewertexteditor.h"
#include "llviewergesture.h" // for triggering gestures
@@ -50,7 +53,6 @@
//
// Constants
//
-const char FLOATER_TITLE[] = "Chat History";
const F32 INSTANT_MSG_SIZE = 8.0f;
const F32 CHAT_MSG_SIZE = 8.0f;
const LLColor4 INSTANT_MSG_COLOR(1, 1, 1, 1);
@@ -60,25 +62,25 @@ const S32 MAX_CHATTER_COUNT = 16;
//
// Global statics
//
-LLFloaterChat* gFloaterChat = NULL;
-
LLColor4 get_text_color(const LLChat& chat);
//
// Member Functions
//
-LLFloaterChat::LLFloaterChat()
-: LLFloater("chat floater", "FloaterChatRect", FLOATER_TITLE,
- RESIZE_YES, 440, 100, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES)
+LLFloaterChat::LLFloaterChat(const LLSD& seed)
+: LLFloater("chat floater", "FloaterChatRect", "",
+ RESIZE_YES, 440, 100, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES),
+ mPanel(NULL)
{
-
- gUICtrlFactory->buildFloater(this,"floater_chat_history.xml");
+ mFactoryMap["chat_panel"] = LLCallbackMap(createChatPanel, NULL);
+ mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL);
+ // do not automatically open singleton floaters (as result of getInstance())
+ BOOL no_open = FALSE;
+ gUICtrlFactory->buildFloater(this,"floater_chat_history.xml",&getFactoryMap(),no_open);
- childSetAction("Mute resident",onClickMute,this);
- childSetAction("Chat", onClickChat, this);
- childSetCommitCallback("chatter combobox",onCommitUserSelect,this);
childSetCommitCallback("show mutes",onClickToggleShowMute,this); //show mutes
childSetVisible("Chat History Editor with mute",FALSE);
+ childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this);
setDefaultBtn("Chat");
}
@@ -92,33 +94,53 @@ void LLFloaterChat::setVisible(BOOL visible)
LLFloater::setVisible( visible );
gSavedSettings.setBOOL("ShowChatHistory", visible);
+}
- // Hide the chat overlay when our history is visible.
- gConsole->setVisible( !visible );
+void LLFloaterChat::draw()
+{
+ // enable say and shout only when text available
+
+ childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel"));
+
+ LLChatBar* chat_barp = (LLChatBar*)getChildByName("chat_panel", TRUE);
+ if (chat_barp)
+ {
+ chat_barp->refresh();
+ }
+
+ mPanel->refreshSpeakers();
+ LLFloater::draw();
}
+BOOL LLFloaterChat::postBuild()
+{
+ mPanel = (LLPanelActiveSpeakers*)LLUICtrlFactory::getPanelByName(this, "active_speakers_panel");
+
+ LLChatBar* chat_barp = (LLChatBar*)getChildByName("chat_panel", TRUE);
+ if (chat_barp)
+ {
+ chat_barp->setGestureCombo(LLUICtrlFactory::getComboBoxByName(this, "Gesture"));
+ }
+ return TRUE;
+}
// public virtual
void LLFloaterChat::onClose(bool app_quitting)
{
- LLFloater::setVisible( FALSE );
-
if (!app_quitting)
{
gSavedSettings.setBOOL("ShowChatHistory", FALSE);
}
-
- // Hide the chat overlay when our history is visible.
- gConsole->setVisible( TRUE );
+ setVisible(FALSE);
}
-
-// public
-void LLFloaterChat::show()
+void LLFloaterChat::onVisibilityChange(BOOL new_visibility)
{
- open(); /*Flawfinder: ignore*/
+ // Hide the chat overlay when our history is visible.
+ gConsole->setVisible( !new_visibility );
}
+
void add_timestamped_line(LLViewerTextEditor* edit, const LLString& line, const LLColor4& color)
{
bool prepend_newline = true;
@@ -142,7 +164,7 @@ void log_chat_text(const LLChat& chat)
}
// static
void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
-{
+{
if ( gSavedPerAccountSettings.getBOOL("LogChat") && log_to_file)
{
log_chat_text(chat);
@@ -165,10 +187,9 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
}
// could flash the chat button in the status bar here. JC
- if (!gFloaterChat) return;
-
- LLViewerTextEditor* history_editor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
- LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
+ LLFloaterChat* chat_floater = LLFloaterChat::getInstance(LLSD());
+ LLViewerTextEditor* history_editor = (LLViewerTextEditor*)chat_floater->getChildByName("Chat History Editor", TRUE);
+ LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)chat_floater->getChildByName("Chat History Editor with mute", TRUE);
history_editor->setParseHTML(TRUE);
history_editor_with_mute->setParseHTML(TRUE);
@@ -184,77 +205,24 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
LLColor4 muted_color = lerp(color, LLColor4::grey, 0.5f);
add_timestamped_line(history_editor_with_mute, chat.mText, color);
}
-
- if (!chat.mMuted
- && chat.mSourceType != CHAT_SOURCE_SYSTEM
- && chat.mFromID.notNull()
- && chat.mFromID != gAgent.getID())
+
+ // add objects as transient speakers that can be muted
+ if (chat.mSourceType == CHAT_SOURCE_OBJECT)
{
-
- LLComboBox* chatter_combo = LLUICtrlFactory::getComboBoxByName(gFloaterChat,"chatter combobox");
-
- if(!chatter_combo)
- {
- return;
- }
-
- if (!chatter_combo->setCurrentByID(chat.mFromID))
- {
- // if we have too many items...
- if (chatter_combo->getItemCount() >= MAX_CHATTER_COUNT)
- {
- chatter_combo->remove(0);
- }
-
- LLMute mute(chat.mFromID, chat.mFromName);
- if (chat.mSourceType == CHAT_SOURCE_OBJECT)
- {
- mute.mType = LLMute::OBJECT;
- }
- else if (chat.mSourceType == CHAT_SOURCE_AGENT)
- {
- mute.mType = LLMute::AGENT;
- }
- LLString item = mute.getDisplayName();
- chatter_combo->add(item, chat.mFromID);
- chatter_combo->setCurrentByIndex(chatter_combo->getItemCount() - 1);
- gFloaterChat->childSetEnabled("Mute resident",TRUE);
- }
+ chat_floater->mPanel->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT);
}
}
// static
void LLFloaterChat::setHistoryCursorAndScrollToEnd()
{
- if (gFloaterChat)
- {
- LLViewerTextEditor* history_editor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
- LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
-
- history_editor->setCursorAndScrollToEnd();
- history_editor_with_mute->setCursorAndScrollToEnd();
- }
-}
-
-
-// static
-void LLFloaterChat::toggle(void*)
-{
- if (gFloaterChat->getVisible())
- {
- gFloaterChat->close();
- }
- else
- {
- gFloaterChat->show();
- }
+ LLViewerTextEditor* history_editor = (LLViewerTextEditor*)LLFloaterChat::getInstance(LLSD())->getChildByName("Chat History Editor", TRUE);
+ LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)LLFloaterChat::getInstance(LLSD())->getChildByName("Chat History Editor with mute", TRUE);
+
+ history_editor->setCursorAndScrollToEnd();
+ history_editor_with_mute->setCursorAndScrollToEnd();
}
-// static
-BOOL LLFloaterChat::visible(void*)
-{
- return (gFloaterChat && gFloaterChat->getVisible());
-}
//static
void LLFloaterChat::onClickMute(void *data)
@@ -279,30 +247,6 @@ void LLFloaterChat::onClickMute(void *data)
}
//static
-void LLFloaterChat::onClickChat(void*)
-{
- // we need this function as a level of indirection because otherwise startChat would
- // cast the data pointer to a character string, and dump garbage in the chat
- LLChatBar::startChat(NULL);
-}
-
-//static
-void LLFloaterChat::onCommitUserSelect(LLUICtrl* caller, void* data)
-{
- LLFloaterChat* floater = (LLFloaterChat*)data;
- LLComboBox* combo = (LLComboBox*)caller;
-
- if (combo->getCurrentIndex() == -1)
- {
- floater->childSetEnabled("Mute resident",FALSE);
- }
- else
- {
- floater->childSetEnabled("Mute resident",TRUE);
- }
-}
-
-//static
void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
{
LLFloaterChat* floater = (LLFloaterChat*)data;
@@ -310,8 +254,8 @@ void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
//LLCheckBoxCtrl*
BOOL show_mute = LLUICtrlFactory::getCheckBoxByName(floater,"show mutes")->get();
- LLViewerTextEditor* history_editor = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor");
- LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor with mute");
+ LLViewerTextEditor* history_editor = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor", TRUE);
+ LLViewerTextEditor* history_editor_with_mute = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor with mute", TRUE);
if (!history_editor || !history_editor_with_mute)
return;
@@ -427,7 +371,7 @@ LLColor4 get_text_color(const LLChat& chat)
//static
void LLFloaterChat::loadHistory()
{
- LLLogChat::loadHistory("chat", &chatFromLogFile, (void *)gFloaterChat);
+ LLLogChat::loadHistory("chat", &chatFromLogFile, (void *)LLFloaterChat::getInstance(LLSD()));
}
//static
@@ -438,3 +382,40 @@ void LLFloaterChat::chatFromLogFile(LLString line, void* userdata)
chat.mText = line;
addChatHistory(chat, FALSE);
}
+
+//static
+void* LLFloaterChat::createSpeakersPanel(void* data)
+{
+ return new LLPanelActiveSpeakers(gLocalSpeakerMgr, TRUE);
+}
+
+//static
+void* LLFloaterChat::createChatPanel(void* data)
+{
+ LLChatBar* chatp = new LLChatBar("floating_chat_bar");
+ return chatp;
+}
+
+//static
+void LLFloaterChat::hideInstance(const LLSD& id)
+{
+ LLFloaterChat* floaterp = LLFloaterChat::getInstance(LLSD());
+ // don't do anything when hosted in the chatterbox
+ if(floaterp->getHost())
+ {
+ LLFloaterChatterBox::hideInstance(LLSD());
+ }
+ else
+ {
+ LLUISingleton<LLFloaterChat>::hideInstance(id);
+ }
+}
+
+// static
+void LLFloaterChat::onClickToggleActiveSpeakers(void* userdata)
+{
+ LLFloaterChat* self = (LLFloaterChat*)userdata;
+
+ self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel"));
+}
+
diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h
index bf93afb455..6da3b9bb1b 100644
--- a/indra/newview/llfloaterchat.h
+++ b/indra/newview/llfloaterchat.h
@@ -24,17 +24,20 @@ class LLViewerTextEditor;
class LLMessageSystem;
class LLUUID;
class LLCheckBoxCtrl;
+class LLPanelActiveSpeakers;
class LLFloaterChat
-: public LLFloater
+: public LLFloater, public LLUISingleton<LLFloaterChat>
{
public:
- LLFloaterChat();
+ LLFloaterChat(const LLSD& seed);
~LLFloaterChat();
- void show();
- virtual void onClose(bool app_quitting);
virtual void setVisible( BOOL b );
+ virtual void draw();
+ virtual BOOL postBuild();
+ virtual void onClose(bool app_quitting);
+ virtual void onVisibilityChange(BOOL cur_visibility);
static void setHistoryCursorAndScrollToEnd();
@@ -45,17 +48,17 @@ public:
// Add chat to history alone.
static void addChatHistory(const LLChat& chat, bool log_to_file = true);
- static void toggle(void*);
- static BOOL visible(void*);
-
static void onClickMute(void *data);
- static void onClickChat(void *);
- static void onCommitUserSelect(LLUICtrl* caller, void* data);
static void onClickToggleShowMute(LLUICtrl* caller, void *data);
+ static void onClickToggleActiveSpeakers(void* userdata);
static void chatFromLogFile(LLString line, void* userdata);
static void loadHistory();
-};
+ static void* createSpeakersPanel(void* data);
+ static void* createChatPanel(void* data);
+ static void hideInstance(const LLSD& id);
-extern LLFloaterChat* gFloaterChat;
+protected:
+ LLPanelActiveSpeakers* mPanel;
+};
#endif
diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp
new file mode 100644
index 0000000000..ab992bdb45
--- /dev/null
+++ b/indra/newview/llfloaterchatterbox.cpp
@@ -0,0 +1,322 @@
+/**
+ * @file llfloaterchatterbox.cpp
+ * @author Richard
+ * @date 2007-05-08
+ * @brief Implementation of the chatterbox integrated conversation ui
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterchatterbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llfloaterchat.h"
+#include "llfloaterfriends.h"
+#include "llfloatergroups.h"
+#include "llviewercontrol.h"
+#include "llimview.h"
+#include "llimpanel.h"
+
+//
+// LLFloaterMyFriends
+//
+
+LLFloaterMyFriends::LLFloaterMyFriends(const LLSD& seed)
+{
+ mFactoryMap["friends_panel"] = LLCallbackMap(LLFloaterMyFriends::createFriendsPanel, NULL);
+ mFactoryMap["groups_panel"] = LLCallbackMap(LLFloaterMyFriends::createGroupsPanel, NULL);
+ // do not automatically open singleton floaters (as result of getInstance())
+ BOOL no_open = FALSE;
+ gUICtrlFactory->buildFloater(this, "floater_my_friends.xml", &getFactoryMap(), no_open);
+}
+
+LLFloaterMyFriends::~LLFloaterMyFriends()
+{
+}
+
+BOOL LLFloaterMyFriends::postBuild()
+{
+ mTabs = LLUICtrlFactory::getTabContainerByName(this, "friends_and_groups");
+
+ return TRUE;
+}
+
+
+void LLFloaterMyFriends::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+}
+
+//static
+LLFloaterMyFriends* LLFloaterMyFriends::showInstance(const LLSD& id)
+{
+ LLFloaterMyFriends* floaterp = LLUIInstanceMgr<LLFloaterMyFriends>::showInstance(id);
+ // garbage values in id will be interpreted as 0, or the friends tab
+ floaterp->mTabs->selectTab(id);
+
+ return floaterp;
+}
+
+//static
+void LLFloaterMyFriends::hideInstance(const LLSD& id)
+{
+ if(instanceVisible(id))
+ {
+ LLFloaterChatterBox::hideInstance(LLSD());
+ }
+}
+
+// is the specified panel currently visible
+//static
+BOOL LLFloaterMyFriends::instanceVisible(const LLSD& id)
+{
+ // if singleton not created yet, trivially return false
+ if (!findInstance(id)) return FALSE;
+
+ LLFloaterMyFriends* floaterp = getInstance(id);
+ return floaterp->isInVisibleChain() && floaterp->mTabs->getCurrentPanelIndex() == id.asInteger();
+}
+
+//static
+void* LLFloaterMyFriends::createFriendsPanel(void* data)
+{
+ return new LLPanelFriends();
+}
+
+//static
+void* LLFloaterMyFriends::createGroupsPanel(void* data)
+{
+ return new LLPanelGroups();
+}
+
+//
+// LLFloaterChatterBox
+//
+LLFloaterChatterBox::LLFloaterChatterBox(const LLSD& seed) :
+ mActiveVoiceFloater(NULL)
+{
+ mAutoResize = FALSE;
+
+ gUICtrlFactory->buildFloater(this, "floater_chatterbox.xml", NULL, FALSE);
+ addFloater(LLFloaterMyFriends::getInstance(0), TRUE);
+ if (gSavedSettings.getBOOL("ChatHistoryTornOff"))
+ {
+ LLFloaterChat* floater_chat = LLFloaterChat::getInstance(LLSD());
+ // add then remove to set up relationship for re-attach
+ addFloater(floater_chat, FALSE);
+ removeFloater(floater_chat);
+ // reparent to floater view
+ gFloaterView->addChild(floater_chat);
+ }
+ else
+ {
+ addFloater(LLFloaterChat::getInstance(LLSD()), FALSE);
+ }
+ mTabContainer->lockTabs();
+}
+
+LLFloaterChatterBox::~LLFloaterChatterBox()
+{
+}
+
+BOOL LLFloaterChatterBox::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (getEnabled()
+ && mask == MASK_CONTROL)
+ {
+ if (key == 'W')
+ {
+ LLFloater* floater = getActiveFloater();
+ // is user closeable and is system closeable
+ if (floater && floater->canClose())
+ {
+ if (floater->isCloseable())
+ {
+ floater->close();
+ }
+ else
+ {
+ // close chatterbox window if frontmost tab is reserved, non-closeable tab
+ // such as contacts or near me
+ close();
+ }
+ }
+ return TRUE;
+ }
+ }
+
+ return LLMultiFloater::handleKeyHere(key, mask, called_from_parent);
+}
+
+void LLFloaterChatterBox::draw()
+{
+ // clear new im notifications when chatterbox is visible
+ if (!isMinimized())
+ {
+ gIMMgr->clearNewIMNotification();
+ }
+ LLFloater* current_active_floater = getCurrentVoiceFloater();
+ // set icon on tab for floater currently associated with active voice channel
+ if(mActiveVoiceFloater != current_active_floater)
+ {
+ // remove image from old floater's tab
+ if (mActiveVoiceFloater)
+ {
+ mTabContainer->setTabImage(mActiveVoiceFloater, "");
+ }
+ }
+
+ // update image on current active tab
+ if (current_active_floater)
+ {
+ LLColor4 icon_color = LLColor4::white;
+ LLVoiceChannel* channelp = LLVoiceChannel::getCurrentVoiceChannel();
+ if (channelp)
+ {
+ if (channelp->isActive())
+ {
+ icon_color = LLColor4::green;
+ }
+ else if (channelp->getState() == LLVoiceChannel::STATE_ERROR)
+ {
+ icon_color = LLColor4::red;
+ }
+ else // active, but not connected
+ {
+ icon_color = LLColor4::yellow;
+ }
+ }
+ mTabContainer->setTabImage(current_active_floater, "active_voice_tab.tga", icon_color);
+ }
+
+ mActiveVoiceFloater = current_active_floater;
+
+ LLFloater::draw();
+}
+
+void LLFloaterChatterBox::onOpen()
+{
+ gSavedSettings.setBOOL("ShowCommunicate", TRUE);
+}
+
+void LLFloaterChatterBox::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+ gSavedSettings.setBOOL("ShowCommunicate", FALSE);
+}
+
+void LLFloaterChatterBox::removeFloater(LLFloater* floaterp)
+{
+ if (floaterp->getName() == "chat floater")
+ {
+ // only my friends floater now locked
+ mTabContainer->lockTabs(1);
+ gSavedSettings.setBOOL("ChatHistoryTornOff", TRUE);
+ floaterp->setCanClose(TRUE);
+ }
+ LLMultiFloater::removeFloater(floaterp);
+}
+
+void LLFloaterChatterBox::addFloater(LLFloater* floaterp,
+ BOOL select_added_floater,
+ LLTabContainerCommon::eInsertionPoint insertion_point)
+{
+ // make sure my friends and chat history both locked when re-attaching chat history
+ if (floaterp->getName() == "chat floater")
+ {
+ // select my friends tab
+ mTabContainer->selectFirstTab();
+ // add chat history to the right of the my friends tab
+ //*TODO: respect select_added_floater so that we don't leave first tab selected
+ LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT);
+ // make sure first two tabs are now locked
+ mTabContainer->lockTabs(2);
+ gSavedSettings.setBOOL("ChatHistoryTornOff", FALSE);
+ floaterp->setCanClose(FALSE);
+ }
+ else
+ {
+ LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+ }
+
+ // make sure active voice icon shows up for new tab
+ if (floaterp == mActiveVoiceFloater)
+ {
+ mTabContainer->setTabImage(floaterp, "active_voice_tab.tga");
+ }
+}
+
+
+//static
+LLFloaterChatterBox* LLFloaterChatterBox::showInstance(const LLSD& seed)
+{
+ LLFloaterChatterBox* floater = LLUISingleton<LLFloaterChatterBox>::showInstance(seed);
+
+ // if TRUE, show tab for active voice channel, otherwise, just show last tab
+ if (seed.asBoolean())
+ {
+ LLFloater* floater_to_show = getCurrentVoiceFloater();
+ if (floater_to_show)
+ {
+ floater_to_show->open();
+ }
+ else
+ {
+ // just open chatterbox if there is no active voice window
+ LLUISingleton<LLFloaterChatterBox>::getInstance(seed)->open();
+ }
+ }
+
+ return floater;
+}
+
+//static
+BOOL LLFloaterChatterBox::instanceVisible(const LLSD &seed)
+{
+ if (seed.asBoolean())
+ {
+ LLFloater* floater_to_show = getCurrentVoiceFloater();
+ if (floater_to_show)
+ {
+ return floater_to_show->isInVisibleChain();
+ }
+ }
+
+ return LLUISingleton<LLFloaterChatterBox>::instanceVisible(seed);
+}
+
+//static
+LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater()
+{
+ if (!LLVoiceClient::voiceEnabled())
+ {
+ return NULL;
+ }
+ if (LLVoiceChannelProximal::getInstance() == LLVoiceChannel::getCurrentVoiceChannel())
+ {
+ // show near me tab if in proximal channel
+ return LLFloaterChat::getInstance(LLSD());
+ }
+ else
+ {
+ LLFloaterChatterBox* floater = LLFloaterChatterBox::getInstance(LLSD());
+ // iterator over all IM tabs (skip friends and near me)
+ for (S32 i = 0; i < floater->getFloaterCount(); i++)
+ {
+ LLPanel* panelp = floater->mTabContainer->getPanelByIndex(i);
+ if (panelp->getName() == "im_floater")
+ {
+ // only LLFloaterIMPanels are called "im_floater"
+ LLFloaterIMPanel* im_floaterp = (LLFloaterIMPanel*)panelp;
+ if (im_floaterp->getVoiceChannel() == LLVoiceChannel::getCurrentVoiceChannel())
+ {
+ return im_floaterp;
+ }
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/indra/newview/llfloaterchatterbox.h b/indra/newview/llfloaterchatterbox.h
new file mode 100644
index 0000000000..38cc7d6e4b
--- /dev/null
+++ b/indra/newview/llfloaterchatterbox.h
@@ -0,0 +1,67 @@
+/**
+ * @file llfloaterchatterbox.h
+ * @author Richard
+ * @date 2007-05-04
+ * @brief Integrated friends and group management/communication tool
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERCHATTERBOX_H
+#define LL_LLFLOATERCHATTERBOX_H
+
+#include "llfloater.h"
+#include "llstring.h"
+
+class LLTabContainerCommon;
+
+class LLFloaterMyFriends : public LLFloater, public LLUISingleton<LLFloaterMyFriends>
+{
+public:
+ LLFloaterMyFriends(const LLSD& seed);
+ virtual ~LLFloaterMyFriends();
+
+ virtual BOOL postBuild();
+
+ void onClose(bool app_quitting);
+
+ // override LLUISingleton behavior
+ static LLFloaterMyFriends* showInstance(const LLSD& id);
+ static void hideInstance(const LLSD& id);
+ static BOOL instanceVisible(const LLSD& id);
+
+ static void* createFriendsPanel(void* data);
+ static void* createGroupsPanel(void* data);
+
+protected:
+ LLTabContainerCommon* mTabs;
+};
+
+class LLFloaterChatterBox : public LLMultiFloater, public LLUISingleton<LLFloaterChatterBox>
+{
+public:
+ LLFloaterChatterBox(const LLSD& seed);
+ virtual ~LLFloaterChatterBox();
+
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ void draw();
+ /*virtual*/ void onOpen();
+ /*virtual*/ void onClose(bool app_quitting);
+
+ /*virtual*/ void removeFloater(LLFloater* floaterp);
+ /*virtual*/ void addFloater(LLFloater* floaterp,
+ BOOL select_added_floater,
+ LLTabContainerCommon::eInsertionPoint insertion_point = LLTabContainerCommon::END);
+
+ static LLFloaterChatterBox* showInstance(const LLSD& seed);
+ static BOOL instanceVisible(const LLSD& seed);
+
+ static LLFloater* getCurrentVoiceFloater();
+
+protected:
+ LLFloater* mActiveVoiceFloater;
+};
+
+
+#endif // LL_LLFLOATERCHATTERBOX_H
diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp
index 7e0a64c420..592cb4186c 100644
--- a/indra/newview/llfloaterfriends.cpp
+++ b/indra/newview/llfloaterfriends.cpp
@@ -43,20 +43,18 @@
class LLLocalFriendsObserver : public LLFriendObserver
{
public:
- LLLocalFriendsObserver(LLFloaterFriends* floater) : mFloater(floater) {}
+ LLLocalFriendsObserver(LLPanelFriends* floater) : mFloater(floater) {}
virtual ~LLLocalFriendsObserver() { mFloater = NULL; }
virtual void changed(U32 mask)
{
mFloater->updateFriends(mask);
}
protected:
- LLFloaterFriends* mFloater;
+ LLPanelFriends* mFloater;
};
-LLFloaterFriends* LLFloaterFriends::sInstance = NULL;
-
-LLFloaterFriends::LLFloaterFriends() :
- LLFloater("Friends"),
+LLPanelFriends::LLPanelFriends() :
+ LLPanel(),
LLEventTimer(1000000),
mObserver(NULL),
mMenuState(0),
@@ -64,81 +62,38 @@ LLFloaterFriends::LLFloaterFriends() :
mAllowRightsChange(TRUE),
mNumRightsChanged(0)
{
- mTimer.stop();
- sInstance = this;
+ mEventTimer.stop();
mObserver = new LLLocalFriendsObserver(this);
LLAvatarTracker::instance().addObserver(mObserver);
- gSavedSettings.setBOOL("ShowFriends", TRUE);
- // Builds and adds to gFloaterView
- gUICtrlFactory->buildFloater(this, "floater_friends.xml");
- refreshUI();
}
-LLFloaterFriends::~LLFloaterFriends()
+LLPanelFriends::~LLPanelFriends()
{
LLAvatarTracker::instance().removeObserver(mObserver);
delete mObserver;
- sInstance = NULL;
- gSavedSettings.setBOOL("ShowFriends", FALSE);
}
-void LLFloaterFriends::tick()
+void LLPanelFriends::tick()
{
- mTimer.stop();
+ mEventTimer.stop();
mPeriod = 1000000;
mAllowRightsChange = TRUE;
updateFriends(LLFriendObserver::ADD);
}
-// static
-void LLFloaterFriends::show(void*)
-{
- if(sInstance)
- {
- sInstance->open(); /*Flawfinder: ignore*/
- }
- else
- {
- LLFloaterFriends* self = new LLFloaterFriends;
- self->open(); /*Flawfinder: ignore*/
- }
-}
-
-
-// static
-BOOL LLFloaterFriends::visible(void*)
-{
- return sInstance && sInstance->getVisible();
-}
-
-
-// static
-void LLFloaterFriends::toggle(void*)
-{
- if (sInstance)
- {
- sInstance->close();
- }
- else
- {
- show();
- }
-}
-
-
-void LLFloaterFriends::updateFriends(U32 changed_mask)
+void LLPanelFriends::updateFriends(U32 changed_mask)
{
LLUUID selected_id;
- LLCtrlListInterface *friends_list = sInstance->childGetListInterface("friend_list");
+ LLCtrlListInterface *friends_list = childGetListInterface("friend_list");
if (!friends_list) return;
- LLCtrlScrollInterface *friends_scroll = sInstance->childGetScrollInterface("friend_list");
+ LLCtrlScrollInterface *friends_scroll = childGetScrollInterface("friend_list");
if (!friends_scroll) return;
// We kill the selection warning, otherwise we'll spam with warning popups
// if the maximum amount of friends are selected
mShowMaxSelectWarning = false;
- LLDynamicArray<LLUUID> selected_friends = sInstance->getSelectedIDs();
+ LLDynamicArray<LLUUID> selected_friends = getSelectedIDs();
if(changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
{
refreshNames();
@@ -149,8 +104,8 @@ void LLFloaterFriends::updateFriends(U32 changed_mask)
if(mNumRightsChanged > 0)
{
mPeriod = RIGHTS_CHANGE_TIMEOUT;
- mTimer.start();
- mTimer.reset();
+ mEventTimer.start();
+ mEventTimer.reset();
mAllowRightsChange = FALSE;
}
else
@@ -175,12 +130,12 @@ void LLFloaterFriends::updateFriends(U32 changed_mask)
}
// virtual
-BOOL LLFloaterFriends::postBuild()
+BOOL LLPanelFriends::postBuild()
{
-
mFriendsList = LLUICtrlFactory::getScrollListByName(this, "friend_list");
mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT);
mFriendsList->setMaxiumumSelectCallback(onMaximumSelect);
+ mFriendsList->setCommitOnSelectionChange(TRUE);
childSetCommitCallback("friend_list", onSelectName, this);
childSetDoubleClickCallback("friend_list", onClickIM);
@@ -195,13 +150,17 @@ BOOL LLFloaterFriends::postBuild()
childSetAction("pay_btn", onClickPay, this);
childSetAction("add_btn", onClickAddFriend, this);
childSetAction("remove_btn", onClickRemove, this);
- childSetAction("close_btn", onClickClose, this);
+
+ setDefaultBtn("im_btn");
+
+ updateFriends(LLFriendObserver::ADD);
+ refreshUI();
return TRUE;
}
-void LLFloaterFriends::addFriend(const std::string& name, const LLUUID& agent_id)
+void LLPanelFriends::addFriend(const std::string& name, const LLUUID& agent_id)
{
LLAvatarTracker& at = LLAvatarTracker::instance();
const LLRelationship* relationInfo = at.getBuddyInfo(agent_id);
@@ -255,17 +214,26 @@ void LLFloaterFriends::addFriend(const std::string& name, const LLUUID& agent_id
mFriendsList->addElement(element, ADD_BOTTOM);
}
-void LLFloaterFriends::refreshRightsChangeList(U8 state)
+void LLPanelFriends::refreshRightsChangeList()
{
LLDynamicArray<LLUUID> friends = getSelectedIDs();
- const LLRelationship* friend_status = NULL;
- if(friends.size() > 0) friend_status = LLAvatarTracker::instance().getBuddyInfo(friends[0]);
+ S32 num_selected = friends.size();
LLSD row;
- bool can_change_visibility = false;
- bool can_change_modify = false;
- bool can_change_online_multiple = true;
- bool can_change_map_multiple = true;
+ bool can_offer_teleport = num_selected >= 1;
+
+ // aggregate permissions over all selected friends
+ bool friends_see_online = true;
+ bool friends_see_on_map = true;
+ bool friends_modify_objects = true;
+
+ // do at least some of the friends selected have these rights?
+ bool some_friends_see_online = false;
+ bool some_friends_see_on_map = false;
+ bool some_friends_modify_objects = false;
+
+ bool selected_friends_online = true;
+
LLTextBox* processing_label = LLUICtrlFactory::getTextBoxByName(this, "process_rights_label");
if(!mAllowRightsChange)
@@ -273,7 +241,9 @@ void LLFloaterFriends::refreshRightsChangeList(U8 state)
if(processing_label)
{
processing_label->setVisible(true);
- state = 0;
+ // ignore selection for now
+ friends.clear();
+ num_selected = 0;
}
}
else
@@ -284,82 +254,111 @@ void LLFloaterFriends::refreshRightsChangeList(U8 state)
}
}
- if(state == 1)
- {
- if(friend_status && !friend_status->isOnline())
- {
- childSetEnabled("offer_teleport_btn", false);
- }
- can_change_visibility = true;
- can_change_modify = true;
- }
- else if (state == 2)
+ const LLRelationship* friend_status = NULL;
+ for(LLDynamicArray<LLUUID>::iterator itr = friends.begin(); itr != friends.end(); ++itr)
{
- can_change_visibility = true;
- can_change_modify = false;
- for(LLDynamicArray<LLUUID>::iterator itr = friends.begin(); itr != friends.end(); ++itr)
+ friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
+ if (friend_status)
{
- friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
+ bool can_see_online = friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS);
+ bool can_see_on_map = friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION);
+ bool can_modify_objects = friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);
+
+ // aggregate rights of this friend into total selection
+ friends_see_online &= can_see_online;
+ friends_see_on_map &= can_see_on_map;
+ friends_modify_objects &= can_modify_objects;
+
+ // can at least one of your selected friends do any of these?
+ some_friends_see_online |= can_see_online;
+ some_friends_see_on_map |= can_see_on_map;
+ some_friends_modify_objects |= can_modify_objects;
+
if(!friend_status->isOnline())
{
- childSetEnabled("offer_teleport_btn", false);
- }
- if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
- {
- can_change_online_multiple = false;
- can_change_map_multiple = false;
- }
- else if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))
- {
- can_change_map_multiple = false;
+ can_offer_teleport = false;
+ selected_friends_online = false;
}
}
-
+ else // missing buddy info, don't allow any operations
+ {
+ can_offer_teleport = false;
+
+ friends_see_online = false;
+ friends_see_on_map = false;
+ friends_modify_objects = false;
+
+ some_friends_see_online = false;
+ some_friends_see_on_map = false;
+ some_friends_modify_objects = false;
+ }
}
- LLCheckboxCtrl* check;
+ // seeing a friend on the map requires seeing online status as a prerequisite
+ friends_see_on_map &= friends_see_online;
+
mMenuState = 0;
- check = LLUICtrlFactory::getCheckBoxByName(this, "online_status_cb");
- check->setEnabled(can_change_visibility);
- check->set(FALSE);
- if(!mAllowRightsChange) check->setVisible(FALSE);
- else check->setVisible(TRUE);
- if(friend_status)
+ // make checkboxes visible after we have finished processing rights
+ childSetVisible("online_status_cb", mAllowRightsChange);
+ childSetVisible("map_status_cb", mAllowRightsChange);
+ childSetVisible("modify_status_cb", mAllowRightsChange);
+
+ if (num_selected == 0) // nothing selected
{
- check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_online_multiple);
- if(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) mMenuState |= LLRelationship::GRANT_ONLINE_STATUS;
+ childSetEnabled("im_btn", FALSE);
+ childSetEnabled("offer_teleport_btn", FALSE);
+
+ childSetEnabled("online_status_cb", FALSE);
+ childSetValue("online_status_cb", FALSE);
+ childSetTentative("online_status_cb", FALSE);
+
+ childSetEnabled("map_status_cb", FALSE);
+ childSetValue("map_status_cb", FALSE);
+ childSetTentative("map_status_cb", FALSE);
+
+ childSetEnabled("modify_status_cb", FALSE);
+ childSetValue("modify_status_cb", FALSE);
+ childSetTentative("modify_status_cb", FALSE);
}
-
- check = LLUICtrlFactory::getCheckBoxByName(this, "map_status_cb");
- check->setEnabled(false);
- check->set(FALSE);
- if(!mAllowRightsChange) check->setVisible(FALSE);
- else check->setVisible(TRUE);
- if(friend_status)
+ else // we have at least one friend selected...
{
- check->setEnabled(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_visibility && can_change_online_multiple);
- check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) && can_change_map_multiple);
- if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION)) mMenuState |= LLRelationship::GRANT_MAP_LOCATION;
- }
+ // only allow IMs to groups when everyone in the group is online
+ // to be consistent with context menus in inventory and because otherwise
+ // offline friends would be silently dropped from the session
+ childSetEnabled("im_btn", selected_friends_online || num_selected == 1);
- check = LLUICtrlFactory::getCheckBoxByName(this, "modify_status_cb");
- check->setEnabled(can_change_modify);
- check->set(FALSE);
- if(!mAllowRightsChange) check->setVisible(FALSE);
- else check->setVisible(TRUE);
- if(can_change_modify)
- {
- if(friend_status)
+ childSetEnabled("offer_teleport_btn", can_offer_teleport);
+
+ childSetEnabled("online_status_cb", TRUE);
+ childSetValue("online_status_cb", some_friends_see_online);
+ childSetTentative("online_status_cb", some_friends_see_online != friends_see_online);
+ if (friends_see_online)
+ {
+ mMenuState |= LLRelationship::GRANT_ONLINE_STATUS;
+ }
+
+ childSetEnabled("map_status_cb", TRUE);
+ childSetValue("map_status_cb", some_friends_see_on_map);
+ childSetTentative("map_status_cb", some_friends_see_on_map != friends_see_on_map);
+ if(friends_see_on_map)
+ {
+ mMenuState |= LLRelationship::GRANT_MAP_LOCATION;
+ }
+
+ // for now, don't allow modify rights change for multiple select
+ childSetEnabled("modify_status_cb", num_selected == 1);
+ childSetValue("modify_status_cb", some_friends_modify_objects);
+ childSetTentative("modify_status_cb", some_friends_modify_objects != friends_modify_objects);
+ if(friends_modify_objects)
{
- check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
- if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)) mMenuState |= LLRelationship::GRANT_MODIFY_OBJECTS;
+ mMenuState |= LLRelationship::GRANT_MODIFY_OBJECTS;
}
}
}
-void LLFloaterFriends::refreshNames()
+void LLPanelFriends::refreshNames()
{
LLDynamicArray<LLUUID> selected_ids = getSelectedIDs();
S32 pos = mFriendsList->getScrollPos();
@@ -389,7 +388,7 @@ void LLFloaterFriends::refreshNames()
}
-void LLFloaterFriends::refreshUI()
+void LLPanelFriends::refreshUI()
{
BOOL single_selected = FALSE;
BOOL multiple_selected = FALSE;
@@ -423,40 +422,34 @@ void LLFloaterFriends::refreshUI()
childSetEnabled("im_btn", single_selected);
childSetEnabled("friend_rights", single_selected);
- //Note: We reset this in refreshRightsChangeList since we already have to iterate
- //through all selected friends there
- childSetEnabled("offer_teleport_btn", single_selected);
-
- refreshRightsChangeList((single_selected + multiple_selected));
+ refreshRightsChangeList();
}
-// static
-LLDynamicArray<LLUUID> LLFloaterFriends::getSelectedIDs()
+LLDynamicArray<LLUUID> LLPanelFriends::getSelectedIDs()
{
LLUUID selected_id;
LLDynamicArray<LLUUID> friend_ids;
- if(sInstance)
+ std::vector<LLScrollListItem*> selected = mFriendsList->getAllSelected();
+ for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
{
- std::vector<LLScrollListItem*> selected = sInstance->mFriendsList->getAllSelected();
- for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
- {
- friend_ids.push_back((*itr)->getUUID());
- }
+ friend_ids.push_back((*itr)->getUUID());
}
return friend_ids;
}
// static
-void LLFloaterFriends::onSelectName(LLUICtrl* ctrl, void* user_data)
+void LLPanelFriends::onSelectName(LLUICtrl* ctrl, void* user_data)
{
- if(sInstance)
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ if(panelp)
{
- sInstance->refreshUI();
+ panelp->refreshUI();
}
}
//static
-void LLFloaterFriends::onMaximumSelect(void* user_data)
+void LLPanelFriends::onMaximumSelect(void* user_data)
{
LLString::format_map_t args;
args["[MAX_SELECT]"] = llformat("%d", MAX_FRIEND_SELECT);
@@ -464,10 +457,12 @@ void LLFloaterFriends::onMaximumSelect(void* user_data)
};
// static
-void LLFloaterFriends::onClickProfile(void* user_data)
+void LLPanelFriends::onClickProfile(void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickProfile()" << llendl;
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ //llinfos << "LLPanelFriends::onClickProfile()" << llendl;
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() > 0)
{
LLUUID agent_id = ids[0];
@@ -478,10 +473,12 @@ void LLFloaterFriends::onClickProfile(void* user_data)
}
// static
-void LLFloaterFriends::onClickIM(void* user_data)
+void LLPanelFriends::onClickIM(void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickIM()" << llendl;
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ //llinfos << "LLPanelFriends::onClickIM()" << llendl;
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() > 0)
{
if(ids.size() == 1)
@@ -494,8 +491,8 @@ void LLFloaterFriends::onClickIM(void* user_data)
{
char buffer[MAX_STRING]; /* Flawfinder: ignore */
snprintf(buffer, MAX_STRING, "%s %s", first, last); /* Flawfinder: ignore */
- gIMView->setFloaterOpen(TRUE);
- gIMView->addSession(
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession(
buffer,
IM_NOTHING_SPECIAL,
agent_id);
@@ -503,17 +500,18 @@ void LLFloaterFriends::onClickIM(void* user_data)
}
else
{
- gIMView->setFloaterOpen(TRUE);
- gIMView->addSession("Friends Conference",
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession("Friends Conference",
IM_SESSION_CONFERENCE_START,
ids[0],
ids);
}
+ make_ui_sound("UISndStartIM");
}
}
// static
-void LLFloaterFriends::requestFriendship(const LLUUID& target_id, const LLString& target_name)
+void LLPanelFriends::requestFriendship(const LLUUID& target_id, const LLString& target_name)
{
// HACK: folder id stored as "message"
LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
@@ -532,7 +530,7 @@ struct LLAddFriendData
};
// static
-void LLFloaterFriends::callbackAddFriend(S32 option, void* data)
+void LLPanelFriends::callbackAddFriend(S32 option, void* data)
{
LLAddFriendData* add = (LLAddFriendData*)data;
if (option == 0)
@@ -543,7 +541,7 @@ void LLFloaterFriends::callbackAddFriend(S32 option, void* data)
}
// static
-void LLFloaterFriends::onPickAvatar(const std::vector<std::string>& names,
+void LLPanelFriends::onPickAvatar(const std::vector<std::string>& names,
const std::vector<LLUUID>& ids,
void* )
{
@@ -553,7 +551,7 @@ void LLFloaterFriends::onPickAvatar(const std::vector<std::string>& names,
}
// static
-void LLFloaterFriends::requestFriendshipDialog(const LLUUID& id,
+void LLPanelFriends::requestFriendshipDialog(const LLUUID& id,
const std::string& name)
{
if(id == gAgentID)
@@ -573,16 +571,24 @@ void LLFloaterFriends::requestFriendshipDialog(const LLUUID& id,
}
// static
-void LLFloaterFriends::onClickAddFriend(void* user_data)
+void LLPanelFriends::onClickAddFriend(void* user_data)
{
- LLFloaterAvatarPicker::show(onPickAvatar, user_data, FALSE, TRUE);
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+ LLFloater* root_floater = gFloaterView->getParentFloater(panelp);
+ LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(onPickAvatar, user_data, FALSE, TRUE);
+ if (root_floater)
+ {
+ root_floater->addDependentFloater(picker);
+ }
}
// static
-void LLFloaterFriends::onClickRemove(void* user_data)
+void LLPanelFriends::onClickRemove(void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickRemove()" << llendl;
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ //llinfos << "LLPanelFriends::onClickRemove()" << llendl;
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
LLStringBase<char>::format_map_t args;
if(ids.size() > 0)
{
@@ -610,9 +616,11 @@ void LLFloaterFriends::onClickRemove(void* user_data)
}
// static
-void LLFloaterFriends::onClickOfferTeleport(void*)
+void LLPanelFriends::onClickOfferTeleport(void* user_data)
{
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() > 0)
{
handle_lure(ids);
@@ -620,43 +628,40 @@ void LLFloaterFriends::onClickOfferTeleport(void*)
}
// static
-void LLFloaterFriends::onClickPay(void*)
+void LLPanelFriends::onClickPay(void* user_data)
{
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
if(ids.size() == 1)
{
handle_pay_by_id(ids[0]);
}
}
-// static
-void LLFloaterFriends::onClickClose(void* user_data)
+void LLPanelFriends::onClickOnlineStatus(LLUICtrl* ctrl, void* user_data)
{
- //llinfos << "LLFloaterFriends::onClickClose()" << llendl;
- if(sInstance)
- {
- sInstance->onClose(false);
- }
-}
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
-void LLFloaterFriends::onClickOnlineStatus(LLUICtrl* ctrl, void* user_data)
-{
bool checked = ctrl->getValue();
- sInstance->updateMenuState(LLRelationship::GRANT_ONLINE_STATUS, checked);
- sInstance->applyRightsToFriends(LLRelationship::GRANT_ONLINE_STATUS, checked);
+ panelp->updateMenuState(LLRelationship::GRANT_ONLINE_STATUS, checked);
+ panelp->applyRightsToFriends(LLRelationship::GRANT_ONLINE_STATUS, checked);
}
-void LLFloaterFriends::onClickMapStatus(LLUICtrl* ctrl, void* user_data)
+void LLPanelFriends::onClickMapStatus(LLUICtrl* ctrl, void* user_data)
{
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
bool checked = ctrl->getValue();
- sInstance->updateMenuState(LLRelationship::GRANT_MAP_LOCATION, checked);
- sInstance->applyRightsToFriends(LLRelationship::GRANT_MAP_LOCATION, checked);
+ panelp->updateMenuState(LLRelationship::GRANT_MAP_LOCATION, checked);
+ panelp->applyRightsToFriends(LLRelationship::GRANT_MAP_LOCATION, checked);
}
-void LLFloaterFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
+void LLPanelFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
{
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
bool checked = ctrl->getValue();
- LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLDynamicArray<LLUUID> ids = panelp->getSelectedIDs();
LLStringBase<char>::format_map_t args;
if(ids.size() > 0)
{
@@ -670,33 +675,35 @@ void LLFloaterFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
args["[FIRST_NAME]"] = first;
args["[LAST_NAME]"] = last;
}
- if(checked) gViewerWindow->alertXml("GrantModifyRights", args, handleModifyRights, NULL);
- else gViewerWindow->alertXml("RevokeModifyRights", args, handleModifyRights, NULL);
+ if(checked) gViewerWindow->alertXml("GrantModifyRights", args, handleModifyRights, user_data);
+ else gViewerWindow->alertXml("RevokeModifyRights", args, handleModifyRights, user_data);
}
else return;
}
}
-void LLFloaterFriends::handleModifyRights(S32 option, void* user_data)
+void LLPanelFriends::handleModifyRights(S32 option, void* user_data)
{
- if(sInstance)
+ LLPanelFriends* panelp = (LLPanelFriends*)user_data;
+
+ if(panelp)
{
if(!option)
{
- sInstance->updateMenuState(LLRelationship::GRANT_MODIFY_OBJECTS, !((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
- sInstance->applyRightsToFriends(LLRelationship::GRANT_MODIFY_OBJECTS, ((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ panelp->updateMenuState(LLRelationship::GRANT_MODIFY_OBJECTS, !((panelp->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ panelp->applyRightsToFriends(LLRelationship::GRANT_MODIFY_OBJECTS, ((panelp->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
}
- sInstance->refreshUI();
+ panelp->refreshUI();
}
}
-void LLFloaterFriends::updateMenuState(S32 flag, BOOL value)
+void LLPanelFriends::updateMenuState(S32 flag, BOOL value)
{
if(value) mMenuState |= flag;
else mMenuState &= ~flag;
}
-void LLFloaterFriends::applyRightsToFriends(S32 flag, BOOL value)
+void LLPanelFriends::applyRightsToFriends(S32 flag, BOOL value)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_GrantUserRights);
@@ -725,7 +732,7 @@ void LLFloaterFriends::applyRightsToFriends(S32 flag, BOOL value)
// static
-void LLFloaterFriends::handleRemove(S32 option, void* user_data)
+void LLPanelFriends::handleRemove(S32 option, void* user_data)
{
LLDynamicArray<LLUUID>* ids = static_cast<LLDynamicArray<LLUUID>*>(user_data);
for(LLDynamicArray<LLUUID>::iterator itr = ids->begin(); itr != ids->end(); ++itr)
diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h
index c6ef365517..6895e469e5 100644
--- a/indra/newview/llfloaterfriends.h
+++ b/indra/newview/llfloaterfriends.h
@@ -11,7 +11,7 @@
#ifndef LL_LLFLOATERFRIENDS_H
#define LL_LLFLOATERFRIENDS_H
-#include "llfloater.h"
+#include "llpanel.h"
#include "llstring.h"
#include "lluuid.h"
#include "lltimer.h"
@@ -20,24 +20,23 @@ class LLFriendObserver;
/**
- * @class LLFloaterFriends
+ * @class LLPanelFriends
* @brief An instance of this class is used for displaying your friends
* and gives you quick access to all agents which a user relationship.
*
* @sa LLFloater
*/
-class LLFloaterFriends : public LLFloater, public LLEventTimer
+class LLPanelFriends : public LLPanel, public LLEventTimer
{
public:
- virtual ~LLFloaterFriends();
+ LLPanelFriends();
+ virtual ~LLPanelFriends();
/**
* @brief This method either creates or brings to the front the
* current instantiation of this floater. There is only once since
* you can currently only look at your local friends.
*/
- static void show(void* ignored = NULL);
-
virtual void tick();
/**
@@ -48,11 +47,6 @@ public:
virtual BOOL postBuild();
- static BOOL visible(void* unused = NULL);
-
- // Toggles visibility of floater
- static void toggle(void* unused = NULL);
-
// Show a dialog explaining what friendship entails, then request
// friendship. JC
static void requestFriendshipDialog(const LLUUID& target_id,
@@ -75,19 +69,18 @@ private:
};
// protected members
- LLFloaterFriends();
void reloadNames();
void refreshNames();
void refreshUI();
- void refreshRightsChangeList(U8 state);
+ void refreshRightsChangeList();
void applyRightsToFriends(S32 flag, BOOL value);
void updateMenuState(S32 flag, BOOL value);
S32 getMenuState() { return mMenuState; }
void addFriend(const std::string& name, const LLUUID& agent_id);
// return LLUUID::null if nothing is selected
- static LLDynamicArray<LLUUID> getSelectedIDs();
+ LLDynamicArray<LLUUID> getSelectedIDs();
// callback methods
static void onSelectName(LLUICtrl* ctrl, void* user_data);
@@ -103,8 +96,6 @@ private:
static void onClickOfferTeleport(void* user_data);
static void onClickPay(void* user_data);
- static void onClickClose(void* user_data);
-
static void onClickOnlineStatus(LLUICtrl* ctrl, void* user_data);
static void onClickMapStatus(LLUICtrl* ctrl, void* user_data);
static void onClickModifyStatus(LLUICtrl* ctrl, void* user_data);
@@ -113,9 +104,6 @@ private:
static void handleModifyRights(S32 option, void* user_data);
private:
- // static data
- static LLFloaterFriends* sInstance;
-
// member data
LLFriendObserver* mObserver;
LLUUID mAddFriendID;
diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp
index 4535a11ec3..60cfa3b809 100644
--- a/indra/newview/llfloatergroups.cpp
+++ b/indra/newview/llfloatergroups.cpp
@@ -1,6 +1,6 @@
/**
* @file llfloatergroups.cpp
- * @brief LLFloaterGroups class implementation
+ * @brief LLPanelGroups class implementation
*
* Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
* $License$
@@ -30,115 +30,135 @@
#include "lltextbox.h"
#include "llvieweruictrlfactory.h"
#include "llviewerwindow.h"
-
-const LLRect FLOATER_RECT(0, 258, 280, 0);
-const char FLOATER_TITLE[] = "Groups";
+#include "llimview.h"
// static
-LLMap<const LLUUID, LLFloaterGroups*> LLFloaterGroups::sInstances;
+std::map<const LLUUID, LLFloaterGroupPicker*> LLFloaterGroupPicker::sInstances;
+// helper functions
+void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id);
///----------------------------------------------------------------------------
-/// Class LLFloaterGroups
+/// Class LLFloaterGroupPicker
///----------------------------------------------------------------------------
-//LLEventListener
-//virtual
-bool LLFloaterGroups::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+// static
+LLFloaterGroupPicker* LLFloaterGroupPicker::findInstance(const LLSD& seed)
{
- if (event->desc() == "new group")
+ instance_map_t::iterator found_it = sInstances.find(seed.asUUID());
+ if (found_it != sInstances.end())
{
- reset();
- return true;
+ return found_it->second;
}
-
- return LLView::handleEvent(event, userdata);
+ return NULL;
}
-// Call this with an agent id and AGENT_GROUPS for an agent's
-// groups, otherwise, call with an object id and SET_OBJECT_GROUP
-// when modifying an object.
// static
-LLFloaterGroups* LLFloaterGroups::show(const LLUUID& id, EGroupDialog type)
+LLFloaterGroupPicker* LLFloaterGroupPicker::createInstance(const LLSD &seed)
+{
+ LLFloaterGroupPicker* pickerp = new LLFloaterGroupPicker(seed);
+ gUICtrlFactory->buildFloater(pickerp, "floater_choose_group.xml");
+ return pickerp;
+}
+
+LLFloaterGroupPicker::LLFloaterGroupPicker(const LLSD& seed) :
+ mSelectCallback(NULL),
+ mCallbackUserdata(NULL)
+{
+ mID = seed.asUUID();
+ sInstances.insert(std::make_pair(mID, this));
+}
+
+LLFloaterGroupPicker::~LLFloaterGroupPicker()
+{
+ sInstances.erase(mID);
+}
+
+void LLFloaterGroupPicker::setSelectCallback(void (*callback)(LLUUID, void*),
+ void* userdata)
{
- LLFloaterGroups* instance = NULL;
- if(sInstances.checkData(id))
+ mSelectCallback = callback;
+ mCallbackUserdata = userdata;
+}
+
+BOOL LLFloaterGroupPicker::postBuild()
+{
+ init_group_list(LLUICtrlFactory::getScrollListByName(this, "group list"), gAgent.getGroupID());
+
+ childSetAction("OK", onBtnOK, this);
+
+ childSetAction("Cancel", onBtnCancel, this);
+
+ setDefaultBtn("OK");
+
+ childSetDoubleClickCallback("group list", onBtnOK);
+ childSetUserData("group list", this);
+
+ childEnable("OK");
+
+ return TRUE;
+}
+
+void LLFloaterGroupPicker::onBtnOK(void* userdata)
+{
+ LLFloaterGroupPicker* self = (LLFloaterGroupPicker*)userdata;
+ if(self) self->ok();
+}
+
+void LLFloaterGroupPicker::onBtnCancel(void* userdata)
+{
+ LLFloaterGroupPicker* self = (LLFloaterGroupPicker*)userdata;
+ if(self) self->close();
+}
+
+
+void LLFloaterGroupPicker::ok()
+{
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
{
- instance = sInstances.getData(id);
- if (instance->getType() != type)
- {
- // not the type we want ==> destroy it and rebuild below
- instance->destroy();
- instance = NULL;
- }
- else
- {
- // Move the existing view to the front
- instance->open(); /* Flawfinder: ignore */
- }
+ group_id = group_list->getCurrentID();
}
-
- if (!instance)
+ if(mSelectCallback)
{
- S32 left = 0;
- S32 top = 0;
- LLRect rect = FLOATER_RECT;
- rect.translate( left - rect.mLeft, top - rect.mTop );
- instance = new LLFloaterGroups("groups", rect, FLOATER_TITLE, id);
- if(instance)
- {
- sInstances.addData(id, instance);
- //instance->init(type);
- instance->mType = type;
- switch (type)
- {
- case AGENT_GROUPS:
- gUICtrlFactory->buildFloater(instance, "floater_groups.xml");
- break;
- case CHOOSE_ONE:
- gUICtrlFactory->buildFloater(instance, "floater_choose_group.xml");
- break;
- }
- instance->center();
- instance->open(); /*Flawfinder: ignore*/
- }
+ mSelectCallback(group_id, mCallbackUserdata);
}
- return instance;
+
+ close();
}
-// static
-LLFloaterGroups* LLFloaterGroups::getInstance(const LLUUID& id)
+///----------------------------------------------------------------------------
+/// Class LLPanelGroups
+///----------------------------------------------------------------------------
+
+//LLEventListener
+//virtual
+bool LLPanelGroups::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
- if(sInstances.checkData(id))
+ if (event->desc() == "new group")
{
- return sInstances.getData(id);
+ reset();
+ return true;
}
- return NULL;
+
+ return LLView::handleEvent(event, userdata);
}
// Default constructor
-LLFloaterGroups::LLFloaterGroups(const std::string& name,
- const LLRect& rect,
- const std::string& title,
- const LLUUID& id) :
- LLFloater(name, rect, title),
- mID(id),
- mType(AGENT_GROUPS),
- mOKCallback(NULL),
- mCallbackUserdata(NULL)
+LLPanelGroups::LLPanelGroups() :
+ LLPanel()
{
+ gAgent.addListener(this, "new group");
}
-// Destroys the object
-LLFloaterGroups::~LLFloaterGroups()
+LLPanelGroups::~LLPanelGroups()
{
- gFocusMgr.releaseFocusIfNeeded( this );
-
- sInstances.removeData(mID);
+ gAgent.removeListener(this);
}
// clear the group list, and get a fresh set of info.
-void LLFloaterGroups::reset()
+void LLPanelGroups::reset()
{
LLCtrlListInterface *group_list = childGetListInterface("group list");
if (group_list)
@@ -148,215 +168,126 @@ void LLFloaterGroups::reset()
childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
- initAgentGroups(gAgent.getGroupID());
+ init_group_list(LLUICtrlFactory::getScrollListByName(this, "group list"), gAgent.getGroupID());
enableButtons();
}
-void LLFloaterGroups::setOkCallback(void (*callback)(LLUUID, void*),
- void* userdata)
-{
- mOKCallback = callback;
- mCallbackUserdata = userdata;
-}
-
-BOOL LLFloaterGroups::postBuild()
+BOOL LLPanelGroups::postBuild()
{
childSetCommitCallback("group list", onGroupList, this);
- if(mType == AGENT_GROUPS)
- {
- childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
- childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
-
- initAgentGroups(gAgent.getGroupID());
-
- childSetAction("Activate", onBtnActivate, this);
+ childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+ childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
- childSetAction("Info", onBtnInfo, this);
+ init_group_list(LLUICtrlFactory::getScrollListByName(this, "group list"), gAgent.getGroupID());
- childSetAction("Leave", onBtnLeave, this);
+ childSetAction("Activate", onBtnActivate, this);
- childSetAction("Create", onBtnCreate, this);
+ childSetAction("Info", onBtnInfo, this);
- childSetAction("Search...", onBtnSearch, this);
+ childSetAction("IM", onBtnIM, this);
- childSetAction("Close", onBtnCancel, this);
+ childSetAction("Leave", onBtnLeave, this);
- setDefaultBtn("Info");
+ childSetAction("Create", onBtnCreate, this);
- childSetDoubleClickCallback("group list", onBtnInfo);
- childSetUserData("group list", this);
- }
- else
- {
- initAgentGroups(gAgent.getGroupID());
-
- childSetAction("OK", onBtnOK, this);
+ childSetAction("Search...", onBtnSearch, this);
- childSetAction("Cancel", onBtnCancel, this);
+ setDefaultBtn("IM");
- setDefaultBtn("OK");
+ childSetDoubleClickCallback("group list", onBtnIM);
+ childSetUserData("group list", this);
- childSetDoubleClickCallback("group list", onBtnOK);
- childSetUserData("group list", this);
- }
-
- enableButtons();
+ reset();
return TRUE;
}
-void LLFloaterGroups::initAgentGroups(const LLUUID& highlight_id)
+void LLPanelGroups::enableButtons()
{
- S32 count = gAgent.mGroups.count();
- LLUUID id;
LLCtrlListInterface *group_list = childGetListInterface("group list");
- if (!group_list) return;
-
- group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
-
- for(S32 i = 0; i < count; ++i)
+ LLUUID group_id;
+ if (group_list)
{
- id = gAgent.mGroups.get(i).mID;
- LLGroupData* group_datap = &gAgent.mGroups.get(i);
- LLString style = "NORMAL";
- if(highlight_id == id)
- {
- style = "BOLD";
- }
-
- LLSD element;
- element["id"] = id;
- element["columns"][0]["column"] = "name";
- element["columns"][0]["value"] = group_datap->mName;
- element["columns"][0]["font"] = "SANSSERIF";
- element["columns"][0]["font-style"] = style;
-
- group_list->addElement(element, ADD_SORTED);
+ group_id = group_list->getCurrentID();
}
+ if(group_id != gAgent.getGroupID())
{
- LLString style = "NORMAL";
- if (highlight_id.isNull())
- {
- style = "BOLD";
- }
- LLSD element;
- element["id"] = LLUUID::null;
- element["columns"][0]["column"] = "name";
- element["columns"][0]["value"] = "none";
- element["columns"][0]["font"] = "SANSSERIF";
- element["columns"][0]["font-style"] = style;
-
- group_list->addElement(element, ADD_TOP);
+ childEnable("Activate");
}
-
- group_list->selectByValue(highlight_id);
-
- childSetFocus("group list");
-}
-
-void LLFloaterGroups::enableButtons()
-{
- LLCtrlListInterface *group_list = childGetListInterface("group list");
- LLUUID group_id;
- if (group_list)
+ else
{
- group_id = group_list->getCurrentID();
+ childDisable("Activate");
}
- if(mType == AGENT_GROUPS)
+ if (group_id.notNull())
{
- if(group_id != gAgent.getGroupID())
- {
- childEnable("Activate");
- }
- else
- {
- childDisable("Activate");
- }
- if (group_id.notNull())
- {
- childEnable("Info");
- childEnable("Leave");
- }
- else
- {
- childDisable("Info");
- childDisable("Leave");
- }
- if(gAgent.mGroups.count() < MAX_AGENT_GROUPS)
- {
- childEnable("Create");
- }
- else
- {
- childDisable("Create");
- }
+ childEnable("Info");
+ childEnable("IM");
+ childEnable("Leave");
}
else
{
- childEnable("OK");
+ childDisable("Info");
+ childDisable("IM");
+ childDisable("Leave");
+ }
+ if(gAgent.mGroups.count() < MAX_AGENT_GROUPS)
+ {
+ childEnable("Create");
+ }
+ else
+ {
+ childDisable("Create");
}
}
-void LLFloaterGroups::onBtnCreate(void* userdata)
+void LLPanelGroups::onBtnCreate(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
if(self) self->create();
}
-void LLFloaterGroups::onBtnActivate(void* userdata)
+void LLPanelGroups::onBtnActivate(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
if(self) self->activate();
}
-void LLFloaterGroups::onBtnInfo(void* userdata)
+void LLPanelGroups::onBtnInfo(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
if(self) self->info();
}
-void LLFloaterGroups::onBtnLeave(void* userdata)
+void LLPanelGroups::onBtnIM(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->leave();
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->startIM();
}
-void LLFloaterGroups::onBtnSearch(void* userdata)
+void LLPanelGroups::onBtnLeave(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->search();
-}
-
-void LLFloaterGroups::onBtnOK(void* userdata)
-{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->ok();
-}
-
-void LLFloaterGroups::onBtnCancel(void* userdata)
-{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->close();
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->leave();
}
-void LLFloaterGroups::onGroupList(LLUICtrl* ctrl, void* userdata)
+void LLPanelGroups::onBtnSearch(void* userdata)
{
- LLFloaterGroups* self = (LLFloaterGroups*)userdata;
- if(self) self->highlightGroupList(ctrl);
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->search();
}
-void LLFloaterGroups::create()
+void LLPanelGroups::create()
{
- llinfos << "LLFloaterGroups::create" << llendl;
+ llinfos << "LLPanelGroups::create" << llendl;
LLFloaterGroupInfo::showCreateGroup(NULL);
}
-void LLFloaterGroups::activate()
+void LLPanelGroups::activate()
{
- llinfos << "LLFloaterGroups::activate" << llendl;
+ llinfos << "LLPanelGroups::activate" << llendl;
LLCtrlListInterface *group_list = childGetListInterface("group list");
LLUUID group_id;
if (group_list)
@@ -372,9 +303,9 @@ void LLFloaterGroups::activate()
gAgent.sendReliableMessage();
}
-void LLFloaterGroups::info()
+void LLPanelGroups::info()
{
- llinfos << "LLFloaterGroups::info" << llendl;
+ llinfos << "LLPanelGroups::info" << llendl;
LLCtrlListInterface *group_list = childGetListInterface("group list");
LLUUID group_id;
if (group_list && (group_id = group_list->getCurrentID()).notNull())
@@ -383,9 +314,36 @@ void LLFloaterGroups::info()
}
}
-void LLFloaterGroups::leave()
+void LLPanelGroups::startIM()
+{
+ //llinfos << "LLPanelFriends::onClickIM()" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+
+ if (group_list && (group_id = group_list->getCurrentID()).notNull())
+ {
+ LLGroupData group_data;
+ if (gAgent.getGroupData(group_id, group_data))
+ {
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession(
+ group_data.mName,
+ IM_SESSION_GROUP_START,
+ group_id);
+ make_ui_sound("UISndStartIM");
+ }
+ else
+ {
+ // this should never happen, as starting a group IM session
+ // relies on you belonging to the group and hence having the group data
+ make_ui_sound("UISndInvalidOp");
+ }
+ }
+}
+
+void LLPanelGroups::leave()
{
- llinfos << "LLFloaterGroups::leave" << llendl;
+ llinfos << "LLPanelGroups::leave" << llendl;
LLCtrlListInterface *group_list = childGetListInterface("group list");
LLUUID group_id;
if (group_list && (group_id = group_list->getCurrentID()).notNull())
@@ -407,13 +365,13 @@ void LLFloaterGroups::leave()
}
}
-void LLFloaterGroups::search()
+void LLPanelGroups::search()
{
LLFloaterDirectory::showGroups();
}
// static
-void LLFloaterGroups::callbackLeaveGroup(S32 option, void* userdata)
+void LLPanelGroups::callbackLeaveGroup(S32 option, void* userdata)
{
LLUUID* group_id = (LLUUID*)userdata;
if(option == 0 && group_id)
@@ -430,25 +388,58 @@ void LLFloaterGroups::callbackLeaveGroup(S32 option, void* userdata)
delete group_id;
}
-void LLFloaterGroups::ok()
+void LLPanelGroups::onGroupList(LLUICtrl* ctrl, void* userdata)
{
- llinfos << "LLFloaterGroups::ok" << llendl;
- LLCtrlListInterface *group_list = childGetListInterface("group list");
- LLUUID group_id;
- if (group_list)
+ LLPanelGroups* self = (LLPanelGroups*)userdata;
+ if(self) self->enableButtons();
+}
+
+void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id)
+{
+ S32 count = gAgent.mGroups.count();
+ LLUUID id;
+ LLCtrlListInterface *group_list = ctrl->getListInterface();
+ if (!group_list) return;
+
+ group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ for(S32 i = 0; i < count; ++i)
{
- group_id = group_list->getCurrentID();
+ id = gAgent.mGroups.get(i).mID;
+ LLGroupData* group_datap = &gAgent.mGroups.get(i);
+ LLString style = "NORMAL";
+ if(highlight_id == id)
+ {
+ style = "BOLD";
+ }
+
+ LLSD element;
+ element["id"] = id;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = group_datap->mName;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_SORTED);
}
- if(mOKCallback)
+
+ // add "none" to list at top
{
- mOKCallback(group_id, mCallbackUserdata);
+ LLString style = "NORMAL";
+ if (highlight_id.isNull())
+ {
+ style = "BOLD";
+ }
+ LLSD element;
+ element["id"] = LLUUID::null;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = "none";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_TOP);
}
- close();
+ group_list->selectByValue(highlight_id);
}
-void LLFloaterGroups::highlightGroupList(LLUICtrl*)
-{
- llinfos << "LLFloaterGroups::highlightGroupList" << llendl;
- enableButtons();
-}
diff --git a/indra/newview/llfloatergroups.h b/indra/newview/llfloatergroups.h
index 6f73c97553..65bed95ea8 100644
--- a/indra/newview/llfloatergroups.h
+++ b/indra/newview/llfloatergroups.h
@@ -21,89 +21,80 @@
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "lluuid.h"
-#include "llmap.h"
-#include "llevent.h"
#include "llfloater.h"
+#include <map>
class LLUICtrl;
class LLTextBox;
class LLScrollListCtrl;
class LLButton;
+class LLFloaterGroupPicker;
-class LLFloaterGroups : public LLFloater
+class LLFloaterGroupPicker : public LLFloater, public LLUIInstanceMgr<LLFloaterGroupPicker>
{
+ friend class LLUIInstanceMgr<LLFloaterGroupPicker>;
public:
+ ~LLFloaterGroupPicker();
+ void setSelectCallback( void (*callback)(LLUUID, void*),
+ void* userdata);
+ BOOL postBuild();
+
+protected:
+ LLFloaterGroupPicker(const LLSD& seed);
+ void ok();
+ static LLFloaterGroupPicker* findInstance(const LLSD& seed);
+ static LLFloaterGroupPicker* createInstance(const LLSD& seed);
+ static void onBtnOK(void* userdata);
+ static void onBtnCancel(void* userdata);
+
+protected:
+ LLUUID mID;
+ void (*mSelectCallback)(LLUUID id, void* userdata);
+ void* mCallbackUserdata;
+
+ typedef std::map<const LLUUID, LLFloaterGroupPicker*> instance_map_t;
+ static instance_map_t sInstances;
+};
+
+class LLPanelGroups : public LLPanel
+{
+public:
+ LLPanelGroups();
+ virtual ~LLPanelGroups();
+
//LLEventListener
/*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
- enum EGroupDialog
- {
- AGENT_GROUPS,
- CHOOSE_ONE
- };
- // Call this with an agent id and AGENT_GROUPS for an agent's
- // groups, otherwise, call with an object id and SET_OBJECT_GROUP
- // when modifying an object.
- static LLFloaterGroups* show(const LLUUID& id, EGroupDialog type);
-
- // Return the instance requested if it already exists. Otherwise,
- // return NULL.
- static LLFloaterGroups* getInstance(const LLUUID& id);
-
// clear the group list, and get a fresh set of info.
void reset();
- void setOkCallback( void (*callback)(LLUUID, void*),
- void* userdata);
-
- EGroupDialog getType() const { return mType; }
-
protected:
// initialize based on the type
BOOL postBuild();
// highlight_id is a group id to highlight
- void initAgentGroups(const LLUUID& highlight_id);
void enableButtons();
+ static void onGroupList(LLUICtrl* ctrl, void* userdata);
static void onBtnCreate(void* userdata);
static void onBtnActivate(void* userdata);
static void onBtnInfo(void* userdata);
+ static void onBtnIM(void* userdata);
static void onBtnLeave(void* userdata);
static void onBtnSearch(void* userdata);
static void onBtnVote(void* userdata);
- static void onBtnOK(void* userdata);
- static void onBtnCancel(void* userdata);
- static void onGroupList(LLUICtrl* ctrl, void* userdata);
static void onDoubleClickGroup(void* userdata);
void create();
void activate();
void info();
+ void startIM();
void leave();
void search();
void callVote();
- void ok();
- void highlightGroupList(LLUICtrl*);
static void callbackLeaveGroup(S32 option, void* userdata);
-protected:
- LLUUID mID;
-
- EGroupDialog mType;
-
- void (*mOKCallback)(LLUUID id, void* userdata);
- void* mCallbackUserdata;
-
-protected:
- static LLMap<const LLUUID, LLFloaterGroups*> sInstances;
-
-public:
- // do not call these directly
- LLFloaterGroups(const std::string& name, const LLRect& rect, const std::string& title,
- const LLUUID& id);
- virtual ~LLFloaterGroups();
};
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 4b18c0c91a..421066688e 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -22,6 +22,7 @@
#include "llfloateravatarpicker.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
+#include "llradiogroup.h"
#include "llcombobox.h"
#include "llfloaterauction.h"
#include "llfloateravatarinfo.h"
@@ -90,6 +91,14 @@ static const char RAW_HTML[] = "Raw HTML";
static const BOOL BUY_GROUP_LAND = TRUE;
static const BOOL BUY_PERSONAL_LAND = FALSE;
+// Values for the parcel voice settings radio group
+enum
+{
+ kRadioVoiceChatEstate = 0,
+ kRadioVoiceChatPrivate = 1,
+ kRadioVoiceChatDisable = 2
+};
+
// Statics
LLFloaterLand* LLFloaterLand::sInstance = NULL;
LLParcelSelectionObserver* LLFloaterLand::sObserver = NULL;
@@ -808,9 +817,20 @@ void LLPanelLandGeneral::draw()
// static
void LLPanelLandGeneral::onClickSetGroup(void* userdata)
{
- LLFloaterGroups* fg;
- fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
- fg->setOkCallback( cbGroupID, userdata );
+ LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)userdata;
+ LLFloaterGroupPicker* fg;
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+
+ fg = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID()));
+ fg->setSelectCallback( cbGroupID, userdata );
+
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, fg);
+ fg->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(fg);
+ }
}
// static
@@ -2319,6 +2339,9 @@ BOOL LLPanelLandMedia::postBuild()
mCheckSoundLocal = LLUICtrlFactory::getCheckBoxByName(this, "check sound local");
childSetCommitCallback("check sound local", onCommitAny, this);
+ mRadioVoiceChat = LLUICtrlFactory::getRadioGroupByName(this, "parcel_voice_channel");
+ childSetCommitCallback("parcel_voice_channel", onCommitAny, this);
+
mMusicURLEdit = LLUICtrlFactory::getLineEditorByName(this, "music_url");
childSetCommitCallback("music_url", onCommitAny, this);
@@ -2362,6 +2385,9 @@ void LLPanelLandMedia::refresh()
mCheckSoundLocal->set(FALSE);
mCheckSoundLocal->setEnabled(FALSE);
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatEstate);
+ mRadioVoiceChat->setEnabled(FALSE);
+
mMusicURLEdit->setText("");
mMusicURLEdit->setEnabled(FALSE);
@@ -2389,6 +2415,20 @@ void LLPanelLandMedia::refresh()
mCheckSoundLocal->set( parcel->getSoundLocal() );
mCheckSoundLocal->setEnabled( can_change_media );
+ if(parcel->getVoiceEnabled())
+ {
+ if(parcel->getVoiceUseEstateChannel())
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatEstate);
+ else
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatPrivate);
+ }
+ else
+ {
+ mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatDisable);
+ }
+
+ mRadioVoiceChat->setEnabled( can_change_media );
+
// don't display urls if you're not able to change it
// much requested change in forums so people can't 'steal' urls
// NOTE: bug#2009 means this is still vunerable - however, bug
@@ -2448,16 +2488,39 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl *ctrl, void *userdata)
// Extract data from UI
BOOL sound_local = self->mCheckSoundLocal->get();
+ int voice_setting = self->mRadioVoiceChat->getSelectedIndex();
std::string music_url = self->mMusicURLEdit->getText();
std::string media_url = self->mMediaURLEdit->getText();
U8 media_auto_scale = self->mMediaAutoScaleCheck->get();
LLUUID media_id = self->mMediaTextureCtrl->getImageAssetID();
+ BOOL voice_enabled;
+ BOOL voice_estate_chan;
+
+ switch(voice_setting)
+ {
+ default:
+ case kRadioVoiceChatEstate:
+ voice_enabled = TRUE;
+ voice_estate_chan = TRUE;
+ break;
+ case kRadioVoiceChatPrivate:
+ voice_enabled = TRUE;
+ voice_estate_chan = FALSE;
+ break;
+ case kRadioVoiceChatDisable:
+ voice_enabled = FALSE;
+ voice_estate_chan = FALSE;
+ break;
+ }
+
// Remove leading/trailing whitespace (common when copying/pasting)
LLString::trim(music_url);
LLString::trim(media_url);
// Push data into current parcel
+ parcel->setParcelFlag(PF_ALLOW_VOICE_CHAT, voice_enabled);
+ parcel->setParcelFlag(PF_USE_ESTATE_VOICE_CHAN, voice_estate_chan);
parcel->setParcelFlag(PF_SOUND_LOCAL, sound_local);
parcel->setMusicURL(music_url.c_str());
parcel->setMediaURL(media_url.c_str());
diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h
index 6c0fb7319f..ab7ff3921c 100644
--- a/indra/newview/llfloaterland.h
+++ b/indra/newview/llfloaterland.h
@@ -21,6 +21,7 @@ const F32 CACHE_REFRESH_TIME = 2.5f;
class LLTextBox;
class LLCheckBoxCtrl;
+class LLRadioGroup;
class LLComboBox;
class LLButton;
class LLNameListCtrl;
@@ -348,6 +349,7 @@ public:
protected:
LLCheckBoxCtrl* mCheckSoundLocal;
+ LLRadioGroup* mRadioVoiceChat;
LLLineEditor* mMusicURLEdit;
LLLineEditor* mMediaURLEdit;
LLTextureCtrl* mMediaTextureCtrl;
diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp
index 602a198a03..2553c36f66 100644
--- a/indra/newview/llfloaternamedesc.cpp
+++ b/indra/newview/llfloaternamedesc.cpp
@@ -193,7 +193,7 @@ void LLFloaterNameDesc::onBtnOK( void* userdata )
fp->childGetValue("name_form").asString(),
fp->childGetValue("description_form").asString(),
bitrate, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
- fp->onClose(false);
+ fp->close(false);
}
// static
@@ -203,5 +203,5 @@ void LLFloaterNameDesc::onBtnOK( void* userdata )
void LLFloaterNameDesc::onBtnCancel( void* userdata )
{
LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata;
- fp->onClose(false);
+ fp->close(false);
}
diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp
index 3eaff0101c..e44dfe7d85 100644
--- a/indra/newview/llfloaterpostcard.cpp
+++ b/indra/newview/llfloaterpostcard.cpp
@@ -203,7 +203,7 @@ void LLFloaterPostcard::onClickCancel(void* data)
{
LLFloaterPostcard *self = (LLFloaterPostcard *)data;
- self->onClose(false);
+ self->close(false);
}
}
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 8a1a0dcaaa..ef68e5bec2 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -34,6 +34,7 @@
#include "llpanelmsgs.h"
#include "llpanelweb.h"
#include "llprefschat.h"
+#include "llprefsvoice.h"
#include "llprefsim.h"
#include "llresizehandle.h"
#include "llresmgr.h"
@@ -124,6 +125,10 @@ LLPreferenceCore::LLPreferenceCore(LLTabContainerCommon* tab_container, LLButton
mTabContainer->addTabPanel(mPrefsChat->getPanel(), mPrefsChat->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
mPrefsChat->getPanel()->setDefaultBtn(default_btn);
+ mPrefsVoice = new LLPrefsVoice();
+ mTabContainer->addTabPanel(mPrefsVoice, mPrefsVoice->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mPrefsVoice->setDefaultBtn(default_btn);
+
mPrefsIM = new LLPrefsIM();
mTabContainer->addTabPanel(mPrefsIM->getPanel(), mPrefsIM->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
mPrefsIM->getPanel()->setDefaultBtn(default_btn);
@@ -205,8 +210,8 @@ void LLPreferenceCore::apply()
mDisplayPanel->apply();
mDisplayPanel2->apply();
mDisplayPanel3->apply();
- mAudioPanel->apply();
mPrefsChat->apply();
+ mPrefsVoice->apply();
mPrefsIM->apply();
mMsgPanel->apply();
#if LL_LIBXUL_ENABLED
@@ -225,6 +230,7 @@ void LLPreferenceCore::cancel()
mDisplayPanel3->cancel();
mAudioPanel->cancel();
mPrefsChat->cancel();
+ mPrefsVoice->cancel();
mPrefsIM->cancel();
mMsgPanel->cancel();
#if LL_LIBXUL_ENABLED
@@ -368,7 +374,7 @@ void LLFloaterPreference::onBtnOK( void* userdata )
if (fp->canClose())
{
fp->apply();
- fp->onClose(false);
+ fp->close(false);
gSavedSettings.saveToFile( gSettingsFileName, TRUE );
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index dcfa123137..1de960c640 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -29,6 +29,7 @@ class LLPanelNetwork;
class LLPanelWeb;
class LLMessageSystem;
class LLPrefsChat;
+class LLPrefsVoice;
class LLPrefsIM;
class LLPanelMsgs;
class LLScrollListCtrl;
@@ -63,6 +64,7 @@ private:
LLPanelAudioPrefs *mAudioPanel;
// LLPanelDebug *mDebugPanel;
LLPrefsChat *mPrefsChat;
+ LLPrefsVoice *mPrefsVoice;
LLPrefsIM *mPrefsIM;
LLPanelMsgs *mMsgPanel;
LLPanelWeb *mWebPanel;
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 1c287a187a..efbf8a7826 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -1410,11 +1410,21 @@ void LLPanelEstateInfo::addAllowedGroup(S32 option, void* user_data)
{
if (option != 0) return;
- LLFloaterGroups* widget;
- widget = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ LLPanelEstateInfo* panelp = (LLPanelEstateInfo*)user_data;
+
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+
+ LLFloaterGroupPicker* widget;
+ widget = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID()));
if (widget)
{
- widget->setOkCallback(addAllowedGroup2, user_data);
+ widget->setSelectCallback(addAllowedGroup2, user_data);
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, widget);
+ widget->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(widget);
+ }
}
}
@@ -1910,6 +1920,7 @@ BOOL LLPanelEstateInfo::postBuild()
initCtrl("deny_anonymous");
initCtrl("deny_identified");
initCtrl("deny_transacted");
+ initCtrl("voice_chat_check");
initHelpBtn("estate_manager_help", "HelpEstateEstateManager");
initHelpBtn("use_global_time_help", "HelpEstateUseGlobalTime");
@@ -1919,6 +1930,7 @@ BOOL LLPanelEstateInfo::postBuild()
initHelpBtn("allow_resident_help", "HelpEstateAllowResident");
initHelpBtn("allow_group_help", "HelpEstateAllowGroup");
initHelpBtn("ban_resident_help", "HelpEstateBanResident");
+ initHelpBtn("voice_chat_help", "HelpEstateVoiceChat");
// set up the use global time checkbox
childSetCommitCallback("use_global_time_check", onChangeUseGlobalTime, this);
@@ -2084,6 +2096,9 @@ void LLPanelEstateInfo::setEstateFlags(U32 flags)
{
childSetValue("externally_visible_check", LLSD(flags & REGION_FLAGS_EXTERNALLY_VISIBLE ? TRUE : FALSE) );
childSetValue("fixed_sun_check", LLSD(flags & REGION_FLAGS_SUN_FIXED ? TRUE : FALSE) );
+ childSetValue(
+ "voice_chat_check",
+ LLSD(flags & REGION_FLAGS_ALLOW_VOICE ? TRUE : FALSE));
childSetValue("allow_direct_teleport", LLSD(flags & REGION_FLAGS_ALLOW_DIRECT_TELEPORT ? TRUE : FALSE) );
childSetValue("deny_anonymous", LLSD(flags & REGION_FLAGS_DENY_ANONYMOUS ? TRUE : FALSE) );
childSetValue("deny_identified", LLSD(flags & REGION_FLAGS_DENY_IDENTIFIED ? TRUE : FALSE) );
@@ -2099,6 +2114,11 @@ U32 LLPanelEstateInfo::computeEstateFlags()
{
flags |= REGION_FLAGS_EXTERNALLY_VISIBLE;
}
+
+ if ( childGetValue("voice_chat_check").asBoolean() )
+ {
+ flags |= REGION_FLAGS_ALLOW_VOICE;
+ }
if (childGetValue("allow_direct_teleport").asBoolean())
{
diff --git a/indra/newview/llfloaterscriptdebug.cpp b/indra/newview/llfloaterscriptdebug.cpp
index 0ba75b71f6..4d2f012396 100644
--- a/indra/newview/llfloaterscriptdebug.cpp
+++ b/indra/newview/llfloaterscriptdebug.cpp
@@ -93,7 +93,7 @@ LLFloater* LLFloaterScriptDebug::addOutputWindow(const LLUUID &object_id)
LLFloater::setFloaterHost(NULL);
// Tabs sometimes overlap resize handle
- sInstance->moveResizeHandleToFront();
+ sInstance->moveResizeHandlesToFront();
return floaterp;
}
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 80e32f9c00..5feb8b2f0d 100644
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -361,8 +361,7 @@ void LLSnapshotLivePreview::draw()
F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
// draw "shine" effect
- LLGLEnable scissor_test(GL_SCISSOR_TEST);
- LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+ LLLocalClipRect clip(getLocalRect());
{
// draw diagonal stripe with gradient that passes over screen
S32 x1 = gViewerWindow->getWindowWidth() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
index b708a4261f..0da0958c9b 100644
--- a/indra/newview/llfloatertools.cpp
+++ b/indra/newview/llfloatertools.cpp
@@ -53,7 +53,6 @@
#include "llviewerparcelmgr.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
-#include "llvolumesliderctrl.h"
#include "viewer.h"
#include "llvieweruictrlfactory.h"
@@ -175,7 +174,6 @@ BOOL LLFloaterTools::postBuild()
childSetAction("button land",LLFloaterTools::setEditTool, (void*)gToolParcel);
mTextStatus = LLUICtrlFactory::getTextBoxByName(this,"text status");
mRadioZoom = LLUICtrlFactory::getCheckBoxByName(this,"radio zoom");
- mSliderZoom = LLViewerUICtrlFactory::getVolumeSliderByName(this,"slider zoom");
childSetCommitCallback("slider zoom",commit_slider_zoom,this);
mRadioOrbit = LLUICtrlFactory::getCheckBoxByName(this,"radio orbit");
childSetCommitCallback("radio orbit",commit_radio_orbit,this);
@@ -476,8 +474,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)
mRadioZoom ->setVisible( focus_visible );
mRadioOrbit ->setVisible( focus_visible );
mRadioPan ->setVisible( focus_visible );
- mSliderZoom ->setVisible( focus_visible );
-
+ childSetVisible("slider zoom", focus_visible);
+
mRadioZoom ->set( !gCameraBtnOrbit &&
!gCameraBtnPan &&
!(mask == MASK_ORBIT) &&
@@ -494,7 +492,7 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)
(mask == (MASK_PAN | MASK_ALT)) );
// multiply by correction factor because volume sliders go [0, 0.5]
- mSliderZoom ->setValue( gAgent.getCameraZoomFraction() * 0.5f);
+ childSetValue( "slider zoom", gAgent.getCameraZoomFraction() * 0.5f);
// Move buttons
BOOL move_visible = (tool == gToolGrab);
@@ -833,9 +831,8 @@ void commit_radio_pan(LLUICtrl *, void*)
void commit_slider_zoom(LLUICtrl *ctrl, void*)
{
- LLVolumeSliderCtrl* slider = (LLVolumeSliderCtrl*)ctrl;
// renormalize value, since max "volume" level is 0.5 for some reason
- F32 zoom_level = (F32)slider->getValue().asReal() * 2.f; // / 0.5f;
+ F32 zoom_level = (F32)ctrl->getValue().asReal() * 2.f; // / 0.5f;
gAgent.setCameraZoomFraction(zoom_level);
}
diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h
index 7c85444578..156f768459 100644
--- a/indra/newview/llfloatertools.h
+++ b/indra/newview/llfloatertools.h
@@ -24,7 +24,6 @@ class LLPanelContents;
class LLPanelFace;
class LLPanelLandInfo;
class LLComboBox;
-class LLVolumeSliderCtrl;
class LLParcelSelection;
class LLObjectSelection;
@@ -100,7 +99,6 @@ public:
LLCheckBoxCtrl *mRadioOrbit;
LLCheckBoxCtrl *mRadioZoom;
LLCheckBoxCtrl *mRadioPan;
- LLVolumeSliderCtrl *mSliderZoom;
// Move buttons
LLCheckBoxCtrl *mRadioMove;
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index be1c26381b..3c51d7dcf1 100644
--- a/indra/newview/llgesturemgr.cpp
+++ b/indra/newview/llgesturemgr.cpp
@@ -83,7 +83,7 @@ void LLGestureManager::activateGesture(const LLUUID& item_id)
mDeactivateSimilarNames.clear();
const BOOL inform_server = TRUE;
- const BOOL deactivate_similar = TRUE;
+ const BOOL deactivate_similar = FALSE;
activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar);
}
@@ -403,7 +403,7 @@ void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new
LLLoadInfo* info = new LLLoadInfo;
info->mItemID = item_id;
info->mInformServer = TRUE;
- info->mDeactivateSimilar = TRUE;
+ info->mDeactivateSimilar = FALSE;
const BOOL high_priority = TRUE;
gAssetStorage->getAssetData(asset_id,
@@ -474,6 +474,8 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
LLString cur_token_lower = cur_token;
LLString::toLower(cur_token_lower);
+ // collect gestures that match
+ std::vector <LLMultiGesture *> matching;
item_map_t::iterator it;
for (it = mActive.begin(); it != mActive.end(); ++it)
{
@@ -481,16 +483,32 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
// Gesture asset data might not have arrived yet
if (!gesture) continue;
-
+
if (!stricmp(gesture->mTrigger.c_str(), cur_token_lower.c_str()))
{
+ matching.push_back(gesture);
+ }
+
+ gesture = NULL;
+ }
+
+
+ if (matching.size() > 0)
+ {
+ // choose one at random
+ {
+ S32 random = ll_rand(matching.size());
+
+ gesture = matching[random];
+
playGesture(gesture);
if (!gesture->mReplaceText.empty())
{
if( !first_token )
{
- revised_string->append( " " );
+ if (revised_string)
+ revised_string->append( " " );
}
// Don't muck with the user's capitalization if we don't have to.
@@ -499,30 +517,34 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
LLString::toLower(output_lower);
if( cur_token_lower == output_lower )
{
- revised_string->append( cur_token );
+ if (revised_string)
+ revised_string->append( cur_token );
}
else
{
- revised_string->append( output );
+ if (revised_string)
+ revised_string->append( output );
}
}
found_gestures = TRUE;
- break;
}
- gesture = NULL;
}
}
-
- if( !gesture )
+
+ if(!gesture)
{
+ // This token doesn't match a gesture. Pass it through to the output.
if( !first_token )
{
- revised_string->append( " " );
+ if (revised_string)
+ revised_string->append( " " );
}
- revised_string->append( cur_token );
+ if (revised_string)
+ revised_string->append( cur_token );
}
first_token = FALSE;
+ gesture = NULL;
}
return found_gestures;
}
@@ -530,7 +552,10 @@ BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::s
BOOL LLGestureManager::triggerGesture(KEY key, MASK mask)
{
+ std::vector <LLMultiGesture *> matching;
item_map_t::iterator it;
+
+ // collect matching gestures
for (it = mActive.begin(); it != mActive.end(); ++it)
{
LLMultiGesture* gesture = (*it).second;
@@ -541,10 +566,20 @@ BOOL LLGestureManager::triggerGesture(KEY key, MASK mask)
if (gesture->mKey == key
&& gesture->mMask == mask)
{
- playGesture(gesture);
- return TRUE;
+ matching.push_back(gesture);
}
}
+
+ // choose one and play it
+ if (matching.size() > 0)
+ {
+ U32 random = ll_rand(matching.size());
+
+ LLMultiGesture* gesture = matching[random];
+
+ playGesture(gesture);
+ return TRUE;
+ }
return FALSE;
}
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
index b79dc0687f..c68ca4f265 100644
--- a/indra/newview/llgesturemgr.h
+++ b/indra/newview/llgesturemgr.h
@@ -85,7 +85,7 @@ public:
BOOL triggerGesture(KEY key, MASK mask);
// Trigger all gestures referenced as substrings in this string
- BOOL triggerAndReviseString(const std::string &str, std::string *revised_string);
+ BOOL triggerAndReviseString(const std::string &str, std::string *revised_string = NULL);
// Does some gesture have this key bound?
BOOL isKeyBound(KEY key, MASK mask);
diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp
index 1c8c45a078..0bcbbbb140 100644
--- a/indra/newview/llhudobject.cpp
+++ b/indra/newview/llhudobject.cpp
@@ -22,6 +22,7 @@
#include "llhudeffectlookat.h"
//Ventrella
+#include "llvoicevisualizer.h"
#include "llanimalcontrols.h"
#include "lllocalanimationobject.h"
#include "llcape.h"
@@ -205,6 +206,9 @@ LLHUDObject *LLHUDObject::addHUDObject(const U8 type)
case LL_HUD_EFFECT_LOOKAT:
hud_objectp = new LLHUDEffectLookAt(type);
break;
+ case LL_HUD_EFFECT_VOICE_VISUALIZER:
+ hud_objectp = new LLVoiceVisualizer(type);
+ break;
case LL_HUD_EFFECT_POINTAT:
hud_objectp = new LLHUDEffectPointAt(type);
break;
diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h
index 9bd14796d5..3b5589c05b 100644
--- a/indra/newview/llhudobject.h
+++ b/indra/newview/llhudobject.h
@@ -68,7 +68,8 @@ public:
LL_HUD_EFFECT_SPIRAL,
LL_HUD_EFFECT_EDIT,
LL_HUD_EFFECT_LOOKAT,
- LL_HUD_EFFECT_POINTAT
+ LL_HUD_EFFECT_POINTAT,
+ LL_HUD_EFFECT_VOICE_VISUALIZER // Ventrella
};
protected:
static void sortObjects();
diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp
index aba3401d3c..70065c48d6 100644
--- a/indra/newview/llhudtext.cpp
+++ b/indra/newview/llhudtext.cpp
@@ -839,7 +839,8 @@ void LLHUDText::updateAll()
}
if (src_textp->mSoftScreenRect.rectInRect(&dst_textp->mSoftScreenRect))
{
- LLRectf intersect_rect = src_textp->mSoftScreenRect & dst_textp->mSoftScreenRect;
+ LLRectf intersect_rect = src_textp->mSoftScreenRect;
+ intersect_rect.intersectWith(dst_textp->mSoftScreenRect);
intersect_rect.stretch(-BUFFER_SIZE * 0.5f);
F32 src_center_x = src_textp->mSoftScreenRect.getCenterX();
diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp
index 015f90a180..3a873bc1a8 100644
--- a/indra/newview/llimpanel.cpp
+++ b/indra/newview/llimpanel.cpp
@@ -13,7 +13,6 @@
#include "indra_constants.h"
#include "llfocusmgr.h"
#include "llfontgl.h"
-#include "llhttpclient.h"
#include "llrect.h"
#include "llerror.h"
#include "llstring.h"
@@ -23,12 +22,14 @@
#include "llagent.h"
#include "llbutton.h"
#include "llcallingcard.h"
+#include "llchat.h"
#include "llconsole.h"
#include "llfloater.h"
#include "llinventory.h"
#include "llinventorymodel.h"
#include "llinventoryview.h"
#include "llfloateravatarinfo.h"
+#include "llfloaterchat.h"
#include "llkeyboard.h"
#include "lllineeditor.h"
#include "llresmgr.h"
@@ -41,8 +42,13 @@
#include "llvieweruictrlfactory.h"
#include "lllogchat.h"
#include "llfloaterhtml.h"
-#include "llviewerregion.h"
#include "llweb.h"
+#include "llhttpclient.h"
+#include "llfloateractivespeakers.h" // LLSpeakerMgr
+#include "llfloatergroupinfo.h"
+#include "llsdutil.h"
+#include "llnotify.h"
+#include "llmutelist.h"
//
// Constants
@@ -59,6 +65,10 @@ static LLString sTitleString = "Instant Message with [NAME]";
static LLString sTypingStartString = "[NAME]: ...";
static LLString sSessionStartString = "Starting session with [NAME] please wait.";
+LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap;
+LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap;
+LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL;
+
void session_starter_helper(const LLUUID& temp_session_id,
const LLUUID& other_participant_id,
EInstantMessage im_type)
@@ -141,45 +151,669 @@ bool send_start_session_messages(const LLUUID& temp_session_id,
return false;
}
-// Member Functions
+class LLVoiceCallCapResponder : public LLHTTPClient::Responder
+{
+public:
+ LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {};
+
+ virtual void error(U32 status, const std::string& reason); // called with bad status codes
+ virtual void result(const LLSD& content);
+
+private:
+ LLUUID mSessionID;
+};
+
+
+void LLVoiceCallCapResponder::error(U32 status, const std::string& reason)
+{
+ llwarns << "LLVoiceCallCapResponder::error("
+ << status << ": " << reason << ")"
+ << llendl;
+ LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
+ if (channelp)
+ {
+ channelp->deactivate();
+ }
+}
+
+void LLVoiceCallCapResponder::result(const LLSD& content)
+{
+ LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
+ if (channelp)
+ {
+ //*TODO: DEBUG SPAM
+ LLSD::map_const_iterator iter;
+ for(iter = content.beginMap(); iter != content.endMap(); ++iter)
+ {
+ llinfos << "LLVoiceCallCapResponder::result got "
+ << iter->first << llendl;
+ }
+
+ channelp->setChannelInfo(
+ content["voice_credentials"]["channel_uri"].asString(),
+ content["voice_credentials"]["channel_credentials"].asString());
+ }
+}
+
+//
+// LLVoiceChannel
+//
+LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const LLString& session_name) :
+ mSessionID(session_id),
+ mState(STATE_NO_CHANNEL_INFO),
+ mSessionName(session_name),
+ mIgnoreNextSessionLeave(FALSE)
+{
+ mNotifyArgs["[VOICE_CHANNEL_NAME]"] = mSessionName;
+
+ if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second)
+ {
+ // a voice channel already exists for this session id, so this instance will be orphaned
+ // the end result should simply be the failure to make voice calls
+ llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl;
+ }
+
+ LLVoiceClient::getInstance()->addStatusObserver(this);
+}
+
+LLVoiceChannel::~LLVoiceChannel()
+{
+ // CANNOT do this here, since it will crash on quit in the LLVoiceChannelProximal singleton destructor.
+ // Do it in all other subclass destructors instead.
+ // deactivate();
+
+ // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed.
+ if(gVoiceClient)
+ {
+ gVoiceClient->removeStatusObserver(this);
+ }
+
+ sVoiceChannelMap.erase(mSessionID);
+ sVoiceChannelURIMap.erase(mURI);
+}
+
+void LLVoiceChannel::setChannelInfo(
+ const LLString& uri,
+ const LLString& credentials)
+{
+ setURI(uri);
+
+ mCredentials = credentials;
+
+ if (mState == STATE_NO_CHANNEL_INFO)
+ {
+ if(!mURI.empty() && !mCredentials.empty())
+ {
+ setState(STATE_READY);
+
+ // if we are supposed to be active, reconnect
+ // this will happen on initial connect, as we request credentials on first use
+ if (sCurrentVoiceChannel == this)
+ {
+ // just in case we got new channel info while active
+ // should move over to new channel
+ activate();
+ }
+ }
+ else
+ {
+ //*TODO: notify user
+ llwarns << "Received invalid credentials for channel " << mSessionName << llendl;
+ deactivate();
+ }
+ }
+}
+
+void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal)
+{
+ if (channelURI != mURI)
+ {
+ return;
+ }
+
+ if (type < BEGIN_ERROR_STATUS)
+ {
+ handleStatusChange(type);
+ }
+ else
+ {
+ handleError(type);
+ }
+}
+
+void LLVoiceChannel::handleStatusChange(EStatusType type)
+{
+ // status updates
+ switch(type)
+ {
+ case STATUS_LOGIN_RETRY:
+ mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle();
+ break;
+ case STATUS_LOGGED_IN:
+ if (!mLoginNotificationHandle.isDead())
+ {
+ LLNotifyBox* notifyp = (LLNotifyBox*)LLPanel::getPanelByHandle(mLoginNotificationHandle);
+ if (notifyp)
+ {
+ notifyp->close();
+ }
+ mLoginNotificationHandle.markDead();
+ }
+ break;
+ case STATUS_LEFT_CHANNEL:
+ if (callStarted() && !mIgnoreNextSessionLeave)
+ {
+ // if forceably removed from channel
+ // update the UI and revert to default channel
+ LLNotifyBox::showXml("VoiceChannelDisconnected", mNotifyArgs);
+ deactivate();
+ }
+ mIgnoreNextSessionLeave = FALSE;
+ break;
+ case STATUS_JOINING:
+ if (callStarted())
+ {
+ setState(STATE_RINGING);
+ }
+ break;
+ case STATUS_JOINED:
+ if (callStarted())
+ {
+ setState(STATE_CONNECTED);
+ }
+ default:
+ break;
+ }
+}
+
+// default behavior is to just deactivate channel
+// derived classes provide specific error messages
+void LLVoiceChannel::handleError(EStatusType type)
+{
+ deactivate();
+ setState(STATE_ERROR);
+}
+
+BOOL LLVoiceChannel::isActive()
+{
+ // only considered active when currently bound channel matches what our channel
+ return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI;
+}
+
+BOOL LLVoiceChannel::callStarted()
+{
+ return mState >= STATE_CALL_STARTED;
+}
+
+void LLVoiceChannel::deactivate()
+{
+ if (mState >= STATE_RINGING)
+ {
+ // ignore session leave event
+ mIgnoreNextSessionLeave = TRUE;
+ }
+
+ if (callStarted())
+ {
+ setState(STATE_HUNG_UP);
+ }
+ if (sCurrentVoiceChannel == this)
+ {
+ // default channel is proximal channel
+ sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
+ sCurrentVoiceChannel->activate();
+ }
+}
+
+void LLVoiceChannel::activate()
+{
+ if (callStarted())
+ {
+ return;
+ }
+
+ // deactivate old channel and mark ourselves as the active one
+ if (sCurrentVoiceChannel != this)
+ {
+ if (sCurrentVoiceChannel)
+ {
+ sCurrentVoiceChannel->deactivate();
+ }
+ sCurrentVoiceChannel = this;
+ }
+
+ if (mState == STATE_NO_CHANNEL_INFO)
+ {
+ // responsible for setting status to active
+ getChannelInfo();
+ }
+ else
+ {
+ setState(STATE_CALL_STARTED);
+ }
+}
+
+void LLVoiceChannel::getChannelInfo()
+{
+ // pretend we have everything we need
+ if (sCurrentVoiceChannel == this)
+ {
+ setState(STATE_CALL_STARTED);
+ }
+}
+
+//static
+LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id)
+{
+ voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id);
+ if (found_it == sVoiceChannelMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return found_it->second;
+ }
+}
+
+//static
+LLVoiceChannel* LLVoiceChannel::getChannelByURI(LLString uri)
+{
+ voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri);
+ if (found_it == sVoiceChannelURIMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return found_it->second;
+ }
+}
+
+
+void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id)
+{
+ sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID));
+ mSessionID = new_session_id;
+ sVoiceChannelMap.insert(std::make_pair(mSessionID, this));
+}
+
+void LLVoiceChannel::setURI(LLString uri)
+{
+ sVoiceChannelURIMap.erase(mURI);
+ mURI = uri;
+ sVoiceChannelURIMap.insert(std::make_pair(mURI, this));
+}
+
+void LLVoiceChannel::setState(EState state)
+{
+ switch(state)
+ {
+ case STATE_RINGING:
+ gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs);
+ break;
+ case STATE_CONNECTED:
+ gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs);
+ break;
+ case STATE_HUNG_UP:
+ gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs);
+ break;
+ default:
+ break;
+ }
+
+ mState = state;
+}
+
+
+//static
+void LLVoiceChannel::initClass()
+{
+ sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
+}
+
+//
+// LLVoiceChannelGroup
+//
+
+LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name) :
+ LLVoiceChannel(session_id, session_name)
+{
+}
+
+LLVoiceChannelGroup::~LLVoiceChannelGroup()
+{
+ deactivate();
+}
+
+void LLVoiceChannelGroup::deactivate()
+{
+ if (callStarted())
+ {
+ LLVoiceClient::getInstance()->leaveNonSpatialChannel();
+ }
+ LLVoiceChannel::deactivate();
+}
+
+void LLVoiceChannelGroup::activate()
+{
+ if (callStarted()) return;
+
+ LLVoiceChannel::activate();
+
+ if (callStarted())
+ {
+ // we have the channel info, just need to use it now
+ LLVoiceClient::getInstance()->setNonSpatialChannel(
+ mURI,
+ mCredentials);
+ }
+}
+
+void LLVoiceChannelGroup::getChannelInfo()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string url = region->getCapability("ChatSessionRequest");
+ LLSD data;
+ data["method"] = "call";
+ data["session-id"] = mSessionID;
+ LLHTTPClient::post(url,
+ data,
+ new LLVoiceCallCapResponder(mSessionID));
+ }
+}
+
+void LLVoiceChannelGroup::handleError(EStatusType status)
+{
+ std::string notify;
+ switch(status)
+ {
+ case ERROR_CHANNEL_LOCKED:
+ case ERROR_CHANNEL_FULL:
+ notify = "VoiceChannelFull";
+ break;
+ case ERROR_UNKNOWN:
+ break;
+ default:
+ break;
+ }
+
+ // notification
+ if (!notify.empty())
+ {
+ LLNotifyBox::showXml(notify, mNotifyArgs);
+ // echo to im window
+ gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, LLNotifyBox::getTemplateMessage(notify, mNotifyArgs).c_str());
+ }
+
+ LLVoiceChannel::handleError(status);
+}
+
+//
+// LLVoiceChannelProximal
+//
+LLVoiceChannelProximal::LLVoiceChannelProximal() :
+ LLVoiceChannel(LLUUID::null, LLString::null)
+{
+ activate();
+}
+
+LLVoiceChannelProximal::~LLVoiceChannelProximal()
+{
+ // DO NOT call deactivate() here, since this will only happen at atexit() time.
+}
+
+BOOL LLVoiceChannelProximal::isActive()
+{
+ return callStarted() && LLVoiceClient::getInstance()->inProximalChannel();
+}
+
+void LLVoiceChannelProximal::activate()
+{
+ if (callStarted()) return;
+
+ LLVoiceChannel::activate();
+
+ if (callStarted())
+ {
+ // this implicitly puts you back in the spatial channel
+ LLVoiceClient::getInstance()->leaveNonSpatialChannel();
+ }
+}
+
+void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal)
+{
+ if (!proximal)
+ {
+ return;
+ }
+
+ if (type < BEGIN_ERROR_STATUS)
+ {
+ handleStatusChange(type);
+ }
+ else
+ {
+ handleError(type);
+ }
+}
+
+void LLVoiceChannelProximal::handleStatusChange(EStatusType status)
+{
+ // status updates
+ switch(status)
+ {
+ case STATUS_LEFT_CHANNEL:
+ // do not notify user when leaving proximal channel
+ return;
+ default:
+ break;
+ }
+ LLVoiceChannel::handleStatusChange(status);
+}
+
+
+void LLVoiceChannelProximal::handleError(EStatusType status)
+{
+ std::string notify;
+ switch(status)
+ {
+ case ERROR_CHANNEL_LOCKED:
+ case ERROR_CHANNEL_FULL:
+ notify = "ProximalVoiceChannelFull";
+ break;
+ default:
+ break;
+ }
+
+ // notification
+ if (!notify.empty())
+ {
+ LLNotifyBox::showXml(notify, mNotifyArgs);
+ }
+
+ LLVoiceChannel::handleError(status);
+}
+
+void LLVoiceChannelProximal::deactivate()
+{
+ if (callStarted())
+ {
+ setState(STATE_HUNG_UP);
+ }
+}
+
//
+// LLVoiceChannelP2P
+//
+LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id) :
+ LLVoiceChannelGroup(session_id, session_name),
+ mOtherUserID(other_user_id)
+{
+ // make sure URI reflects encoded version of other user's agent id
+ setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id));
+}
+
+LLVoiceChannelP2P::~LLVoiceChannelP2P()
+{
+ deactivate();
+}
+
+void LLVoiceChannelP2P::handleStatusChange(EStatusType type)
+{
+ // status updates
+ switch(type)
+ {
+ case STATUS_LEFT_CHANNEL:
+ if (callStarted() && !mIgnoreNextSessionLeave)
+ {
+ if (mState == STATE_RINGING)
+ {
+ // other user declined call
+ LLNotifyBox::showXml("P2PCallDeclined", mNotifyArgs);
+ }
+ else
+ {
+ // other user hung up
+ LLNotifyBox::showXml("VoiceChannelDisconnectedP2P", mNotifyArgs);
+ }
+ deactivate();
+ }
+ mIgnoreNextSessionLeave = FALSE;
+ return;
+ default:
+ break;
+ }
-LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name,
- const LLRect& rect,
- const std::string& session_label,
- const LLUUID& session_id,
- const LLUUID& other_participant_id,
- EInstantMessage dialog) :
+ LLVoiceChannelGroup::handleStatusChange(type);
+}
+
+void LLVoiceChannelP2P::handleError(EStatusType type)
+{
+ switch(type)
+ {
+ case ERROR_NOT_AVAILABLE:
+ LLNotifyBox::showXml("P2PCallNoAnswer", mNotifyArgs);
+ break;
+ default:
+ break;
+ }
+
+ LLVoiceChannelGroup::handleError(type);
+}
+
+void LLVoiceChannelP2P::activate()
+{
+ if (callStarted()) return;
+
+ LLVoiceChannel::activate();
+
+ if (callStarted())
+ {
+ // no session handle yet, we're starting the call
+ if (mSessionHandle.empty())
+ {
+ LLVoiceClient::getInstance()->callUser(mOtherUserID);
+ }
+ // otherwise answering the call
+ else
+ {
+ LLVoiceClient::getInstance()->answerInvite(mSessionHandle, mOtherUserID);
+ // using the session handle invalidates it. Clear it out here so we can't reuse it by accident.
+ mSessionHandle.clear();
+ }
+ }
+}
+
+void LLVoiceChannelP2P::getChannelInfo()
+{
+ // pretend we have everything we need, since P2P doesn't use channel info
+ if (sCurrentVoiceChannel == this)
+ {
+ setState(STATE_CALL_STARTED);
+ }
+}
+
+// receiving session from other user who initiated call
+void LLVoiceChannelP2P::setSessionHandle(const LLString& handle)
+{
+ BOOL needs_activate = FALSE;
+ if (callStarted())
+ {
+ // defer to lower agent id when already active
+ if (mOtherUserID < gAgent.getID())
+ {
+ // pretend we haven't started the call yet, so we can connect to this session instead
+ deactivate();
+ needs_activate = TRUE;
+ }
+ else
+ {
+ // we are active and have priority, invite the other user again
+ // under the assumption they will join this new session
+ mSessionHandle.clear();
+ LLVoiceClient::getInstance()->callUser(mOtherUserID);
+ return;
+ }
+ }
+
+ mSessionHandle = handle;
+ // The URI of a p2p session should always be the other end's SIP URI.
+ setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));
+
+ if (needs_activate)
+ {
+ activate();
+ }
+}
+
+//
+// LLFloaterIMPanel
+//
+LLFloaterIMPanel::LLFloaterIMPanel(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& session_label,
+ const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ EInstantMessage dialog) :
LLFloater(name, rect, session_label),
mInputEditor(NULL),
mHistoryEditor(NULL),
mSessionUUID(session_id),
- mSessionInitRequested(FALSE),
+ mVoiceChannel(NULL),
mSessionInitialized(FALSE),
+
mOtherParticipantUUID(other_participant_id),
mDialog(dialog),
mTyping(FALSE),
mOtherTyping(FALSE),
mTypingLineStartIndex(0),
mSentTypingState(TRUE),
+ mShowSpeakersOnConnect(TRUE),
+ mAutoConnect(FALSE),
+ mSpeakerPanel(NULL),
mFirstKeystrokeTimer(),
mLastKeystrokeTimer()
{
init(session_label);
}
-LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name,
- const LLRect& rect,
- const std::string& session_label,
- const LLUUID& session_id,
- const LLUUID& other_participant_id,
- const LLDynamicArray<LLUUID>& ids,
- EInstantMessage dialog) :
+LLFloaterIMPanel::LLFloaterIMPanel(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& session_label,
+ const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ const LLDynamicArray<LLUUID>& ids,
+ EInstantMessage dialog) :
LLFloater(name, rect, session_label),
mInputEditor(NULL),
mHistoryEditor(NULL),
mSessionUUID(session_id),
- mSessionInitRequested(FALSE),
+ mVoiceChannel(NULL),
mSessionInitialized(FALSE),
mOtherParticipantUUID(other_participant_id),
mDialog(dialog),
@@ -187,6 +821,10 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name,
mOtherTyping(FALSE),
mTypingLineStartIndex(0),
mSentTypingState(TRUE),
+ mShowSpeakersOnConnect(TRUE),
+ mAutoConnect(FALSE),
+ mSpeakers(NULL),
+ mSpeakerPanel(NULL),
mFirstKeystrokeTimer(),
mLastKeystrokeTimer()
{
@@ -197,11 +835,53 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name,
void LLFloaterIMPanel::init(const LLString& session_label)
{
+ LLString xml_filename;
+ switch(mDialog)
+ {
+ case IM_SESSION_GROUP_START:
+ mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
+ xml_filename = "floater_instant_message_group.xml";
+ mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label);
+ break;
+ case IM_SESSION_INVITE:
+ mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
+ if (gAgent.isInGroup(mSessionUUID))
+ {
+ xml_filename = "floater_instant_message_group.xml";
+ }
+ else // must be invite to ad hoc IM
+ {
+ xml_filename = "floater_instant_message_ad_hoc.xml";
+ }
+ mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label);
+ break;
+ case IM_SESSION_P2P_INVITE:
+ xml_filename = "floater_instant_message.xml";
+ mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID);
+ break;
+ case IM_SESSION_CONFERENCE_START:
+ mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
+ xml_filename = "floater_instant_message_ad_hoc.xml";
+ mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label);
+ break;
+ // just received text from another user
+ case IM_NOTHING_SPECIAL:
+ xml_filename = "floater_instant_message.xml";
+ mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID);
+ break;
+ default:
+ llwarns << "Unknown session type" << llendl;
+ xml_filename = "floater_instant_message.xml";
+ break;
+ }
+
+ mSpeakers = new LLIMSpeakerMgr(mVoiceChannel);
+
gUICtrlFactory->buildFloater(this,
- "floater_instant_message.xml",
- NULL,
- FALSE);
-
+ xml_filename,
+ &getFactoryMap(),
+ FALSE);
+
setLabel(session_label);
setTitle(session_label);
mInputEditor->setMaxTextLength(1023);
@@ -238,18 +918,30 @@ void LLFloaterIMPanel::init(const LLString& session_label)
addHistoryLine(
session_start,
- LLColor4::grey,
+ gSavedSettings.getColor4("SystemChatColor"),
false);
}
}
}
+LLFloaterIMPanel::~LLFloaterIMPanel()
+{
+ delete mSpeakers;
+ mSpeakers = NULL;
+
+ //kicks you out of the voice channel if it is currently active
+
+ // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point).
+ mVoiceChannel->deactivate();
+
+ delete mVoiceChannel;
+ mVoiceChannel = NULL;
+}
+
BOOL LLFloaterIMPanel::postBuild()
{
requires("chat_editor", WIDGET_TYPE_LINE_EDITOR);
- requires("profile_btn", WIDGET_TYPE_BUTTON);
- requires("close_btn", WIDGET_TYPE_BUTTON);
requires("im_history", WIDGET_TYPE_TEXT_EDITOR);
requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX);
requires("title_string", WIDGET_TYPE_TEXT_BOX);
@@ -262,22 +954,28 @@ BOOL LLFloaterIMPanel::postBuild()
mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived );
mInputEditor->setFocusLostCallback( onInputEditorFocusLost );
mInputEditor->setKeystrokeCallback( onInputEditorKeystroke );
+ mInputEditor->setCommitCallback( onCommitChat );
mInputEditor->setCallbackUserData(this);
mInputEditor->setCommitOnFocusLost( FALSE );
mInputEditor->setRevertOnEsc( FALSE );
- LLButton* profile_btn = LLUICtrlFactory::getButtonByName(this, "profile_btn");
- profile_btn->setClickedCallback(&LLFloaterIMPanel::onClickProfile, this);
+ childSetAction("profile_callee_btn", onClickProfile, this);
+ childSetAction("group_info_btn", onClickGroupInfo, this);
- LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn");
- close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this);
+ childSetAction("start_call_btn", onClickStartCall, this);
+ childSetAction("end_call_btn", onClickEndCall, this);
+ childSetAction("send_btn", onClickSend, this);
+ childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this);
+
+ //LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn");
+ //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this);
mHistoryEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "im_history");
mHistoryEditor->setParseHTML(TRUE);
- if (IM_SESSION_GROUP_START == mDialog)
+ if ( IM_SESSION_GROUP_START == mDialog )
{
- profile_btn->setEnabled(FALSE);
+ childSetEnabled("profile_btn", FALSE);
}
LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string");
sTitleString = title->getText();
@@ -290,16 +988,91 @@ BOOL LLFloaterIMPanel::postBuild()
this,
"session_start_string");
sSessionStartString = session_start->getText();
+ if (mSpeakerPanel)
+ {
+ mSpeakerPanel->refreshSpeakers();
+ }
+ if (mDialog == IM_NOTHING_SPECIAL)
+ {
+ childSetCommitCallback("mute_btn", onClickMuteVoice, this);
+ childSetCommitCallback("speaker_volume", onVolumeChange, this);
+ }
+
+ setDefaultBtn("send_btn");
return TRUE;
}
return FALSE;
}
+void* LLFloaterIMPanel::createSpeakersPanel(void* data)
+{
+ LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)data;
+ floaterp->mSpeakerPanel = new LLPanelActiveSpeakers(floaterp->mSpeakers, TRUE);
+ return floaterp->mSpeakerPanel;
+}
+
+//static
+void LLFloaterIMPanel::onClickMuteVoice(LLUICtrl* source, void* user_data)
+{
+ LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;
+ if (floaterp)
+ {
+ BOOL is_muted = gMuteListp->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat);
+
+ LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT);
+ if (!is_muted)
+ {
+ gMuteListp->add(mute, LLMute::flagVoiceChat);
+ }
+ else
+ {
+ gMuteListp->remove(mute, LLMute::flagVoiceChat);
+ }
+ }
+}
+
+//static
+void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data)
+{
+ LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;
+ if (floaterp)
+ {
+ gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal());
+ }
+}
+
+
// virtual
void LLFloaterIMPanel::draw()
-{
+{
+ LLViewerRegion* region = gAgent.getRegion();
+
+ BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "")
+ && mSessionInitialized
+ && LLVoiceClient::voiceEnabled();
+
+ // hide/show start call and end call buttons
+ childSetVisible("end_call_btn", mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED);
+ childSetVisible("start_call_btn", mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED);
+ childSetEnabled("start_call_btn", enable_connect);
+ childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty());
+
+ if (mAutoConnect && enable_connect)
+ {
+ onClickStartCall(this);
+ mAutoConnect = FALSE;
+ }
+
+ // show speakers window when voice first connects
+ if (mShowSpeakersOnConnect && mVoiceChannel->isActive())
+ {
+ childSetVisible("active_speakers_panel", TRUE);
+ mShowSpeakersOnConnect = FALSE;
+ }
+ childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel"));
+
if (mTyping)
{
// Time out if user hasn't typed for a while.
@@ -318,6 +1091,19 @@ void LLFloaterIMPanel::draw()
}
}
+ if (mSpeakerPanel)
+ {
+ mSpeakerPanel->refreshSpeakers();
+ }
+ else
+ {
+ // refresh volume and mute checkbox
+ childSetEnabled("speaker_volume", mVoiceChannel->isActive());
+ childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID));
+
+ childSetValue("mute_btn", gMuteListp->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat));
+ childSetEnabled("mute_btn", mVoiceChannel->isActive());
+ }
LLFloater::draw();
}
@@ -342,16 +1128,22 @@ private:
BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids)
{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ {
+ return FALSE;
+ }
+
S32 count = ids.count();
- if( isAddAllowed() && (count > 0) )
+ if( isInviteAllowed() && (count > 0) )
{
- llinfos << "LLFloaterIMPanel::inviteToSession() - adding participants" << llendl;
+ llinfos << "LLFloaterIMPanel::inviteToSession() - inviting participants" << llendl;
- std::string url =
- gAgent.getRegion()->getCapability("ChatSessionRequest");
+ std::string url = region->getCapability("ChatSessionRequest");
LLSD data;
+
data["params"] = LLSD::emptyArray();
for (int i = 0; i < count; i++)
{
@@ -378,6 +1170,13 @@ BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids)
return TRUE;
}
+void LLFloaterIMPanel::addHistoryLine(const LLUUID& source, const std::string &utf8msg, const LLColor4& color, bool log_to_file)
+{
+ addHistoryLine(utf8msg, color, log_to_file);
+ mSpeakers->speakerChatted(source);
+ mSpeakers->setSpeakerTyping(source, FALSE);
+}
+
void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file)
{
LLMultiFloater* hostp = getHost();
@@ -391,7 +1190,7 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4
// Now we're adding the actual line of text, so erase the
// "Foo is typing..." text segment, and the optional timestamp
// if it was present. JC
- removeTypingIndicator();
+ removeTypingIndicator(NULL);
// Actually add the line
LLString timestring;
@@ -470,7 +1269,7 @@ BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_paren
// but not shift-return or control-return
if ( !gSavedSettings.getBOOL("PinTalkViewOpen") && !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT) )
{
- gIMView->toggle(NULL);
+ gIMMgr->toggle(NULL);
}
}
else if ( KEY_ESCAPE == key )
@@ -481,7 +1280,7 @@ BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_paren
// Close talk panel with escape
if( !gSavedSettings.getBOOL("PinTalkViewOpen") )
{
- gIMView->toggle(NULL);
+ gIMMgr->toggle(NULL);
}
}
}
@@ -522,7 +1321,7 @@ BOOL LLFloaterIMPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop)
{
- BOOL rv = isAddAllowed();
+ BOOL rv = isInviteAllowed();
if(rv && item && item->getCreatorUUID().notNull())
{
if(drop)
@@ -542,7 +1341,7 @@ BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop)
BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop)
{
- BOOL rv = isAddAllowed();
+ BOOL rv = isInviteAllowed();
if(rv && category)
{
LLInventoryModel::cat_array_t cats;
@@ -571,11 +1370,11 @@ BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop)
return rv;
}
-BOOL LLFloaterIMPanel::isAddAllowed() const
+BOOL LLFloaterIMPanel::isInviteAllowed() const
{
- return ((IM_SESSION_CONFERENCE_START == mDialog)
- || (IM_SESSION_INVITE) );
+ return ( (IM_SESSION_CONFERENCE_START == mDialog)
+ || (IM_SESSION_INVITE == mDialog) );
}
@@ -600,6 +1399,15 @@ void LLFloaterIMPanel::onClickProfile( void* userdata )
}
// static
+void LLFloaterIMPanel::onClickGroupInfo( void* userdata )
+{
+ // Bring up the Profile window
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+
+ LLFloaterGroupInfo::showFromUUID(self->mSessionUUID);
+}
+
+// static
void LLFloaterIMPanel::onClickClose( void* userdata )
{
LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
@@ -610,6 +1418,44 @@ void LLFloaterIMPanel::onClickClose( void* userdata )
}
// static
+void LLFloaterIMPanel::onClickStartCall(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+
+ self->mVoiceChannel->activate();
+}
+
+// static
+void LLFloaterIMPanel::onClickEndCall(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+
+ self->getVoiceChannel()->deactivate();
+}
+
+// static
+void LLFloaterIMPanel::onClickSend(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
+ self->sendMsg();
+}
+
+// static
+void LLFloaterIMPanel::onClickToggleActiveSpeakers(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
+
+ self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel"));
+}
+
+// static
+void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata)
+{
+ LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
+ self->sendMsg();
+}
+
+// static
void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata )
{
LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
@@ -660,7 +1506,7 @@ void LLFloaterIMPanel::onClose(bool app_quitting)
mSessionUUID);
gAgent.sendReliableMessage();
}
- gIMView->removeSession(mSessionUUID);
+ gIMMgr->removeSession(mSessionUUID);
destroy();
}
@@ -748,10 +1594,9 @@ void LLFloaterIMPanel::sendMsg()
}
history_echo += utf8_text;
-
BOOL other_was_typing = mOtherTyping;
- addHistoryLine(history_echo);
+ addHistoryLine(gAgent.getID(), history_echo);
if (other_was_typing)
{
@@ -762,6 +1607,8 @@ void LLFloaterIMPanel::sendMsg()
}
else
{
+ //queue up the message to send once the session is
+ //initialized
mQueuedMsgsForInit.append(utf8_text);
}
@@ -775,15 +1622,31 @@ void LLFloaterIMPanel::sendMsg()
mSentTypingState = TRUE;
}
+void LLFloaterIMPanel::updateSpeakersList(LLSD speaker_updates)
+{
+ mSpeakers->processSpeakerListUpdate(speaker_updates);
+}
+
+void LLFloaterIMPanel::setSpeakersListFromMap(LLSD speaker_map)
+{
+ mSpeakers->processSpeakerMap(speaker_map);
+}
+
+void LLFloaterIMPanel::setSpeakersList(LLSD speaker_list)
+{
+ mSpeakers->processSpeakerList(speaker_list);
+}
+
void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id)
{
mSessionUUID = session_id;
+ mVoiceChannel->updateSessionID(session_id);
mSessionInitialized = TRUE;
//we assume the history editor hasn't moved at all since
//we added the starting session message
//so, we count how many characters to remove
- S32 chars_to_remove = mHistoryEditor->getText().length() -
+ S32 chars_to_remove = mHistoryEditor->getText().length() -
mSessionStartMsgPos;
mHistoryEditor->removeTextFromEnd(chars_to_remove);
@@ -793,13 +1656,18 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id)
iter != mQueuedMsgsForInit.endArray();
++iter)
{
- deliver_message(iter->asString(),
- mSessionUUID,
- mOtherParticipantUUID,
- mDialog);
+ deliver_message(
+ iter->asString(),
+ mSessionUUID,
+ mOtherParticipantUUID,
+ mDialog);
}
}
+void LLFloaterIMPanel::requestAutoConnect()
+{
+ mAutoConnect = TRUE;
+}
void LLFloaterIMPanel::setTyping(BOOL typing)
{
@@ -816,6 +1684,8 @@ void LLFloaterIMPanel::setTyping(BOOL typing)
// Will send typing state after a short delay.
mSentTypingState = FALSE;
}
+
+ mSpeakers->setSpeakerTyping(gAgent.getID(), TRUE);
}
else
{
@@ -825,6 +1695,7 @@ void LLFloaterIMPanel::setTyping(BOOL typing)
sendTypingState(FALSE);
mSentTypingState = TRUE;
}
+ mSpeakers->setSpeakerTyping(gAgent.getID(), FALSE);
}
mTyping = typing;
@@ -853,7 +1724,6 @@ void LLFloaterIMPanel::sendTypingState(BOOL typing)
gAgent.sendReliableMessage();
}
-
void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing)
{
if (typing)
@@ -864,7 +1734,7 @@ void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing)
else
{
// other user stopped typing
- removeTypingIndicator();
+ removeTypingIndicator(im_info);
}
}
@@ -877,14 +1747,17 @@ void LLFloaterIMPanel::addTypingIndicator(const std::string &name)
mTypingLineStartIndex = mHistoryEditor->getText().length();
LLUIString typing_start = sTypingStartString;
typing_start.setArg("[NAME]", name);
- addHistoryLine(typing_start, LLColor4::grey, false);
+ addHistoryLine(typing_start, gSavedSettings.getColor4("SystemChatColor"), false);
mOtherTypingName = name;
mOtherTyping = TRUE;
}
+ // MBW -- XXX -- merge from release broke this (argument to this function changed from an LLIMInfo to a name)
+ // Richard will fix.
+// mSpeakers->setSpeakerTyping(im_info->mFromID, TRUE);
}
-void LLFloaterIMPanel::removeTypingIndicator()
+void LLFloaterIMPanel::removeTypingIndicator(const LLIMInfo* im_info)
{
if (mOtherTyping)
{
@@ -893,6 +1766,10 @@ void LLFloaterIMPanel::removeTypingIndicator()
S32 chars_to_remove = mHistoryEditor->getText().length() - mTypingLineStartIndex;
mHistoryEditor->removeTextFromEnd(chars_to_remove);
+ if (im_info)
+ {
+ mSpeakers->setSpeakerTyping(im_info->mFromID, FALSE);
+ }
}
}
@@ -905,4 +1782,3 @@ void LLFloaterIMPanel::chatFromLogFile(LLString line, void* userdata)
self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey);
}
-
diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h
index cb8042e010..dbe09fb396 100644
--- a/indra/newview/llimpanel.h
+++ b/indra/newview/llimpanel.h
@@ -13,15 +13,122 @@
#include "lluuid.h"
#include "lldarray.h"
#include "llinstantmessage.h"
+#include "llvoiceclient.h"
class LLLineEditor;
class LLViewerTextEditor;
class LLInventoryItem;
class LLInventoryCategory;
+class LLIMSpeakerMgr;
+class LLPanelActiveSpeakers;
+
+class LLVoiceChannel : public LLVoiceClientStatusObserver
+{
+public:
+ typedef enum e_voice_channel_state
+ {
+ STATE_NO_CHANNEL_INFO,
+ STATE_ERROR,
+ STATE_HUNG_UP,
+ STATE_READY,
+ STATE_CALL_STARTED,
+ STATE_RINGING,
+ STATE_CONNECTED
+ } EState;
+
+ LLVoiceChannel(const LLUUID& session_id, const LLString& session_name);
+ virtual ~LLVoiceChannel();
+
+ void setChannelInfo(const LLString& uri, const LLString& credentials);
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+
+ virtual void handleStatusChange(EStatusType status);
+ virtual void handleError(EStatusType status);
+ virtual void deactivate();
+ virtual void activate();
+ virtual void getChannelInfo();
+ virtual BOOL isActive();
+ virtual BOOL callStarted();
+ EState getState() { return mState; }
+
+ void updateSessionID(const LLUUID& new_session_id);
+
+ static LLVoiceChannel* getChannelByID(const LLUUID& session_id);
+ static LLVoiceChannel* getChannelByURI(LLString uri);
+ static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; }
+ static void initClass();
+
+protected:
+ void setState(EState state);
+ void setURI(LLString uri);
+
+ LLString mURI;
+ LLString mCredentials;
+ LLUUID mSessionID;
+ EState mState;
+ LLString mSessionName;
+ LLString::format_map_t mNotifyArgs;
+ BOOL mIgnoreNextSessionLeave;
+ LLViewHandle mLoginNotificationHandle;
+
+ typedef std::map<LLUUID, LLVoiceChannel*> voice_channel_map_t;
+ static voice_channel_map_t sVoiceChannelMap;
+
+ typedef std::map<LLString, LLVoiceChannel*> voice_channel_map_uri_t;
+ static voice_channel_map_uri_t sVoiceChannelURIMap;
+
+ static LLVoiceChannel* sCurrentVoiceChannel;
+};
+
+class LLVoiceChannelGroup : public LLVoiceChannel
+{
+public:
+ LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name);
+ virtual ~LLVoiceChannelGroup();
+
+ /*virtual*/ void handleError(EStatusType status);
+ /*virtual*/ void activate();
+ /*virtual*/ void deactivate();
+ /*virtual*/ void getChannelInfo();
+};
+
+class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoiceChannelProximal>
+{
+public:
+ LLVoiceChannelProximal();
+ virtual ~LLVoiceChannelProximal();
+
+ /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal);
+ /*virtual*/ void handleStatusChange(EStatusType status);
+ /*virtual*/ void handleError(EStatusType status);
+ /*virtual*/ BOOL isActive();
+ /*virtual*/ void activate();
+ /*virtual*/ void deactivate();
+
+};
+
+class LLVoiceChannelP2P : public LLVoiceChannelGroup
+{
+public:
+ LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id);
+ virtual ~LLVoiceChannelP2P();
+
+ /*virtual*/ void handleStatusChange(EStatusType status);
+ /*virtual*/ void handleError(EStatusType status);
+ /*virtual*/ void activate();
+ /*virtual*/ void getChannelInfo();
+
+ void setSessionHandle(const LLString& handle);
+
+private:
+ LLString mSessionHandle;
+ LLUUID mOtherUserID;
+};
class LLFloaterIMPanel : public LLFloater
{
public:
+
// The session id is the id of the session this is for. The target
// refers to the user (or group) that where this session serves as
// the default. For example, if you open a session though a
@@ -40,7 +147,7 @@ public:
const LLUUID& target_id,
const LLDynamicArray<LLUUID>& ids,
EInstantMessage dialog);
-
+ virtual ~LLFloaterIMPanel();
/*virtual*/ BOOL postBuild();
@@ -53,6 +160,10 @@ public:
// Return TRUE if successful, otherwise FALSE.
BOOL inviteToSession(const LLDynamicArray<LLUUID>& agent_ids);
+ void addHistoryLine(const LLUUID& source,
+ const std::string &utf8msg,
+ const LLColor4& color = LLColor4::white,
+ bool log_to_file = true);
void addHistoryLine(const std::string &utf8msg,
const LLColor4& color = LLColor4::white,
bool log_to_file = true);
@@ -71,15 +182,32 @@ public:
static void onInputEditorFocusReceived( LLUICtrl* caller, void* userdata );
static void onInputEditorFocusLost(LLUICtrl* caller, void* userdata);
static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata);
+ static void onCommitChat(LLUICtrl* caller, void* userdata);
static void onTabClick( void* userdata );
- static void onClickProfile( void* userdata ); // Profile button pressed
+ static void onClickProfile( void* userdata );
+ static void onClickGroupInfo( void* userdata );
static void onClickClose( void* userdata );
+ static void onClickStartCall( void* userdata );
+ static void onClickEndCall( void* userdata );
+ static void onClickSend( void* userdata );
+ static void onClickToggleActiveSpeakers( void* userdata );
+ static void* createSpeakersPanel(void* data);
+
+ //callbacks for P2P muting and volume control
+ static void onClickMuteVoice(LLUICtrl* source, void* user_data);
+ static void onVolumeChange(LLUICtrl* source, void* user_data);
const LLUUID& getSessionID() const { return mSessionUUID; }
const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; }
+ void updateSpeakersList(LLSD speaker_updates);
+ void setSpeakersListFromMap(LLSD speaker_list);
+ void setSpeakersList(LLSD speaker_list);
+ LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; }
EInstantMessage getDialogType() const { return mDialog; }
+ void requestAutoConnect();
+
void sessionInitReplyReceived(const LLUUID& im_session_id);
// Handle other participant in the session typing.
@@ -98,7 +226,7 @@ private:
BOOL dropCategory(LLInventoryCategory* category, BOOL drop);
// test if local agent can add agents.
- BOOL isAddAllowed() const;
+ BOOL isInviteAllowed() const;
// Called whenever the user starts or stops typing.
// Sends the typing state to the other user if necessary.
@@ -108,7 +236,7 @@ private:
void addTypingIndicator(const std::string &name);
// Remove the "User is typing..." indicator.
- void removeTypingIndicator();
+ void removeTypingIndicator(const LLIMInfo* im_info);
void sendTypingState(BOOL typing);
@@ -125,7 +253,8 @@ private:
// 911 ==> Gaurdian_Angel_Group_ID ^ gAgent.getID()
LLUUID mSessionUUID;
- BOOL mSessionInitRequested;
+ LLVoiceChannel* mVoiceChannel;
+
BOOL mSessionInitialized;
LLSD mQueuedMsgsForInit;
@@ -150,10 +279,17 @@ private:
// Where does the "User is typing..." line start?
S32 mTypingLineStartIndex;
- //Where does the "Starting session..." line start?
+ // Where does the "Starting session..." line start?
S32 mSessionStartMsgPos;
-
+
BOOL mSentTypingState;
+
+ BOOL mShowSpeakersOnConnect;
+
+ BOOL mAutoConnect;
+
+ LLIMSpeakerMgr* mSpeakers;
+ LLPanelActiveSpeakers* mSpeakerPanel;
// Optimization: Don't send "User is typing..." until the
// user has actually been typing for a little while. Prevents
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 5a6cbb045d..d8643ab9c1 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -1,5 +1,5 @@
/**
- * @file llimview.cpp
+ * @file LLIMMgr.cpp
* @brief Container for Instant Messaging
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
@@ -12,9 +12,9 @@
#include "llfontgl.h"
#include "llrect.h"
-#include "lldbstrings.h"
#include "llerror.h"
#include "llbutton.h"
+#include "llhttpclient.h"
#include "llsdutil.h"
#include "llstring.h"
#include "linked_lists.h"
@@ -26,8 +26,8 @@
#include "llviewerwindow.h"
#include "llresmgr.h"
#include "llfloaterchat.h"
+#include "llfloaterchatterbox.h"
#include "llfloaternewim.h"
-#include "llhttpclient.h"
#include "llhttpnode.h"
#include "llimpanel.h"
#include "llresizebar.h"
@@ -42,40 +42,48 @@
#include "llcallingcard.h"
#include "lltoolbar.h"
#include "llviewermessage.h"
+#include "llnotify.h"
#include "llviewerregion.h"
+#include "llfirstuse.h"
+
const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START;
const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL;
//
// Globals
//
-LLIMView* gIMView = NULL;
+LLIMMgr* gIMMgr = NULL;
//
// Statics
//
+//*FIXME: make these all either UIStrings or Strings
static LLString sOnlyUserMessage;
-static LLString sOfflineMessage;
+static LLUIString sOfflineMessage;
static std::map<std::string,LLString> sEventStringsMap;
static std::map<std::string,LLString> sErrorStringsMap;
static std::map<std::string,LLString> sForceCloseSessionMap;
+static LLUIString sInviteMessage;
//
// Helper Functions
//
// returns true if a should appear before b
-static BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b )
-{
- return (LLString::compareDict( a->mName, b->mName ) < 0);
-}
+//static BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b )
+//{
+// return (LLString::compareDict( a->mName, b->mName ) < 0);
+//}
// the other_participant_id is either an agent_id, a group_id, or an inventory
// folder item_id (collection of calling cards)
-static LLUUID compute_session_id(EInstantMessage dialog,
- const LLUUID& other_participant_id)
+
+// static
+LLUUID LLIMMgr::computeSessionID(
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id)
{
LLUUID session_id;
if (IM_SESSION_GROUP_START == dialog)
@@ -87,6 +95,11 @@ static LLUUID compute_session_id(EInstantMessage dialog,
{
session_id.generate();
}
+ else if (IM_SESSION_INVITE == dialog)
+ {
+ // use provided session id for invites
+ session_id = other_participant_id;
+ }
else
{
LLUUID agent_id = gAgent.getID();
@@ -121,87 +134,25 @@ LLFloaterIM::LLFloaterIM()
BOOL LLFloaterIM::postBuild()
{
- requires("only_user_message", WIDGET_TYPE_TEXT_BOX);
- requires("offline_message", WIDGET_TYPE_TEXT_BOX);
- requires("generic_request_error", WIDGET_TYPE_TEXT_BOX);
- requires("insufficient_perms_error", WIDGET_TYPE_TEXT_BOX);
- requires("generic_request_error", WIDGET_TYPE_TEXT_BOX);
- requires("add_session_event", WIDGET_TYPE_TEXT_BOX);
- requires("message_session_event", WIDGET_TYPE_TEXT_BOX);
- requires("removed_from_group", WIDGET_TYPE_TEXT_BOX);
-
- if (checkRequirements())
- {
- sOnlyUserMessage = childGetText("only_user_message");
- sOfflineMessage = childGetText("offline_message");
-
- sErrorStringsMap["generic"] =
- childGetText("generic_request_error");
- sErrorStringsMap["unverified"] =
- childGetText("insufficient_perms_error");
- sErrorStringsMap["no_user_911"] =
- childGetText("user_no_help");
+ sOnlyUserMessage = getFormattedUIString("only_user_message");
+ sOfflineMessage = getUIString("offline_message");
- sEventStringsMap["add"] = childGetText("add_session_event");
- sEventStringsMap["message"] =
- childGetText("message_session_event");
+ sErrorStringsMap["generic"] =
+ getFormattedUIString("generic_request_error");
+ sErrorStringsMap["unverified"] =
+ getFormattedUIString("insufficient_perms_error");
+ sErrorStringsMap["no_user_911"] =
+ getFormattedUIString("user_no_help");
- sForceCloseSessionMap["removed"] =
- childGetText("removed_from_group");
+ sEventStringsMap["add"] = childGetText("add_session_event");
+ sEventStringsMap["message"] =
+ getFormattedUIString("message_session_event");
- return TRUE;
- }
- return FALSE;
-}
-
-//// virtual
-//BOOL LLFloaterIM::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
-//{
-// BOOL handled = FALSE;
-// if (getEnabled()
-// && mask == (MASK_CONTROL|MASK_SHIFT))
-// {
-// if (key == 'W')
-// {
-// LLFloater* floater = getActiveFloater();
-// if (floater)
-// {
-// if (mTabContainer->getTabCount() == 1)
-// {
-// // trying to close last tab, close
-// // entire window.
-// close();
-// handled = TRUE;
-// }
-// }
-// }
-// }
-// return handled || LLMultiFloater::handleKeyHere(key, mask, called_from_parent);
-//}
+ sForceCloseSessionMap["removed"] =
+ getFormattedUIString("removed_from_group");
-void LLFloaterIM::onClose(bool app_quitting)
-{
- if (!app_quitting)
- {
- gSavedSettings.setBOOL("ShowIM", FALSE);
- }
- setVisible(FALSE);
-}
-
-//virtual
-void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
-{
-/*
- Code removed via patch from VWR-1626
-
- // this code is needed to fix the bug where new IMs received will resize the IM floater.
- // SL-29075, SL-24556, and others
- LLRect parent_rect = getRect();
- S32 dheight = LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT;
- LLRect rect(0, parent_rect.getHeight()-dheight, parent_rect.getWidth(), 0);
- floaterp->reshape(rect.getWidth(), rect.getHeight(), TRUE);
-*/
- LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+ sInviteMessage = getUIString("invite_message");
+ return TRUE;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -213,7 +164,7 @@ void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLT
class LLIMViewFriendObserver : public LLFriendObserver
{
public:
- LLIMViewFriendObserver(LLIMView* tv) : mTV(tv) {}
+ LLIMViewFriendObserver(LLIMMgr* tv) : mTV(tv) {}
virtual ~LLIMViewFriendObserver() {}
virtual void changed(U32 mask)
{
@@ -223,7 +174,30 @@ public:
}
}
protected:
- LLIMView* mTV;
+ LLIMMgr* mTV;
+};
+
+
+class LLIMMgr::LLIMSessionInvite
+{
+public:
+ LLIMSessionInvite(const LLUUID& session_id, const LLString& session_name, const LLUUID& caller_id,const LLString& caller_name, EInstantMessage type, const LLString& session_handle, const LLString& notify_box) :
+ mSessionID(session_id),
+ mSessionName(session_name),
+ mCallerID(caller_id),
+ mCallerName(caller_name),
+ mType(type),
+ mSessionHandle(session_handle),
+ mNotifyBox(notify_box)
+ {};
+
+ LLUUID mSessionID;
+ LLString mSessionName;
+ LLUUID mCallerID;
+ LLString mCallerName;
+ EInstantMessage mType;
+ LLString mSessionHandle;
+ LLString mNotifyBox;
};
@@ -234,7 +208,7 @@ protected:
// This is a helper function to determine what kind of im session
// should be used for the given agent.
// static
-EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id)
+EInstantMessage LLIMMgr::defaultIMTypeForAgent(const LLUUID& agent_id)
{
EInstantMessage type = IM_NOTHING_SPECIAL;
if(is_agent_friend(agent_id))
@@ -248,20 +222,20 @@ EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id)
}
// static
-//void LLIMView::onPinButton(void*)
+//void LLIMMgr::onPinButton(void*)
//{
// BOOL state = gSavedSettings.getBOOL( "PinTalkViewOpen" );
// gSavedSettings.setBOOL( "PinTalkViewOpen", !state );
//}
// static
-void LLIMView::toggle(void*)
+void LLIMMgr::toggle(void*)
{
static BOOL return_to_mouselook = FALSE;
// Hide the button and show the floater or vice versa.
- llassert( gIMView );
- BOOL old_state = gIMView->getFloaterOpen();
+ llassert( gIMMgr );
+ BOOL old_state = gIMMgr->getFloaterOpen();
// If we're in mouselook and we triggered the Talk View, we want to talk.
if( gAgent.cameraMouselook() && old_state )
@@ -292,53 +266,39 @@ void LLIMView::toggle(void*)
return_to_mouselook = FALSE;
}
- gIMView->setFloaterOpen( new_state );
+ gIMMgr->setFloaterOpen( new_state );
}
//
// Member Functions
//
-LLIMView::LLIMView(const std::string& name, const LLRect& rect) :
- LLView(name, rect, FALSE),
+LLIMMgr::LLIMMgr() :
mFriendObserver(NULL),
mIMReceived(FALSE)
{
- gIMView = this;
mFriendObserver = new LLIMViewFriendObserver(this);
LLAvatarTracker::instance().addObserver(mFriendObserver);
- mTalkFloater = new LLFloaterIM();
+ //*HACK: use floater to initialize string constants from xml file
+ // then delete it right away
+ LLFloaterIM* dummy_floater = new LLFloaterIM();
+ delete dummy_floater;
- // New IM Panel
- mNewIMFloater = new LLFloaterNewIM();
- mTalkFloater->addFloater(mNewIMFloater, TRUE);
-
- // Tabs sometimes overlap resize handle
- mTalkFloater->moveResizeHandleToFront();
+ mPendingVoiceInvitations = LLSD::emptyMap();
}
-LLIMView::~LLIMView()
+LLIMMgr::~LLIMMgr()
{
LLAvatarTracker::instance().removeObserver(mFriendObserver);
delete mFriendObserver;
// Children all cleaned up by default view destructor.
}
-EWidgetType LLIMView::getWidgetType() const
-{
- return WIDGET_TYPE_TALK_VIEW;
-}
-
-LLString LLIMView::getWidgetTag() const
-{
- return LL_TALK_VIEW_TAG;
-}
-
// Add a message to a session.
-void LLIMView::addMessage(
+void LLIMMgr::addMessage(
const LLUUID& session_id,
- const LLUUID& other_participant_id,
+ const LLUUID& target_id,
const char* from,
const char* msg,
const char* session_name,
@@ -347,11 +307,30 @@ void LLIMView::addMessage(
const LLUUID& region_id,
const LLVector3& position)
{
+ LLUUID other_participant_id = target_id;
+ bool is_from_system = target_id.isNull();
+
+ // don't process muted IMs
+ if (gMuteListp->isMuted(
+ other_participant_id,
+ LLMute::flagTextChat) && !gMuteListp->isLinden(from))
+ {
+ return;
+ }
+
+ //not sure why...but if it is from ourselves we set the target_id
+ //to be NULL
+ if( other_participant_id == gAgent.getID() )
+ {
+ other_participant_id = LLUUID::null;
+ }
+
LLFloaterIMPanel* floater;
LLUUID new_session_id = session_id;
if (new_session_id.isNull())
{
- new_session_id = compute_session_id(dialog, other_participant_id);
+ //no session ID...compute new one
+ new_session_id = computeSessionID(dialog, other_participant_id);
}
floater = findFloaterBySession(new_session_id);
if (!floater)
@@ -363,20 +342,10 @@ void LLIMView::addMessage(
<< " by participant " << other_participant_id << llendl;
}
}
- if(floater)
- {
- floater->addHistoryLine(msg);
- }
- else
- {
- //if we have recently requsted to be dropped from a session
- //but are still receiving messages from the session, don't make
- //a new floater
- if ( mSessionsDropRequested.has(session_id.asString()) )
- {
- return ;
- }
+ // create IM window as necessary
+ if(!floater)
+ {
const char* name = from;
if(session_name && (strlen(session_name)>1))
{
@@ -384,7 +353,12 @@ void LLIMView::addMessage(
}
- floater = createFloater(new_session_id, other_participant_id, name, dialog, FALSE);
+ floater = createFloater(
+ new_session_id,
+ other_participant_id,
+ name,
+ dialog,
+ FALSE);
// When we get a new IM, and if you are a god, display a bit
// of information about the source. This is to help liaisons
@@ -404,27 +378,41 @@ void LLIMView::addMessage(
//<< "*** region_id: " << region_id << std::endl
//<< "*** position: " << position << std::endl;
- floater->addHistoryLine(bonus_info.str());
+ floater->addHistoryLine(bonus_info.str(), gSavedSettings.getColor4("SystemChatColor"));
}
- floater->addHistoryLine(msg);
make_ui_sound("UISndNewIncomingIMSession");
}
- if( !mTalkFloater->getVisible() && !floater->getVisible())
+ // now add message to floater
+ if ( is_from_system ) // chat came from system
+ {
+ floater->addHistoryLine(
+ other_participant_id,
+ msg,
+ gSavedSettings.getColor4("SystemChatColor"));
+ }
+ else
+ {
+ floater->addHistoryLine(other_participant_id, msg);
+ }
+
+ LLFloaterChatterBox* chat_floater = LLFloaterChatterBox::getInstance(LLSD());
+
+ if( !chat_floater->getVisible() && !floater->getVisible())
{
//if the IM window is not open and the floater is not visible (i.e. not torn off)
- LLFloater* previouslyActiveFloater = mTalkFloater->getActiveFloater();
+ LLFloater* previouslyActiveFloater = chat_floater->getActiveFloater();
// select the newly added floater (or the floater with the new line added to it).
// it should be there.
- mTalkFloater->selectFloater(floater);
+ chat_floater->selectFloater(floater);
//there was a previously unseen IM, make that old tab flashing
//it is assumed that the most recently unseen IM tab is the one current selected/active
if ( previouslyActiveFloater && getIMReceived() )
{
- mTalkFloater->setFloaterFlashing(previouslyActiveFloater, TRUE);
+ chat_floater->setFloaterFlashing(previouslyActiveFloater, TRUE);
}
//notify of a new IM
@@ -432,37 +420,87 @@ void LLIMView::addMessage(
}
}
-void LLIMView::notifyNewIM()
+void LLIMMgr::addSystemMessage(const LLUUID& session_id, const LLString& message_name, const LLString::format_map_t& args)
+{
+ LLUIString message;
+
+ // null session id means near me (chat history)
+ if (session_id.isNull())
+ {
+ LLFloaterChat* floaterp = LLFloaterChat::getInstance();
+
+ message = floaterp->getUIString(message_name);
+ message.setArgList(args);
+
+ LLChat chat(message);
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
+ LLFloaterChat::getInstance()->addChatHistory(chat);
+ }
+ else // going to IM session
+ {
+ LLFloaterIMPanel* floaterp = findFloaterBySession(session_id);
+ if (floaterp)
+ {
+ message = floaterp->getUIString(message_name);
+ message.setArgList(args);
+
+ gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString().c_str());
+ }
+ }
+}
+
+void LLIMMgr::notifyNewIM()
{
- if(!gIMView->getFloaterOpen())
+ if(!gIMMgr->getFloaterOpen())
{
mIMReceived = TRUE;
}
}
-BOOL LLIMView::getIMReceived() const
+void LLIMMgr::clearNewIMNotification()
+{
+ mIMReceived = FALSE;
+}
+
+BOOL LLIMMgr::getIMReceived() const
{
return mIMReceived;
}
// This method returns TRUE if the local viewer has a session
// currently open keyed to the uuid.
-BOOL LLIMView::isIMSessionOpen(const LLUUID& uuid)
+BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid)
{
LLFloaterIMPanel* floater = findFloaterBySession(uuid);
if(floater) return TRUE;
return FALSE;
}
+LLUUID LLIMMgr::addP2PSession(const std::string& name,
+ const LLUUID& other_participant_id,
+ const LLString& voice_session_handle)
+{
+ LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id);
+
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(floater)
+ {
+ LLVoiceChannelP2P* voice_channelp = (LLVoiceChannelP2P*)floater->getVoiceChannel();
+ voice_channelp->setSessionHandle(voice_session_handle);
+ }
+
+ return session_id;
+}
+
// This adds a session to the talk view. The name is the local name of
// the session, dialog specifies the type of session. If the session
// exists, it is brought forward. Specifying id = NULL results in an
// im session to everyone. Returns the uuid of the session.
-LLUUID LLIMView::addSession(const std::string& name,
+LLUUID LLIMMgr::addSession(const std::string& name,
EInstantMessage dialog,
const LLUUID& other_participant_id)
{
- LLUUID session_id = compute_session_id(dialog, other_participant_id);
+ LLUUID session_id = computeSessionID(dialog, other_participant_id);
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
if(!floater)
@@ -478,7 +516,7 @@ LLUUID LLIMView::addSession(const std::string& name,
TRUE);
noteOfflineUsers(floater, ids);
- mTalkFloater->showFloater(floater);
+ LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater);
}
else
{
@@ -491,7 +529,7 @@ LLUUID LLIMView::addSession(const std::string& name,
// Adds a session using the given session_id. If the session already exists
// the dialog type is assumed correct. Returns the uuid of the session.
-LLUUID LLIMView::addSession(const std::string& name,
+LLUUID LLIMMgr::addSession(const std::string& name,
EInstantMessage dialog,
const LLUUID& other_participant_id,
const LLDynamicArray<LLUUID>& ids)
@@ -501,7 +539,7 @@ LLUUID LLIMView::addSession(const std::string& name,
return LLUUID::null;
}
- LLUUID session_id = compute_session_id(dialog,
+ LLUUID session_id = computeSessionID(dialog,
other_participant_id);
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
@@ -520,7 +558,7 @@ LLUUID LLIMView::addSession(const std::string& name,
noteOfflineUsers(floater, ids);
}
- mTalkFloater->showFloater(floater);
+ LLFloaterChatterBox::getInstance(LLSD())->showFloater(floater);
//mTabContainer->selectTabPanel(panel);
floater->setInputFocus(TRUE);
return floater->getSessionID();
@@ -528,134 +566,272 @@ LLUUID LLIMView::addSession(const std::string& name,
// This removes the panel referenced by the uuid, and then restores
// internal consistency. The internal pointer is not deleted.
-void LLIMView::removeSession(const LLUUID& session_id)
+void LLIMMgr::removeSession(const LLUUID& session_id)
{
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
if(floater)
{
mFloaters.erase(floater->getHandle());
- mTalkFloater->removeFloater(floater);
+ LLFloaterChatterBox::getInstance(LLSD())->removeFloater(floater);
//mTabContainer->removeTabPanel(floater);
- if(session_id.notNull()
- && (floater->getDialogType() != IM_NOTHING_SPECIAL))
- {
- mSessionsDropRequested[session_id.asString()] = LLSD();
- }
}
}
-void LLIMView::refresh()
+void LLIMMgr::inviteToSession(
+ const LLUUID& session_id,
+ const LLString& session_name,
+ const LLUUID& caller_id,
+ const LLString& caller_name,
+ EInstantMessage type,
+ const LLString& session_handle)
{
- S32 old_scroll_pos = mNewIMFloater->getScrollPos();
- mNewIMFloater->clearAllTargets();
+ //ignore voice invites from voice-muted residents
+ if (gMuteListp->isMuted(caller_id))
+ {
+ return;
+ }
- // build a list of groups.
- LLLinkedList<LLGroupData> group_list( group_dictionary_sort );
+ LLString notify_box_type;
- LLGroupData* group;
- S32 count = gAgent.mGroups.count();
- S32 i;
- // read/sort groups on the first pass.
- for(i = 0; i < count; ++i)
+ BOOL ad_hoc_invite = FALSE;
+ if(type == IM_SESSION_P2P_INVITE)
{
- group = &(gAgent.mGroups.get(i));
- group_list.addDataSorted( group );
+ notify_box_type = "VoiceInviteP2P";
}
-
- // add groups to the floater on the second pass.
- for(group = group_list.getFirstData();
- group;
- group = group_list.getNextData())
+ else if (gAgent.isInGroup(session_id))
{
- mNewIMFloater->addGroup(group->mID, (void*)(&GROUP_DIALOG), TRUE, FALSE);
+ notify_box_type = "VoiceInviteGroup";
+ }
+ else
+ {
+ notify_box_type = "VoiceInviteAdHoc";
+ ad_hoc_invite = TRUE;
}
- // build a set of buddies in the current buddy list.
- LLCollectAllBuddies collector;
- LLAvatarTracker::instance().applyFunctor(collector);
- LLCollectAllBuddies::buddy_map_t::iterator it;
- LLCollectAllBuddies::buddy_map_t::iterator end;
- it = collector.mOnline.begin();
- end = collector.mOnline.end();
- for( ; it != end; ++it)
+ LLIMSessionInvite* invite = new LLIMSessionInvite(
+ session_id,
+ session_name,
+ caller_id,
+ caller_name,
+ type,
+ session_handle,
+ notify_box_type);
+
+ LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
+ if (channelp && channelp->callStarted())
{
- mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), TRUE);
+ // you have already started a call to the other user, so just accept the invite
+ inviteUserResponse(0, invite);
+ return;
}
- it = collector.mOffline.begin();
- end = collector.mOffline.end();
- for( ; it != end; ++it)
+
+ if (type == IM_SESSION_P2P_INVITE || ad_hoc_invite)
{
- mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), FALSE);
+ // is the inviter a friend?
+ if (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL)
+ {
+ // if not, and we are ignoring voice invites from non-friends
+ // then silently decline
+ if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly"))
+ {
+ // invite not from a friend, so decline
+ inviteUserResponse(1, invite);
+ return;
+ }
+ }
}
- mNewIMFloater->setScrollPos( old_scroll_pos );
+ if ( !mPendingVoiceInvitations.has(session_id.asString()) )
+ {
+ if (caller_name.empty())
+ {
+ gCacheName->getName(caller_id, onInviteNameLookup, invite);
+ }
+ else
+ {
+ LLString::format_map_t args;
+ args["[NAME]"] = caller_name;
+ args["[GROUP]"] = session_name;
+
+ LLNotifyBox::showXml(notify_box_type,
+ args,
+ inviteUserResponse,
+ (void*)invite);
+
+ }
+ mPendingVoiceInvitations[session_id.asString()] = LLSD();
+ }
}
-// JC - This used to set console visibility. It doesn't any more.
-void LLIMView::setFloaterOpen(BOOL set_open)
+//static
+void LLIMMgr::onInviteNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* userdata)
{
- gSavedSettings.setBOOL("ShowIM", set_open);
+ LLIMSessionInvite* invite = (LLIMSessionInvite*)userdata;
- //RN "visible" and "open" are considered synonomous for now
- if (set_open)
+ invite->mCallerName = llformat("%s %s", first, last);
+ invite->mSessionName = invite->mCallerName;
+
+ LLString::format_map_t args;
+ args["[NAME]"] = invite->mCallerName;
+
+ LLNotifyBox::showXml(invite->mNotifyBox,
+ args,
+ inviteUserResponse,
+ (void*)invite);
+}
+
+class LLViewerChatterBoxInvitationAcceptResponder :
+ public LLHTTPClient::Responder
+{
+public:
+ LLViewerChatterBoxInvitationAcceptResponder(
+ const LLUUID& session_id,
+ bool is_voice_invitation)
{
- mTalkFloater->open(); /*Flawfinder: ignore*/
+ mSessionID = session_id;
+ mIsVoiceInvitiation = is_voice_invitation;
}
- else
+
+ void result(const LLSD& content)
{
- mTalkFloater->close();
+ if ( gIMMgr)
+ {
+ LLFloaterIMPanel* floaterp =
+ gIMMgr->findFloaterBySession(mSessionID);
+
+ if (floaterp)
+ {
+ floaterp->setSpeakersList(content["agents"]);
+
+ if ( mIsVoiceInvitiation )
+ {
+ floaterp->requestAutoConnect();
+ LLFloaterIMPanel::onClickStartCall(floaterp);
+ }
+ }
+
+ if ( mIsVoiceInvitiation )
+ {
+ gIMMgr->clearPendingVoiceInviation(mSessionID);
+ }
+ }
}
- if( set_open )
+ void error(U32 statusNum, const std::string& reason)
{
- // notifyNewIM();
-
- // We're showing the IM, so mark view as non-pending
- mIMReceived = FALSE;
+ //throw something back to the viewer here?
+ if ( gIMMgr && mIsVoiceInvitiation )
+ {
+ gIMMgr->clearPendingVoiceInviation(mSessionID);
+ }
}
-}
+private:
+ LLUUID mSessionID;
+ bool mIsVoiceInvitiation;
+};
-BOOL LLIMView::getFloaterOpen()
+//static
+void LLIMMgr::inviteUserResponse(S32 option, void* user_data)
{
- return mTalkFloater->getVisible();
-}
-
-void LLIMView::pruneSessions()
-{
- if(mNewIMFloater)
+ LLIMSessionInvite* invitep = (LLIMSessionInvite*)user_data;
+
+ switch(option)
{
- BOOL removed = TRUE;
- LLFloaterIMPanel* floater = NULL;
- while(removed)
+ case 0: // accept
+ {
+ if (invitep->mType == IM_SESSION_P2P_INVITE)
+ {
+ // create a normal IM session
+ invitep->mSessionID = gIMMgr->addP2PSession(
+ invitep->mSessionName,
+ invitep->mCallerID,
+ invitep->mSessionHandle);
+
+ LLFloaterIMPanel* im_floater =
+ gIMMgr->findFloaterBySession(
+ invitep->mSessionID);
+ if (im_floater)
+ {
+ im_floater->requestAutoConnect();
+ LLFloaterIMPanel::onClickStartCall(im_floater);
+ }
+
+ gIMMgr->clearPendingVoiceInviation(invitep->mSessionID);
+ }
+ else
+ {
+ gIMMgr->addSession(
+ invitep->mSessionName,
+ invitep->mType,
+ invitep->mSessionID);
+
+ std::string url = gAgent.getRegion()->getCapability(
+ "ChatSessionRequest");
+
+ LLSD data;
+ data["method"] = "accept invitation";
+ data["session-id"] = invitep->mSessionID;
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLViewerChatterBoxInvitationAcceptResponder(
+ invitep->mSessionID,
+ true));
+ }
+ }
+ break;
+ case 2: // mute (also implies ignore, so this falls through to the "ignore" case below)
+ {
+ // mute the sender of this invite
+ if (!gMuteListp->isMuted(invitep->mCallerID))
+ {
+ LLMute mute(invitep->mCallerID, invitep->mCallerName, LLMute::AGENT);
+ gMuteListp->add(mute);
+ }
+ }
+ /* FALLTHROUGH */
+
+ case 1: // ignore
{
- removed = FALSE;
- std::set<LLViewHandle>::iterator handle_it;
- for(handle_it = mFloaters.begin();
- handle_it != mFloaters.end();
- ++handle_it)
+ if (invitep->mType == IM_SESSION_P2P_INVITE)
{
- floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
- if(floater && !mNewIMFloater->isUUIDAvailable(floater->getOtherParticipantID()))
+ if(gVoiceClient)
{
- // remove this floater
- removed = TRUE;
- mFloaters.erase(handle_it++);
- floater->close();
- break;
+ gVoiceClient->declineInvite(invitep->mSessionHandle);
}
}
}
+ break;
}
+
+ delete invitep;
}
+void LLIMMgr::refresh()
+{
+}
-void LLIMView::disconnectAllSessions()
+void LLIMMgr::setFloaterOpen(BOOL set_open)
{
- if(mNewIMFloater)
+ if (set_open)
{
- mNewIMFloater->setEnabled(FALSE);
+ LLFloaterChatterBox::showInstance(LLSD());
}
+ else
+ {
+ LLFloaterChatterBox::hideInstance(LLSD());
+ }
+}
+
+
+BOOL LLIMMgr::getFloaterOpen()
+{
+ return LLFloaterChatterBox::instanceVisible(LLSD());
+}
+
+void LLIMMgr::disconnectAllSessions()
+{
LLFloaterIMPanel* floater = NULL;
std::set<LLViewHandle>::iterator handle_it;
for(handle_it = mFloaters.begin();
@@ -670,7 +846,7 @@ void LLIMView::disconnectAllSessions()
if (floater)
{
floater->setEnabled(FALSE);
- floater->onClose(TRUE);
+ floater->close(TRUE);
}
}
}
@@ -679,7 +855,7 @@ void LLIMView::disconnectAllSessions()
// This method returns the im panel corresponding to the uuid
// provided. The uuid can either be a session id or an agent
// id. Returns NULL if there is no matching panel.
-LLFloaterIMPanel* LLIMView::findFloaterBySession(const LLUUID& session_id)
+LLFloaterIMPanel* LLIMMgr::findFloaterBySession(const LLUUID& session_id)
{
LLFloaterIMPanel* rv = NULL;
std::set<LLViewHandle>::iterator handle_it;
@@ -698,17 +874,25 @@ LLFloaterIMPanel* LLIMView::findFloaterBySession(const LLUUID& session_id)
}
-BOOL LLIMView::hasSession(const LLUUID& session_id)
+BOOL LLIMMgr::hasSession(const LLUUID& session_id)
{
return (findFloaterBySession(session_id) != NULL);
}
+void LLIMMgr::clearPendingVoiceInviation(const LLUUID& session_id)
+{
+ if ( mPendingVoiceInvitations.has(session_id.asString()) )
+ {
+ mPendingVoiceInvitations.erase(session_id.asString());
+ }
+}
+
// create a floater and update internal representation for
// consistency. Returns the pointer, caller (the class instance since
// it is a private method) is not responsible for deleting the
// pointer. Add the floater to this but do not select it.
-LLFloaterIMPanel* LLIMView::createFloater(
+LLFloaterIMPanel* LLIMMgr::createFloater(
const LLUUID& session_id,
const LLUUID& other_participant_id,
const std::string& session_label,
@@ -720,7 +904,7 @@ LLFloaterIMPanel* LLIMView::createFloater(
llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
}
- llinfos << "LLIMView::createFloater: from " << other_participant_id
+ llinfos << "LLIMMgr::createFloater: from " << other_participant_id
<< " in session " << session_id << llendl;
LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
LLRect(),
@@ -729,12 +913,12 @@ LLFloaterIMPanel* LLIMView::createFloater(
other_participant_id,
dialog);
LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
- mTalkFloater->addFloater(floater, FALSE, i_pt);
+ LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt);
mFloaters.insert(floater->getHandle());
return floater;
}
-LLFloaterIMPanel* LLIMView::createFloater(
+LLFloaterIMPanel* LLIMMgr::createFloater(
const LLUUID& session_id,
const LLUUID& other_participant_id,
const std::string& session_label,
@@ -747,7 +931,7 @@ LLFloaterIMPanel* LLIMView::createFloater(
llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
}
- llinfos << "LLIMView::createFloater: from " << other_participant_id
+ llinfos << "LLIMMgr::createFloater: from " << other_participant_id
<< " in session " << session_id << llendl;
LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
LLRect(),
@@ -757,18 +941,18 @@ LLFloaterIMPanel* LLIMView::createFloater(
ids,
dialog);
LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
- mTalkFloater->addFloater(floater, FALSE, i_pt);
+ LLFloaterChatterBox::getInstance(LLSD())->addFloater(floater, FALSE, i_pt);
mFloaters.insert(floater->getHandle());
return floater;
}
-void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater,
+void LLIMMgr::noteOfflineUsers(LLFloaterIMPanel* floater,
const LLDynamicArray<LLUUID>& ids)
{
S32 count = ids.count();
if(count == 0)
{
- floater->addHistoryLine(sOnlyUserMessage);
+ floater->addHistoryLine(sOnlyUserMessage, gSavedSettings.getColor4("SystemChatColor"));
}
else
{
@@ -785,25 +969,25 @@ void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater,
LLUIString offline = sOfflineMessage;
offline.setArg("[FIRST]", first);
offline.setArg("[LAST]", last);
- floater->addHistoryLine(offline);
+ floater->addHistoryLine(offline, gSavedSettings.getColor4("SystemChatColor"));
}
}
}
}
-void LLIMView::processIMTypingStart(const LLIMInfo* im_info)
+void LLIMMgr::processIMTypingStart(const LLIMInfo* im_info)
{
processIMTypingCore(im_info, TRUE);
}
-void LLIMView::processIMTypingStop(const LLIMInfo* im_info)
+void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info)
{
processIMTypingCore(im_info, FALSE);
}
-void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
+void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
{
- LLUUID session_id = compute_session_id(im_info->mIMType, im_info->mFromID);
+ LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID);
LLFloaterIMPanel* floater = findFloaterBySession(session_id);
if (floater)
{
@@ -811,8 +995,9 @@ void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
}
}
-void LLIMView::updateFloaterSessionID(const LLUUID& old_session_id,
- const LLUUID& new_session_id)
+void LLIMMgr::updateFloaterSessionID(
+ const LLUUID& old_session_id,
+ const LLUUID& new_session_id)
{
LLFloaterIMPanel* floater = findFloaterBySession(old_session_id);
if (floater)
@@ -821,9 +1006,9 @@ void LLIMView::updateFloaterSessionID(const LLUUID& old_session_id,
}
}
-void LLIMView::onDropRequestReplyReceived(const LLUUID& session_id)
-{
- mSessionsDropRequested.erase(session_id.asString());
+LLFloaterChatterBox* LLIMMgr::getFloater()
+{
+ return LLFloaterChatterBox::getInstance(LLSD());
}
void onConfirmForceCloseError(S32 option, void* data)
@@ -831,25 +1016,24 @@ void onConfirmForceCloseError(S32 option, void* data)
//only 1 option really
LLFloaterIMPanel* floater = ((LLFloaterIMPanel*) data);
- if ( floater ) floater->onClose(FALSE);
+ if ( floater ) floater->close(FALSE);
}
-class LLViewerIMSessionStartReply : public LLHTTPNode
+class LLViewerChatterBoxSessionStartReply : public LLHTTPNode
{
public:
virtual void describe(Description& desc) const
{
- desc.shortInfo("Used for receiving a reply to a request to initialize an IM session");
+ desc.shortInfo("Used for receiving a reply to a request to initialize an ChatterBox session");
desc.postAPI();
desc.input(
"{\"client_session_id\": UUID, \"session_id\": UUID, \"success\" boolean, \"reason\": string");
desc.source(__FILE__, __LINE__);
}
- virtual void post(
- ResponsePtr response,
- const LLSD& context,
- const LLSD& input) const
+ virtual void post(ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
{
LLSD body;
LLUUID temp_session_id;
@@ -863,16 +1047,21 @@ public:
if ( success )
{
session_id = body["session_id"].asUUID();
- gIMView->updateFloaterSessionID(
+ gIMMgr->updateFloaterSessionID(
temp_session_id,
session_id);
+ LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id);
+ if (floaterp)
+ {
+ floaterp->setSpeakersList(body["agents"]);
+ }
}
else
{
//throw an error dialog and close the temp session's
//floater
LLFloaterIMPanel* floater =
- gIMView->findFloaterBySession(temp_session_id);
+ gIMMgr->findFloaterBySession(temp_session_id);
if (floater)
{
LLString::format_map_t args;
@@ -880,22 +1069,22 @@ public:
sErrorStringsMap[body["error"].asString()];
args["[RECIPIENT]"] = floater->getTitle();
- gViewerWindow->alertXml(
- "IMSessionStartError",
- args,
- onConfirmForceCloseError,
- floater);
+ gViewerWindow->alertXml("ChatterBoxSessionStartError",
+ args,
+ onConfirmForceCloseError,
+ floater);
+
}
}
}
};
-class LLViewerIMSessionEventReply : public LLHTTPNode
+class LLViewerChatterBoxSessionEventReply : public LLHTTPNode
{
public:
virtual void describe(Description& desc) const
{
- desc.shortInfo("Used for receiving a reply to a IM session event");
+ desc.shortInfo("Used for receiving a reply to a ChatterBox session event");
desc.postAPI();
desc.input(
"{\"event\": string, \"reason\": string, \"success\": boolean, \"session_id\": UUID");
@@ -917,7 +1106,7 @@ public:
{
//throw an error dialog
LLFloaterIMPanel* floater =
- gIMView->findFloaterBySession(session_id);
+ gIMMgr->findFloaterBySession(session_id);
if (floater)
{
LLString::format_map_t args;
@@ -927,14 +1116,14 @@ public:
sEventStringsMap[body["event"].asString()];
args["[RECIPIENT]"] = floater->getTitle();
- gViewerWindow->alertXml("IMSessionEventError",
+ gViewerWindow->alertXml("ChatterBoxSessionEventError",
args);
}
}
}
};
-class LLViewerForceCloseIMSession: public LLHTTPNode
+class LLViewerForceCloseChatterBoxSession: public LLHTTPNode
{
public:
virtual void post(ResponsePtr response,
@@ -948,7 +1137,7 @@ public:
reason = input["body"]["reason"].asString();
LLFloaterIMPanel* floater =
- gIMView ->findFloaterBySession(session_id);
+ gIMMgr ->findFloaterBySession(session_id);
if ( floater )
{
@@ -957,7 +1146,7 @@ public:
args["[NAME]"] = floater->getTitle();
args["[REASON]"] = sForceCloseSessionMap[reason];
- gViewerWindow->alertXml("ForceCloseIMSession",
+ gViewerWindow->alertXml("ForceCloseChatterBoxSession",
args,
onConfirmForceCloseError,
floater);
@@ -965,28 +1154,6 @@ public:
}
};
-class LLViewerIMSessionDropReply : public LLHTTPNode
-{
-public:
- virtual void post(ResponsePtr response,
- const LLSD& context,
- const LLSD& input) const
- {
- LLUUID session_id;
- bool success;
-
- success = input["body"]["success"].asBoolean();
- session_id = input["body"]["session_id"].asUUID();
-
- if ( !success )
- {
- //throw an error alert?
- }
-
- gIMView->onDropRequestReplyReceived(session_id);
- }
-};
-
class LLViewerChatterBoxSessionAgentListUpdates : public LLHTTPNode
{
public:
@@ -995,29 +1162,38 @@ public:
const LLSD& context,
const LLSD& input) const
{
+ LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID());
+ if (floaterp)
+ {
+ floaterp->updateSpeakersList(input["body"]["updates"]);
+ }
}
};
class LLViewerChatterBoxInvitation : public LLHTTPNode
{
public:
+
virtual void post(
- ResponsePtr responder,
+ ResponsePtr response,
const LLSD& context,
const LLSD& input) const
{
if ( input["body"].has("instantmessage") )
{
+ LLString capability = input["body"]["capabilities"]["call"].asString();
+
LLSD message_params =
input["body"]["instantmessage"]["message_params"];
+ //do something here to have the IM invite behave
+ //just like a normal IM
//this is just replicated code from process_improved_im
//and should really go in it's own function -jwolk
if (gNoRender)
{
return;
}
-
char buffer[DB_IM_MSG_BUF_SIZE * 2]; /* Flawfinder: ignore */
LLChat chat;
@@ -1032,7 +1208,11 @@ public:
(time_t) message_params["timestamp"].asInteger();
BOOL is_busy = gAgent.getBusy();
- BOOL is_muted = gMuteListp->isMuted(from_id, name);
+ BOOL is_muted = gMuteListp->isMuted(
+ from_id,
+ name.c_str(),
+ LLMute::flagTextChat);
+
BOOL is_linden = gMuteListp->isLinden(
name.c_str());
char separator_string[3]=": "; /* Flawfinder: ignore */
@@ -1049,7 +1229,8 @@ public:
chat.mMuted = is_muted && !is_linden;
chat.mFromID = from_id;
chat.mFromName = name;
- if (!is_linden && is_busy)
+
+ if (!is_linden && (is_busy || is_muted))
{
return;
}
@@ -1077,10 +1258,9 @@ public:
BOOL is_this_agent = FALSE;
if(from_id == gAgentID)
{
- from_id = LLUUID::null;
is_this_agent = TRUE;
}
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name.c_str(),
@@ -1102,11 +1282,7 @@ public:
chat.mText = buffer;
LLFloaterChat::addChat(chat, TRUE, is_this_agent);
- //if we succesfully accepted the invitation
- //send a message back down
-
- //TODO - When availble, have this response just be part
- //of an automatic response system
+ //K now we want to accept the invitation
std::string url = gAgent.getRegion()->getCapability(
"ChatSessionRequest");
@@ -1118,28 +1294,46 @@ public:
LLHTTPClient::post(
url,
data,
- NULL);
+ new LLViewerChatterBoxInvitationAcceptResponder(
+ input["body"]["session_id"],
+ false));
}
} //end if invitation has instant message
+ else if ( input["body"].has("voice") )
+ {
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if(!LLVoiceClient::voiceEnabled())
+ {
+ // Don't display voice invites unless the user has voice enabled.
+ return;
+ }
+
+ gIMMgr->inviteToSession(
+ input["body"]["session_id"].asUUID(),
+ input["body"]["session_name"].asString(),
+ input["body"]["from_id"].asUUID(),
+ input["body"]["from_name"].asString(),
+ IM_SESSION_INVITE);
+ }
}
};
-LLHTTPRegistration<LLViewerIMSessionStartReply>
- gHTTPRegistrationMessageImsessionstartreply(
+LLHTTPRegistration<LLViewerChatterBoxSessionStartReply>
+ gHTTPRegistrationMessageChatterboxsessionstartreply(
"/message/ChatterBoxSessionStartReply");
-LLHTTPRegistration<LLViewerIMSessionEventReply>
- gHTTPRegistrationMessageImsessioneventreply(
+LLHTTPRegistration<LLViewerChatterBoxSessionEventReply>
+ gHTTPRegistrationMessageChatterboxsessioneventreply(
"/message/ChatterBoxSessionEventReply");
-LLHTTPRegistration<LLViewerForceCloseIMSession>
- gHTTPRegistrationMessageForceCloseImSession(
+LLHTTPRegistration<LLViewerForceCloseChatterBoxSession>
+ gHTTPRegistrationMessageForceclosechatterboxsession(
"/message/ForceCloseChatterBoxSession");
-LLHTTPRegistration<LLViewerIMSessionDropReply>
- gHTTPRegistrationMessageImSessionDropReply(
- "/message/ChatterBoxSessionLeaveReply");
-
LLHTTPRegistration<LLViewerChatterBoxSessionAgentListUpdates>
gHTTPRegistrationMessageChatterboxsessionagentlistupdates(
"/message/ChatterBoxSessionAgentListUpdates");
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index aac6fd63ce..f8a36107d6 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -1,5 +1,5 @@
/**
- * @file llimview.h
+ * @file LLIMMgr.h
* @brief Container for Instant Messaging
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
@@ -13,31 +13,32 @@
#include "llinstantmessage.h"
#include "lluuid.h"
-class LLFloaterNewIM;
+class LLFloaterChatterBox;
class LLUUID;
class LLFloaterIMPanel;
class LLFriendObserver;
class LLFloaterIM;
-class LLIMView : public LLView
+class LLIMMgr : public LLSingleton<LLIMMgr>
{
public:
- LLIMView(const std::string& name, const LLRect& rect);
- ~LLIMView();
-
- virtual EWidgetType getWidgetType() const;
- virtual LLString getWidgetTag() const;
+ LLIMMgr();
+ virtual ~LLIMMgr();
// Add a message to a session. The session can keyed to sesion id
// or agent id.
- void addMessage(const LLUUID& session_id, const LLUUID& target_id,
- const char* from, const char* msg,
+ void addMessage(const LLUUID& session_id,
+ const LLUUID& target_id,
+ const char* from,
+ const char* msg,
const char* session_name = NULL,
EInstantMessage dialog = IM_NOTHING_SPECIAL,
U32 parent_estate_id = 0,
const LLUUID& region_id = LLUUID::null,
const LLVector3& position = LLVector3::zero);
+ void addSystemMessage(const LLUUID& session_id, const LLString& message_name, const LLString::format_map_t& args);
+
// This method returns TRUE if the local viewer has a session
// currently open keyed to the uuid. The uuid can be keyed by
// either session id or agent id.
@@ -62,11 +63,23 @@ public:
const LLUUID& other_participant_id,
const LLDynamicArray<LLUUID>& ids);
+ // Creates a P2P session with the requisite handle for responding to voice calls
+ LLUUID addP2PSession(const std::string& name,
+ const LLUUID& other_participant_id,
+ const LLString& voice_session_handle);
+
// This removes the panel referenced by the uuid, and then
// restores internal consistency. The internal pointer is not
// deleted.
void removeSession(const LLUUID& session_id);
+ void inviteToSession(const LLUUID& session_id,
+ const LLString& session_name,
+ const LLUUID& caller,
+ const LLString& caller_name,
+ EInstantMessage type,
+ const LLString& session_handle = LLString::null);
+
//Updates a given session's session IDs. Does not open,
//create or do anything new. If the old session doesn't
//exist, then nothing happens.
@@ -80,6 +93,7 @@ public:
void refresh();
void notifyNewIM();
+ void clearNewIMNotification();
// IM received that you haven't seen yet
BOOL getIMReceived() const;
@@ -87,10 +101,7 @@ public:
void setFloaterOpen(BOOL open); /*Flawfinder: ignore*/
BOOL getFloaterOpen();
- LLFloaterIM * getFloater() { return mTalkFloater; }
-
- // close any sessions which are not available in the newimpanel.
- void pruneSessions();
+ LLFloaterChatterBox* getFloater();
// This method is used to go through all active sessions and
// disable all of them. This method is usally called when you are
@@ -111,9 +122,13 @@ public:
// is no matching panel.
LLFloaterIMPanel* findFloaterBySession(const LLUUID& session_id);
- void onDropRequestReplyReceived(const LLUUID& session_id);
+ static LLUUID computeSessionID(EInstantMessage dialog, const LLUUID& other_participant_id);
+ void clearPendingVoiceInviation(const LLUUID& session_id);
+
private:
+ class LLIMSessionInvite;
+
// create a panel and update internal representation for
// consistency. Returns the pointer, caller (the class instance
// since it is a private method) is not responsible for deleting
@@ -139,9 +154,8 @@ private:
void processIMTypingCore(const LLIMInfo* im_info, BOOL typing);
-public:
- LLFloaterIM* mTalkFloater;
- LLFloaterNewIM* mNewIMFloater;
+ static void inviteUserResponse(S32 option, void* user_data);
+ static void onInviteNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* userdata);
private:
std::set<LLViewHandle> mFloaters;
@@ -150,7 +164,7 @@ private:
// An IM has been received that you haven't seen yet.
BOOL mIMReceived;
- LLSD mSessionsDropRequested;
+ LLSD mPendingVoiceInvitations;
};
@@ -158,13 +172,10 @@ class LLFloaterIM : public LLMultiFloater
{
public:
LLFloaterIM();
- ///*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
/*virtual*/ BOOL postBuild();
- /*virtual*/ void onClose(bool app_quitting);
- /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainerCommon::END);
};
// Globals
-extern LLIMView *gIMView;
+extern LLIMMgr *gIMMgr;
#endif // LL_LLIMView_H
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 4a1d496d6f..8a08bd2a4b 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -2573,8 +2573,8 @@ void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel*
if (item && (item->getCreatorUUID() != gAgent.getID()) &&
(!item->getCreatorUUID().isNull()))
{
- gIMView->setFloaterOpen(TRUE);
- gIMView->addSession(item->getName(), IM_NOTHING_SPECIAL, item->getCreatorUUID());
+ gIMMgr->setFloaterOpen(TRUE);
+ gIMMgr->addSession(item->getName(), IM_NOTHING_SPECIAL, item->getCreatorUUID());
}
}
else if ("lure" == action)
@@ -2651,7 +2651,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
BOOL good_card = (item
&& (LLUUID::null != item->getCreatorUUID())
&& (item->getCreatorUUID() != gAgent.getID()));
-
+ BOOL user_online = (LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()));
items.push_back("Send Instant Message");
items.push_back("Offer Teleport...");
items.push_back("Conference Chat");
@@ -2659,6 +2659,9 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
if (!good_card)
{
disabled_items.push_back("Send Instant Message");
+ }
+ if (!good_card || !user_online)
+ {
disabled_items.push_back("Offer Teleport...");
disabled_items.push_back("Conference Chat");
}
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index f7aa4d4251..793571e111 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1466,11 +1466,18 @@ bool LLInventoryModel::loadSkeleton(
{
LLViewerInventoryCategory* cat = categories[i];
cat_set_t::iterator cit = temp_cats.find(cat);
+ if (cit == temp_cats.end())
+ {
+ continue; // cache corruption?? not sure why this happens -SJB
+ }
LLViewerInventoryCategory* tcat = *cit;
// we can safely ignore anything loaded from file, but
// not sent down in the skeleton.
- if(cit == not_cached) continue;
+ if(cit == not_cached)
+ {
+ continue;
+ }
if(cat->getVersion() != tcat->getVersion())
{
// if the cached version does not match the server version,
diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp
index 8b07dfbfeb..62c7d40a90 100644
--- a/indra/newview/llmutelist.cpp
+++ b/indra/newview/llmutelist.cpp
@@ -169,11 +169,11 @@ BOOL LLMuteList::isLinden(const LLString& name) const
}
-BOOL LLMuteList::add(const LLMute& mute)
+BOOL LLMuteList::add(const LLMute& mute, U32 flags)
{
- // Can't mute Lindens
+ // Can't mute text from Lindens
if ((mute.mType == LLMute::AGENT || mute.mType == LLMute::BY_NAME)
- && isLinden(mute.mName))
+ && isLinden(mute.mName) && (flags & LLMute::flagTextChat || flags == 0))
{
gViewerWindow->alertXml("MuteLinden");
return FALSE;
@@ -218,25 +218,59 @@ BOOL LLMuteList::add(const LLMute& mute)
}
else
{
- std::pair<mute_set_t::iterator, bool> result = mMutes.insert(mute);
- if (result.second)
+ // Need a local (non-const) copy to set up flags properly.
+ LLMute localmute = mute;
+
+ // If an entry for the same entity is already in the list, remove it, saving flags as necessary.
+ mute_set_t::iterator it = mMutes.find(localmute);
+ if (it != mMutes.end())
{
- llinfos << "Muting " << mute.mName << " id " << mute.mID << llendl;
- updateAdd(mute);
- notifyObservers();
- //Kill all particle systems owned by muted task
- if(mute.mType == LLMute::AGENT || mute.mType == LLMute::OBJECT)
- {
- gWorldPointer->mPartSim.cleanMutedParticles(mute.mID);
- }
+ // This mute is already in the list. Save the existing entry's flags if that's warranted.
+ localmute.mFlags = it->mFlags;
+
+ mMutes.erase(it);
+ // Don't need to call notifyObservers() here, since it will happen after the entry has been re-added below.
+ }
+ else
+ {
+ // There was no entry in the list previously. Fake things up by making it look like the previous entry had all properties unmuted.
+ localmute.mFlags = LLMute::flagAll;
+ }
- return TRUE;
+ if(flags)
+ {
+ // The user passed some combination of flags. Make sure those flag bits are turned off (i.e. those properties will be muted).
+ localmute.mFlags &= (~flags);
}
else
{
- return FALSE;
+ // The user passed 0. Make sure all flag bits are turned off (i.e. all properties will be muted).
+ localmute.mFlags = 0;
+ }
+
+ // (re)add the mute entry.
+ {
+ std::pair<mute_set_t::iterator, bool> result = mMutes.insert(localmute);
+ if (result.second)
+ {
+ llinfos << "Muting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
+ updateAdd(mute);
+ notifyObservers();
+ if(!(localmute.mFlags & LLMute::flagParticles))
+ {
+ //Kill all particle systems owned by muted task
+ if(localmute.mType == LLMute::AGENT || localmute.mType == LLMute::OBJECT)
+ {
+ gWorldPointer->mPartSim.cleanMutedParticles(localmute.mID);
+ }
+ }
+ return TRUE;
+ }
}
}
+
+ // If we were going to return success, we'd have done it by now.
+ return FALSE;
}
void LLMuteList::updateAdd(const LLMute& mute)
@@ -251,14 +285,14 @@ void LLMuteList::updateAdd(const LLMute& mute)
msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
msg->addStringFast(_PREHASH_MuteName, mute.mName);
msg->addS32("MuteType", mute.mType);
- msg->addU32("MuteFlags", 0x0); // future
+ msg->addU32("MuteFlags", mute.mFlags);
gAgent.sendReliableMessage();
mIsLoaded = TRUE;
}
-BOOL LLMuteList::remove(const LLMute& mute)
+BOOL LLMuteList::remove(const LLMute& mute, U32 flags)
{
BOOL found = FALSE;
@@ -266,8 +300,46 @@ BOOL LLMuteList::remove(const LLMute& mute)
mute_set_t::iterator it = mMutes.find(mute);
if (it != mMutes.end())
{
- updateRemove(*it);
+ LLMute localmute = *it;
+ bool remove = true;
+ if(flags)
+ {
+ // If the user passed mute flags, we may only want to turn some flags on.
+ localmute.mFlags |= flags;
+
+ if(localmute.mFlags == LLMute::flagAll)
+ {
+ // Every currently available mute property has been masked out.
+ // Remove the mute entry entirely.
+ }
+ else
+ {
+ // Only some of the properties are masked out. Update the entry.
+ remove = false;
+ }
+ }
+ else
+ {
+ // The caller didn't pass any flags -- just remove the mute entry entirely.
+ }
+
+ // Always remove the entry from the set -- it will be re-added with new flags if necessary.
mMutes.erase(it);
+
+ if(remove)
+ {
+ // The entry was actually removed. Notify the server.
+ updateRemove(localmute);
+ llinfos << "Unmuting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
+ }
+ else
+ {
+ // Flags were updated, the mute entry needs to be retransmitted to the server and re-added to the list.
+ mMutes.insert(localmute);
+ updateAdd(localmute);
+ llinfos << "Updating mute entry " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
+ }
+
// Must be after erase.
notifyObservers();
found = TRUE;
@@ -361,7 +433,7 @@ BOOL LLMuteList::loadFromFile(const LLString& filename)
buffer, " %d %254s %254[^|]| %u\n", &type, id_buffer, name_buffer,
&flags);
LLUUID id = LLUUID(id_buffer);
- LLMute mute(id, name_buffer, (LLMute::EType)type);
+ LLMute mute(id, name_buffer, (LLMute::EType)type, flags);
if (mute.mID.isNull()
|| mute.mType == LLMute::BY_NAME)
{
@@ -410,19 +482,27 @@ BOOL LLMuteList::saveToFile(const LLString& filename)
{
it->mID.toString(id_string);
const LLString& name = it->mName;
- fprintf(fp, "%d %s %s|\n", (S32)it->mType, id_string, name.c_str());
+ fprintf(fp, "%d %s %s|%u\n", (S32)it->mType, id_string, name.c_str(), it->mFlags);
}
fclose(fp);
return TRUE;
}
-BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name) const
+BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name, U32 flags) const
{
// don't need name or type for lookup
LLMute mute(id);
mute_set_t::const_iterator mute_it = mMutes.find(mute);
- if (mute_it != mMutes.end()) return TRUE;
+ if (mute_it != mMutes.end())
+ {
+ // If any of the flags the caller passed are set, this item isn't considered muted for this caller.
+ if(flags & mute_it->mFlags)
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
// empty names can't be legacy-muted
if (name.empty()) return FALSE;
diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h
index 7f9b1b3bf6..f035e4c2b1 100644
--- a/indra/newview/llmutelist.h
+++ b/indra/newview/llmutelist.h
@@ -23,8 +23,22 @@ public:
// Legacy mutes are BY_NAME and have null UUID.
enum EType { BY_NAME = 0, AGENT = 1, OBJECT = 2, GROUP = 3, COUNT = 4 };
- LLMute(const LLUUID& id, const LLString& name = "", EType type = BY_NAME)
- : mID(id), mName(name), mType(type) { }
+ // Bits in the mute flags. For backwards compatibility (since any mute list entries that were created before the flags existed
+ // will have a flags field of 0), some of the flags are "inverted".
+ // Note that it's possible, through flags, to completely disable an entry in the mute list. The code should detect this case
+ // and remove the mute list entry instead.
+ enum
+ {
+ flagTextChat = 0x00000001, // If set, don't mute user's text chat
+ flagVoiceChat = 0x00000002, // If set, don't mute user's voice chat
+ flagParticles = 0x00000004, // If set, don't mute user's particles
+ flagObjectSounds = 0x00000008, // If set, mute user's object sounds
+
+ flagAll = 0x0000000F // Mask of all currently defined flags
+ };
+
+ LLMute(const LLUUID& id, const LLString& name = "", EType type = BY_NAME, U32 flags = 0)
+ : mID(id), mName(name), mType(type),mFlags(flags) { }
// Returns name + suffix based on type
// For example: "James Tester (resident)"
@@ -39,6 +53,7 @@ public:
LLUUID mID; // agent or object id
LLString mName; // agent or object name
EType mType; // needed for UI display of existing mutes
+ U32 mFlags; // flags pertaining to this mute entry
};
class LLMuteList
@@ -50,14 +65,17 @@ public:
void addObserver(LLMuteListObserver* observer);
void removeObserver(LLMuteListObserver* observer);
- // Add either a normal or a BY_NAME mute.
- BOOL add(const LLMute& mute);
+ // Add either a normal or a BY_NAME mute, for any or all properties.
+ BOOL add(const LLMute& mute, U32 flags = 0);
- // Remove both normal and legacy mutes.
- BOOL remove(const LLMute& mute);
+ // Remove both normal and legacy mutes, for any or all properties.
+ BOOL remove(const LLMute& mute, U32 flags = 0);
// Name is required to test against legacy text-only mutes.
- BOOL isMuted(const LLUUID& id, const LLString& name = LLString::null) const;
+ BOOL isMuted(const LLUUID& id, const LLString& name = LLString::null, U32 flags = 0) const;
+
+ // Alternate (convenience) form for places we don't need to pass the name, but do need flags
+ BOOL isMuted(const LLUUID& id, U32 flags) const { return isMuted(id, LLString::null, flags); };
BOOL isLinden(const LLString& name) const;
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 4fde8988f5..754c096296 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -226,7 +226,7 @@ void LLNetMap::draw()
{
LLGLSNoTexture no_texture;
- LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+ LLLocalClipRect clip(getLocalRect());
glMatrixMode(GL_MODELVIEW);
diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp
index 8e36297c93..0847864be0 100644
--- a/indra/newview/lloverlaybar.cpp
+++ b/indra/newview/lloverlaybar.cpp
@@ -14,24 +14,26 @@
#include "lloverlaybar.h"
#include "audioengine.h"
-#include "llparcel.h"
-
#include "llagent.h"
#include "llbutton.h"
-#include "llviewercontrol.h"
+#include "llfocusmgr.h"
#include "llimview.h"
-#include "lltextbox.h"
-#include "llvoavatar.h"
#include "llmediaengine.h"
-#include "viewer.h"
+#include "llpanelaudiovolume.h"
+#include "llparcel.h"
+#include "lltextbox.h"
#include "llui.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
#include "llviewermenu.h" // handle_reset_view()
#include "llviewerparcelmgr.h"
-#include "llwebbrowserctrl.h"
#include "llvieweruictrlfactory.h"
-#include "llviewerimagelist.h"
#include "llviewerwindow.h"
-#include "llfocusmgr.h"
+#include "llvoiceclient.h"
+#include "llvoavatar.h"
+#include "llvoiceremotectrl.h"
+#include "llwebbrowserctrl.h"
+#include "viewer.h"
//
// Globals
@@ -47,38 +49,54 @@ extern S32 MENU_BAR_HEIGHT;
//static
-void* LLOverlayBar::createMediaRemote(void* userdata)
+void* LLOverlayBar::createMasterRemote(void* userdata)
{
-
- LLOverlayBar *self = (LLOverlayBar*)userdata;
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+ self->mMasterRemote = new LLMediaRemoteCtrl ( "master_volume",
+ "volume",
+ LLRect(),
+ "panel_master_volume.xml");
+ return self->mMasterRemote;
+}
-
+void* LLOverlayBar::createMediaRemote(void* userdata)
+{
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
self->mMediaRemote = new LLMediaRemoteCtrl ( "media_remote",
- "media",
- LLRect(),
- "panel_media_remote.xml");
+ "media",
+ LLRect(),
+ "panel_media_remote.xml");
return self->mMediaRemote;
}
-
-
void* LLOverlayBar::createMusicRemote(void* userdata)
{
-
LLOverlayBar *self = (LLOverlayBar*)userdata;
-
self->mMusicRemote = new LLMediaRemoteCtrl ( "music_remote",
- "music",
- LLRect(),
- "panel_music_remote.xml" );
+ "music",
+ LLRect(),
+ "panel_music_remote.xml" );
return self->mMusicRemote;
}
+void* LLOverlayBar::createVoiceRemote(void* userdata)
+{
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+ self->mVoiceRemote = new LLVoiceRemoteCtrl("voice_remote");
+ return self->mVoiceRemote;
+}
+
LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
-: LLPanel(name, rect, FALSE) // not bordered
+ : LLPanel(name, rect, FALSE), // not bordered
+ mMasterRemote(NULL),
+ mMusicRemote(NULL),
+ mMediaRemote(NULL),
+ mVoiceRemote(NULL),
+ mMediaState(STOPPED),
+ mMusicState(STOPPED)
{
setMouseOpaque(FALSE);
setIsChrome(TRUE);
@@ -86,8 +104,10 @@ LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
isBuilt = FALSE;
LLCallbackMap::map_t factory_map;
+ factory_map["master_volume"] = LLCallbackMap(LLOverlayBar::createMasterRemote, this);
factory_map["media_remote"] = LLCallbackMap(LLOverlayBar::createMediaRemote, this);
factory_map["music_remote"] = LLCallbackMap(LLOverlayBar::createMusicRemote, this);
+ factory_map["voice_remote"] = LLCallbackMap(LLOverlayBar::createVoiceRemote, this);
gUICtrlFactory->buildPanel(this, "panel_overlaybar.xml", &factory_map);
@@ -97,34 +117,17 @@ LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
childSetAction("Mouselook",onClickMouselook,this);
childSetAction("Stand Up",onClickStandUp,this);
- mMusicRemote->addObserver ( this );
-
- if ( gAudiop )
- {
- //HACK / NOT HACK
- //maintenance patch - bhear obsoletes this, do not merge (poppy)
- F32 audioLevelMusic = gSavedSettings.getF32 ( "AudioLevelMusic" );
- mMusicRemote->setVolume ( audioLevelMusic );
- gAudiop->setInternetStreamGain ( audioLevelMusic );
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
- };
-
mIsFocusRoot = TRUE;
-
- mMediaRemote->addObserver ( this );
- mMediaRemote->setVolume ( gSavedSettings.getF32 ( "MediaAudioVolume" ) );
-
isBuilt = true;
+ // make overlay bar conform to window size
+ setRect(rect);
layoutButtons();
}
LLOverlayBar::~LLOverlayBar()
{
// LLView destructor cleans up children
-
- mMusicRemote->remObserver ( this );
- mMediaRemote->remObserver ( this );
}
EWidgetType LLOverlayBar::getWidgetType() const
@@ -148,20 +151,28 @@ void LLOverlayBar::reshape(S32 width, S32 height, BOOL called_from_parent)
}
}
-
void LLOverlayBar::layoutButtons()
{
S32 width = mRect.getWidth();
- if (width > 800) width = 800;
+ if (width > 1024) width = 1024;
S32 count = getChildCount();
const S32 PAD = gSavedSettings.getS32("StatusBarPad");
- F32 segment_width = (F32)(width) / (F32)count;
+ const S32 num_media_controls = 3;
+ S32 media_remote_width = mMediaRemote ? mMediaRemote->getRect().getWidth() : 0;
+ S32 music_remote_width = mMusicRemote ? mMusicRemote->getRect().getWidth() : 0;
+ S32 voice_remote_width = mVoiceRemote ? mVoiceRemote->getRect().getWidth() : 0;
+ S32 master_remote_width = mMasterRemote ? mMasterRemote->getRect().getWidth() : 0;
- S32 btn_width = lltrunc(segment_width - PAD);
+ // total reserved width for all media remotes
+ const S32 ENDPAD = 20;
+ S32 remote_total_width = media_remote_width + PAD + music_remote_width + PAD + voice_remote_width + PAD + master_remote_width + ENDPAD;
- S32 remote_width = mMusicRemote->getRect().getWidth();
+ // calculate button widths
+ F32 segment_width = (F32)(width - remote_total_width) / (F32)(count - num_media_controls);
+
+ S32 btn_width = lltrunc(segment_width - PAD);
// Evenly space all views
LLRect r;
@@ -171,22 +182,47 @@ void LLOverlayBar::layoutButtons()
{
LLView *view = *child_iter;
r = view->getRect();
- r.mLeft = (width) - llround((i+1)*segment_width);
+ r.mLeft = (width) - llround(remote_total_width + (i-num_media_controls+1)*segment_width);
r.mRight = r.mLeft + btn_width;
view->setRect(r);
i++;
}
// Fix up remotes to have constant width because they can't shrink
- r = mMusicRemote->getRect();
- r.mRight = r.mLeft + remote_width;
- mMusicRemote->setRect(r);
-
- r = mMediaRemote->getRect();
- r.mLeft = mMusicRemote->getRect().mRight + PAD;
- r.mRight = r.mLeft + remote_width;
- mMediaRemote->setRect(r);
-
+ S32 right = mRect.getWidth() - remote_total_width - PAD;
+ if (mMediaRemote)
+ {
+ r = mMediaRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + media_remote_width;
+ r.mRight = right;
+ mMediaRemote->setRect(r);
+ }
+ if (mMusicRemote)
+ {
+ r = mMusicRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + music_remote_width;
+ r.mRight = right;
+ mMusicRemote->setRect(r);
+ }
+ if (mVoiceRemote)
+ {
+ r = mVoiceRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + voice_remote_width;
+ r.mRight = right;
+ mVoiceRemote->setRect(r);
+ }
+ if (mMasterRemote)
+ {
+ r = mMasterRemote->getRect();
+ r.mLeft = right + PAD;
+ right = r.mLeft + master_remote_width;
+ r.mRight = right;
+ mMasterRemote->setRect(r);
+ }
+
updateRect();
}
@@ -266,7 +302,7 @@ void LLOverlayBar::draw()
// Per-frame updates of visibility
void LLOverlayBar::refresh()
{
- BOOL im_received = gIMView->getIMReceived();
+ BOOL im_received = gIMMgr->getIMReceived();
childSetVisible("IM Received", im_received);
childSetEnabled("IM Received", im_received);
@@ -297,10 +333,10 @@ void LLOverlayBar::refresh()
}
- if ( gAudiop )
+ if ( mMusicRemote && gAudiop )
{
LLParcel* parcel = gParcelMgr->getAgentParcel();
- if (!parcel
+ if (!parcel
|| !parcel->getMusicURL()
|| !parcel->getMusicURL()[0]
|| !gSavedSettings.getBOOL("AudioStreamingMusic"))
@@ -316,50 +352,29 @@ void LLOverlayBar::refresh()
}
// if there is a url and a texture and media is enabled and available and media streaming is on... (phew!)
- if ( LLMediaEngine::getInstance () &&
- LLMediaEngine::getInstance ()->getUrl ().length () &&
- LLMediaEngine::getInstance ()->getImageUUID ().notNull () &&
- LLMediaEngine::getInstance ()->isEnabled () &&
- LLMediaEngine::getInstance ()->isAvailable () &&
- gSavedSettings.getBOOL ( "AudioStreamingVideo" ) )
+ if ( mMediaRemote )
{
- // display remote control
- mMediaRemote->setVisible ( TRUE );
- mMediaRemote->setEnabled ( TRUE );
-
- if ( LLMediaEngine::getInstance ()->getMediaRenderer () )
+ if (LLMediaEngine::getInstance () &&
+ LLMediaEngine::getInstance ()->getUrl ().length () &&
+ LLMediaEngine::getInstance ()->getImageUUID ().notNull () &&
+ LLMediaEngine::getInstance ()->isEnabled () &&
+ LLMediaEngine::getInstance ()->isAvailable () &&
+ gSavedSettings.getBOOL ( "AudioStreamingVideo" ) )
{
- if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPlaying () ||
- LLMediaEngine::getInstance ()->getMediaRenderer ()->isLooping () )
- {
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
- }
- else
- if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPaused () )
- {
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
- }
- else
- {
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
- };
- };
+ // display remote control
+ mMediaRemote->setVisible ( TRUE );
+ mMediaRemote->setEnabled ( TRUE );
+ }
+ else
+ {
+ mMediaRemote->setVisible ( FALSE );
+ mMediaRemote->setEnabled ( FALSE );
+ }
}
- else
+ if (mVoiceRemote)
{
- mMediaRemote->setVisible ( FALSE );
- mMediaRemote->setEnabled ( FALSE );
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
- };
-
- BOOL any_button = (childIsVisible("IM Received")
- || childIsVisible("Set Not Busy")
- || childIsVisible("Release Keys")
- || childIsVisible("Mouselook")
- || childIsVisible("Stand Up")
- || mMusicRemote->getVisible()
- || mMediaRemote->getVisible() );
-
+ mVoiceRemote->setVisible(LLVoiceClient::voiceEnabled());
+ }
// turn off the whole bar in mouselook
if (gAgent.cameraMouselook())
@@ -368,8 +383,8 @@ void LLOverlayBar::refresh()
}
else
{
- setVisible(any_button);
- };
+ setVisible(TRUE);
+ }
}
//-----------------------------------------------------------------------
@@ -379,7 +394,7 @@ void LLOverlayBar::refresh()
// static
void LLOverlayBar::onClickIMReceived(void*)
{
- gIMView->setFloaterOpen(TRUE);
+ gIMMgr->setFloaterOpen(TRUE);
}
@@ -415,134 +430,162 @@ void LLOverlayBar::onClickStandUp(void*)
}
////////////////////////////////////////////////////////////////////////////////
-//
-//
-void
-LLOverlayBar::
-onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
-{
- LLUICtrl* control = eventIn.getControl ();
- F32 value = eventIn.getValue ();
+// static media helpers
+// *TODO: Move this into an audio manager abstraction
- if ( control == mMusicRemote )
+//static
+void LLOverlayBar::mediaPlay(void*)
+{
+ if (!gOverlayBar)
{
- if (gAudiop)
- {
- gAudiop->setInternetStreamGain ( value );
- };
- gSavedSettings.setF32 ( "AudioLevelMusic", value );
+ return;
}
- else
- if ( control == mMediaRemote )
+ gOverlayBar->mMediaState = PLAYING; // desired state
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
{
- LLMediaEngine::getInstance ()->setVolume ( value );
- gSavedSettings.setF32 ( "MediaAudioVolume", value );
-
- };
+ LLString path("");
+ LLMediaEngine::getInstance()->convertImageAndLoadUrl( true, false, path );
+ }
}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-//
-void
-LLOverlayBar::
-onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+//static
+void LLOverlayBar::mediaPause(void*)
{
- LLUICtrl* control = eventIn.getControl ();
-
- if ( control == mMusicRemote )
+ if (!gOverlayBar)
{
- if ( gAudiop )
- {
- gAudiop->stopInternetStream ();
- };
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ return;
}
- else
- if ( control == mMediaRemote )
+ gOverlayBar->mMediaState = PAUSED; // desired state
+ LLMediaEngine::getInstance()->pause();
+}
+//static
+void LLOverlayBar::mediaStop(void*)
+{
+ if (!gOverlayBar)
{
- LLMediaEngine::getInstance ()->stop ();
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
- };
+ return;
+ }
+ gOverlayBar->mMediaState = STOPPED; // desired state
+ LLMediaEngine::getInstance()->stop();
}
-////////////////////////////////////////////////////////////////////////////////
-//
-//
-void LLOverlayBar::onPlayButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+//static
+void LLOverlayBar::musicPlay(void*)
{
- LLUICtrl* control = eventIn.getControl ();
-
- LLParcel* parcel = gParcelMgr->getAgentParcel();
- if ( control == mMusicRemote )
+ if (!gOverlayBar)
{
- if (gAudiop)
+ return;
+ }
+ gOverlayBar->mMusicState = PLAYING; // desired state
+ if (gAudiop)
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if ( parcel )
{
- if ( parcel )
+ // this doesn't work properly when crossing parcel boundaries - even when the
+ // stream is stopped, it doesn't return the right thing - commenting out for now.
+// if ( gAudiop->isInternetStreamPlaying() == 0 )
{
- // this doesn't work properly when crossing parcel boundaries - even when the
- // stream is stopped, it doesn't return the right thing - commenting out for now.
- //if ( gAudiop->isInternetStreamPlaying() == 0 )
- //{
- const char* music_url = parcel->getMusicURL();
-
- gAudiop->startInternetStream(music_url);
-
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
- //}
+ gAudiop->startInternetStream(parcel->getMusicURL());
}
- };
-
- // CP: this is the old way of doing things (click play each time on a parcel to start stream)
- //if (gAudiop)
- //{
- // if (gAudiop->isInternetStreamPlaying() > 0)
- // {
- // gAudiop->pauseInternetStream ( 0 );
- // }
- // else
- // {
- // if (parcel)
- // {
- // const char* music_url = parcel->getMusicURL();
- // gAudiop->startInternetStream(music_url);
- // }
- // }
- //};
- //mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
}
- else
- if ( control == mMediaRemote )
+}
+//static
+void LLOverlayBar::musicPause(void*)
+{
+ if (!gOverlayBar)
{
- LLParcel* parcel = gParcelMgr->getAgentParcel();
- if (parcel)
+ return;
+ }
+ gOverlayBar->mMusicState = PAUSED; // desired state
+ if (gAudiop)
+ {
+ gAudiop->pauseInternetStream(1);
+ }
+}
+//static
+void LLOverlayBar::musicStop(void*)
+{
+ if (!gOverlayBar)
+ {
+ return;
+ }
+ gOverlayBar->mMusicState = STOPPED; // desired state
+ if (gAudiop)
+ {
+ gAudiop->stopInternetStream();
+ }
+}
+
+//static
+void LLOverlayBar::enableMusicButtons(LLPanel* panel)
+{
+ BOOL play_enabled = FALSE;
+ BOOL play_visible = TRUE;
+ BOOL pause_visible = FALSE;
+ BOOL stop_enabled = FALSE;
+ if ( gAudiop && gOverlayBar && gSavedSettings.getBOOL("AudioStreamingMusic"))
+ {
+ play_enabled = TRUE;
+ S32 is_playing = gAudiop->isInternetStreamPlaying();
+ if (is_playing == 1)
{
- LLString path( "" );
- LLMediaEngine::getInstance ()->convertImageAndLoadUrl( true, false, path );
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
+ play_visible = FALSE;
+ pause_visible = TRUE;
+ stop_enabled = TRUE;
}
- };
+ else if (is_playing == 2)
+ {
+ play_visible = TRUE;
+ pause_visible = FALSE;
+ stop_enabled = TRUE;
+ }
+ }
+ panel->childSetEnabled("music_play", play_enabled);
+ panel->childSetEnabled("music_pause", play_enabled);
+ panel->childSetVisible("music_play", play_visible);
+ panel->childSetVisible("music_pause", pause_visible);
+ panel->childSetEnabled("music_stop", stop_enabled);
}
-////////////////////////////////////////////////////////////////////////////////
-//
-//
-void LLOverlayBar::onPauseButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+//static
+void LLOverlayBar::enableMediaButtons(LLPanel* panel)
{
- LLUICtrl* control = eventIn.getControl ();
+ // Media
+ BOOL play_enabled = FALSE;
+ BOOL play_visible = TRUE;
+ BOOL pause_visible = FALSE;
+ BOOL stop_enabled = FALSE;
- if ( control == mMusicRemote )
+ if ( LLMediaEngine::getInstance() && gOverlayBar && gSavedSettings.getBOOL("AudioStreamingVideo") )
{
- if (gAudiop)
+ play_enabled = TRUE;
+ if (LLMediaEngine::getInstance()->getMediaRenderer())
{
- gAudiop->pauseInternetStream ( 1 );
- };
- mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ if ( LLMediaEngine::getInstance()->getMediaRenderer()->isPlaying() ||
+ LLMediaEngine::getInstance()->getMediaRenderer()->isLooping() )
+ {
+ play_visible = FALSE;
+ pause_visible = TRUE;
+ stop_enabled = TRUE;
+ }
+ else if ( LLMediaEngine::getInstance()->getMediaRenderer()->isPaused() )
+ {
+ play_visible = TRUE;
+ pause_visible = FALSE;
+ stop_enabled = TRUE;
+ }
+ }
}
- else
- if ( control == mMediaRemote )
- {
- LLMediaEngine::getInstance ()->pause ();
- mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
- };
+ panel->childSetEnabled("media_play", play_enabled);
+ panel->childSetEnabled("media_pause", play_enabled);
+ panel->childSetVisible("media_play", play_visible);
+ panel->childSetVisible("media_pause", pause_visible);
+ panel->childSetEnabled("media_stop", stop_enabled);
+}
+
+void LLOverlayBar::toggleAudioVolumeFloater(void* user_data)
+{
+ LLFloaterAudioVolume::toggleInstance(LLSD());
}
diff --git a/indra/newview/lloverlaybar.h b/indra/newview/lloverlaybar.h
index 78f544df57..d7fff6ba1e 100644
--- a/indra/newview/lloverlaybar.h
+++ b/indra/newview/lloverlaybar.h
@@ -25,11 +25,10 @@ class LLUUID;
class LLFrameTimer;
class LLStatGraph;
class LLSlider;
-class LLVolumeSliderCtrl;
+class LLVoiceRemoteCtrl;
class LLOverlayBar
-: public LLPanel,
- public LLMediaRemoteCtrlObserver
+: public LLPanel
{
public:
LLOverlayBar(const std::string& name, const LLRect& rect );
@@ -38,14 +37,16 @@ public:
virtual EWidgetType getWidgetType() const;
virtual LLString getWidgetTag() const;
- virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
-
- void refresh();
+ /*virtual*/ void refresh();
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
void layoutButtons();
- /*virtual*/ void draw();
-
+ // helpers for returning desired state
+ BOOL mediaPlaying() { return mMediaState == PLAYING; }
+ BOOL musicPlaying() { return mMusicState == PLAYING; }
+
static void onClickIMReceived(void* data);
static void onClickSetNotBusy(void* data);
static void onClickReleaseKeys(void* data);
@@ -53,23 +54,35 @@ public:
static void onClickStandUp(void* data);
static void onClickResetView(void* data);
- // observer overrides
- void onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
- void onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
- void onPlayButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
- void onPauseButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
-
- LLMediaRemoteCtrl* getMusicRemoteControl () { return mMusicRemote; };
+ //static media helper functions
+ static void mediaPlay(void*);
+ static void mediaPause(void*);
+ static void mediaStop(void*);
+
+ static void musicPlay(void*);
+ static void musicPause(void*);
+ static void musicStop(void*);
-protected:
+ static void toggleAudioVolumeFloater(void*);
+
+ static void enableMediaButtons(LLPanel* panel);
+ static void enableMusicButtons(LLPanel* panel);
+protected:
+ static void* createMasterRemote(void* userdata);
static void* createMusicRemote(void* userdata);
static void* createMediaRemote(void* userdata);
+ static void* createVoiceRemote(void* userdata);
protected:
+ LLMediaRemoteCtrl* mMasterRemote;
LLMediaRemoteCtrl* mMusicRemote;
LLMediaRemoteCtrl* mMediaRemote;
+ LLVoiceRemoteCtrl* mVoiceRemote;
BOOL isBuilt;
+ enum { STOPPED=0, PLAYING=1, PAUSED=2 };
+ BOOL mMediaState;
+ BOOL mMusicState;
};
extern LLOverlayBar* gOverlayBar;
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index 2567684fa9..ab2e30014d 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -1398,6 +1398,7 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
setOnlineStatus(online_status);
BOOL own_avatar = (mAvatarID == gAgent.getID() );
+ BOOL avatar_is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL;
mPanelSecondLife->enableControls(own_avatar && mAllowEdit);
mPanelWeb->enableControls(own_avatar && mAllowEdit);
@@ -1515,7 +1516,7 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
childSetToolTip("Show on Map",childGetValue("ShowOnMapFriendOnline").asString());
}
childSetVisible("Add Friend...", true);
- childSetEnabled("Add Friend...", true);
+ childSetEnabled("Add Friend...", !avatar_is_friend);
childSetVisible("Pay...",TRUE);
childSetEnabled("Pay...",FALSE);
}
@@ -1589,12 +1590,12 @@ void LLPanelAvatar::resetGroupList()
void LLPanelAvatar::onClickIM(void* userdata)
{
LLPanelAvatar* self = (LLPanelAvatar*) userdata;
- gIMView->setFloaterOpen(TRUE);
+ gIMMgr->setFloaterOpen(TRUE);
std::string name;
LLNameEditor* nameedit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
if (nameedit) name = nameedit->getText();
- gIMView->addSession(name, IM_NOTHING_SPECIAL, self->mAvatarID);
+ gIMMgr->addSession(name, IM_NOTHING_SPECIAL, self->mAvatarID);
}
@@ -1624,7 +1625,7 @@ void LLPanelAvatar::onClickAddFriend(void* userdata)
LLNameEditor* name_edit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
if (name_edit)
{
- LLFloaterFriends::requestFriendshipDialog(self->getAvatarID(),
+ LLPanelFriends::requestFriendshipDialog(self->getAvatarID(),
name_edit->getText());
}
}
diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp
index 23f7a4aba6..7a288a1730 100644
--- a/indra/newview/llpanelgroup.cpp
+++ b/indra/newview/llpanelgroup.cpp
@@ -105,16 +105,7 @@ void LLPanelGroupTab::handleClickHelp()
LLAlertDialog* dialogp = gViewerWindow->alertXml("GenericAlert", args);
if (dialogp)
{
- LLView* viewp = this;
- LLFloater* root_floater = NULL;
- while(viewp)
- {
- if(viewp->getWidgetType() == WIDGET_TYPE_FLOATER)
- {
- root_floater = (LLFloater*)viewp;
- }
- viewp = viewp->getParent();
- }
+ LLFloater* root_floater = gFloaterView->getParentFloater(this);;
if (root_floater)
{
root_floater->addDependentFloater(dialogp);
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index c3d4551032..e32a64eb8e 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -24,6 +24,7 @@
#include "llcombobox.h"
#include "llviewercontrol.h"
#include "llfloaterabout.h"
+#include "llfloatertest.h"
#include "llfloaterpreference.h"
#include "llfocusmgr.h"
#include "lllineeditor.h"
@@ -404,23 +405,30 @@ BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
return TRUE;
}
- #if LL_LIBXUL_ENABLED
+ if (('T' == key) && (MASK_CONTROL == mask))
+ {
+ new LLFloaterSimple("floater_test.xml");
+ return TRUE;
+ }
+
+#if LL_LIBXUL_ENABLED
if ( KEY_F1 == key )
{
llinfos << "Spawning HTML help window" << llendl;
gViewerHtmlHelp.show();
return TRUE;
- };
- #if ! LL_RELEASE_FOR_DOWNLOAD
- if ( KEY_F2 == key )
- {
- llinfos << "Spawning floater TOS window" << llendl;
- LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,"");
- tos_dialog->startModal();
- return TRUE;
- };
- #endif
- #endif
+ }
+
+# if !LL_RELEASE_FOR_DOWNLOAD
+ if ( KEY_F2 == key )
+ {
+ llinfos << "Spawning floater TOS window" << llendl;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,"");
+ tos_dialog->startModal();
+ return TRUE;
+ }
+# endif
+#endif
if (!called_from_parent)
{
diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp
index da7fc16cef..d0061e281e 100644
--- a/indra/newview/llpanelpermissions.cpp
+++ b/indra/newview/llpanelpermissions.cpp
@@ -787,14 +787,24 @@ void LLPanelPermissions::onClickOwner(void *data)
void LLPanelPermissions::onClickGroup(void* data)
{
+ LLPanelPermissions* panelp = (LLPanelPermissions*)data;
LLUUID owner_id;
LLString name;
BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, name);
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+
if(owners_identical && (owner_id == gAgent.getID()))
{
- LLFloaterGroups* fg;
- fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
- fg->setOkCallback( cbGroupID, data );
+ LLFloaterGroupPicker* fg;
+ fg = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID()));
+ fg->setSelectCallback( cbGroupID, data );
+
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, fg);
+ fg->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(fg);
+ }
}
}
diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp
index c4f958420b..c1885ad898 100644
--- a/indra/newview/llpreview.cpp
+++ b/indra/newview/llpreview.cpp
@@ -42,7 +42,8 @@ LLPreview::LLPreview(const std::string& name) :
mForceClose(FALSE),
mUserResized(FALSE),
mCloseAfterSave(FALSE),
- mAssetStatus(PREVIEW_ASSET_UNLOADED)
+ mAssetStatus(PREVIEW_ASSET_UNLOADED),
+ mItem(NULL)
{
// don't add to instance list, since ItemID is null
mAuxItem = new LLInventoryItem; // (LLPointer is auto-deleted)
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index 2d4a6d11d9..0c11226710 100644
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -1074,9 +1074,6 @@ LLPreviewLSL::LLPreviewLSL(const std::string& name, const LLRect& rect,
gUICtrlFactory->buildFloater(this,"floater_script_preview.xml", &factory_map);
- moveResizeHandleToFront();
-
-
const LLInventoryItem* item = getItem();
childSetCommitCallback("desc", LLPreview::onText, this);
@@ -1600,8 +1597,6 @@ LLLiveLSLEditor::LLLiveLSLEditor(const std::string& name,
LLCallbackMap::map_t factory_map;
factory_map["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this);
- moveResizeHandleToFront();
-
gUICtrlFactory->buildFloater(this,"floater_live_lsleditor.xml", &factory_map);
diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp
index 7df0e48762..22b47eb8ca 100644
--- a/indra/newview/llpreviewsound.cpp
+++ b/indra/newview/llpreviewsound.cpp
@@ -8,15 +8,16 @@
#include "llviewerprecompiledheaders.h"
-#include "llpreviewsound.h"
+#include "audioengine.h"
+#include "llagent.h" // gAgent
#include "llbutton.h"
-#include "llresmgr.h"
#include "llinventory.h"
#include "llinventoryview.h"
-#include "audioengine.h"
-#include "llviewermessage.h" // send_guid_sound_trigger
-#include "llagent.h" // gAgent
#include "lllineeditor.h"
+#include "llpreviewsound.h"
+#include "llresmgr.h"
+#include "llviewercontrol.h"
+#include "llviewermessage.h" // send_guid_sound_trigger
#include "llvieweruictrlfactory.h"
extern LLAudioEngine* gAudiop;
@@ -82,7 +83,7 @@ void LLPreviewSound::auditionSound( void *userdata )
if(item && gAudiop)
{
LLVector3d lpos_global = gAgent.getPositionGlobal();
-
- gAudiop->triggerSound(item->getAssetUUID(), gAgent.getID(), SOUND_GAIN, lpos_global);
+ F32 volume = SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(item->getAssetUUID(), gAgent.getID(), volume, lpos_global);
}
}
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
index 974ee27476..2462bb7bdf 100644
--- a/indra/newview/llpreviewtexture.cpp
+++ b/indra/newview/llpreviewtexture.cpp
@@ -415,7 +415,7 @@ void LLPreviewTexture::updateAspectRatio()
S32 old_left = mRect.mLeft;
if (getHost())
{
- getHost()->growToFit(this, view_width, view_height);
+ getHost()->growToFit(view_width, view_height);
}
else
{
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 117f020793..20761ded11 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -65,6 +65,8 @@
#include "lleventnotifier.h"
#include "llface.h"
#include "llfeaturemanager.h"
+#include "llfirstuse.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterchat.h"
#include "llfloatergesture.h"
#include "llfloaterland.h"
@@ -133,6 +135,7 @@
#include "llfasttimerview.h"
#include "llfloatermap.h"
#include "llweb.h"
+#include "llvoiceclient.h"
#if LL_LIBXUL_ENABLED
#include "llmozlib.h"
@@ -1402,6 +1405,9 @@ BOOL idle_startup()
gAutoLogin = FALSE;
show_connect_box = TRUE;
}
+
+ // Pass the user information to the voice chat server interface.
+ gVoiceClient->userAuthorized(firstname, lastname, gAgentID);
}
else
{
@@ -1700,14 +1706,7 @@ BOOL idle_startup()
{
if (gViewerWindow)
{
- if (gSavedSettings.getBOOL("MuteAudio"))
- {
- LLMediaEngine::updateClass( 0.0f );
- }
- else
- {
- LLMediaEngine::updateClass( gSavedSettings.getF32( "MediaAudioVolume" ) );
- }
+ audio_update_volume(true);
}
#if LL_QUICKTIME_ENABLED // windows only right now but will be ported to mac
@@ -2287,6 +2286,9 @@ BOOL idle_startup()
// On first start, ask user for gender
dialog_choose_gender_first_start();
+ // setup voice
+ LLFirstUse::useVoice();
+
// Start automatic replay if the flag is set.
if (gSavedSettings.getBOOL("StatsAutoRun"))
{
@@ -2314,7 +2316,7 @@ BOOL idle_startup()
}
}
}
-
+
// Clean up the userauth stuff.
if (gUserAuthp)
{
@@ -2323,13 +2325,9 @@ BOOL idle_startup()
}
gStartupState++;
- //RN: unmute audio now that we are entering world
- //JC: But only if the user wants audio working.
- if (gAudiop)
- {
- BOOL mute = gSavedSettings.getBOOL("MuteAudio");
- gAudiop->setMuted(mute);
- }
+
+ // Unmute audio if desired and setup volumes
+ audio_update_volume();
// reset keyboard focus to sane state of pointing at world
gFocusMgr.setKeyboardFocus(NULL, NULL);
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index bc27d9af9f..3df559dee5 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -50,11 +50,14 @@
#include "llviewerparcelmgr.h"
#include "llviewerthrottle.h"
#include "llvieweruictrlfactory.h"
+#include "llvoiceclient.h" // for gVoiceClient
#include "lltoolmgr.h"
#include "llfocusmgr.h"
#include "viewer.h"
+//#include "llfirstuse.h"
+
//
// Globals
//
@@ -76,6 +79,18 @@ const F32 ICON_FLASH_FREQUENCY = 2.f;
const S32 GRAPHIC_FUDGE = 4;
const S32 TEXT_HEIGHT = 18;
+static void onClickParcelInfo(void*);
+static void onClickBalance(void*);
+static void onClickBuyCurrency(void*);
+static void onClickHealth(void*);
+static void onClickFly(void*);
+static void onClickPush(void*);
+static void onClickVoice(void*);
+static void onClickBuild(void*);
+static void onClickScripts(void*);
+static void onClickBuyLand(void*);
+static void onClickScriptDebug(void*);
+
std::vector<std::string> LLStatusBar::sDays;
std::vector<std::string> LLStatusBar::sMonths;
const U32 LLStatusBar::MAX_DATE_STRING_LENGTH = 2000;
@@ -106,15 +121,6 @@ LLStatusBar::LLStatusBar(const std::string& name, const LLRect& rect)
// build date necessary data (must do after panel built)
setupDate();
- mBtnScriptOut = LLUICtrlFactory::getButtonByName( this, "scriptout" );
- mBtnHealth = LLUICtrlFactory::getButtonByName( this, "health" );
- mBtnFly = LLUICtrlFactory::getButtonByName( this, "fly" );
- mBtnBuild = LLUICtrlFactory::getButtonByName( this, "build" );
- mBtnScripts = LLUICtrlFactory::getButtonByName( this, "scripts" );
- mBtnPush = LLUICtrlFactory::getButtonByName( this, "restrictpush" );
- mBtnBuyLand = LLUICtrlFactory::getButtonByName( this, "buyland" );
- mBtnBuyCurrency = LLUICtrlFactory::getButtonByName( this, "buycurrency" );
-
mTextParcelName = LLUICtrlFactory::getTextBoxByName( this, "ParcelNameText" );
mTextBalance = LLUICtrlFactory::getTextBoxByName( this, "BalanceText" );
@@ -176,6 +182,7 @@ BOOL LLStatusBar::postBuild()
childSetAction("build", onClickBuild, this );
childSetAction("scripts", onClickScripts, this );
childSetAction("restrictpush", onClickPush, this );
+ childSetAction("status_voice", onClickVoice, this );
childSetActionTextbox("ParcelNameText", onClickParcelInfo );
childSetActionTextbox("BalanceText", onClickBalance );
@@ -285,13 +292,13 @@ void LLStatusBar::refresh()
{
childGetRect( "scriptout", buttonRect );
r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnScriptOut->setRect(r);
- mBtnScriptOut->setVisible(TRUE);
+ childSetRect("scriptout",r);
+ childSetVisible("scriptout", true);
x += buttonRect.getWidth();
}
else
{
- mBtnScriptOut->setVisible(FALSE);
+ childSetVisible("scriptout", false);
}
if ((region && region->getAllowDamage()) ||
@@ -300,19 +307,19 @@ void LLStatusBar::refresh()
// set visibility based on flashing
if( mHealthTimer->hasExpired() )
{
- mBtnHealth->setVisible( TRUE );
+ childSetVisible("health", true);
}
else
{
BOOL flash = S32(mHealthTimer->getElapsedSeconds() * ICON_FLASH_FREQUENCY) & 1;
- mBtnHealth->setVisible( flash );
+ childSetVisible("health", flash);
}
mTextHealth->setVisible(TRUE);
// Health
childGetRect( "health", buttonRect );
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnHealth->setRect(r);
+ childSetRect("health", r);
x += buttonRect.getWidth();
const S32 health_width = S32( LLFontGL::sSansSerifSmall->getWidth("100%") );
@@ -323,7 +330,7 @@ void LLStatusBar::refresh()
else
{
// invisible if region doesn't allow damage
- mBtnHealth->setVisible(FALSE);
+ childSetVisible("health", false);
mTextHealth->setVisible(FALSE);
}
@@ -332,24 +339,24 @@ void LLStatusBar::refresh()
{
// No Fly Zone
childGetRect( "fly", buttonRect );
- mBtnFly->setVisible(TRUE);
+ childSetVisible( "fly", true );
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnFly->setRect(r);
+ childSetRect( "fly", r );
x += buttonRect.getWidth();
}
else
{
- mBtnFly->setVisible(FALSE);
+ childSetVisible("fly", false);
}
BOOL no_build = parcel && !parcel->getAllowModify();
- mBtnBuild->setVisible( no_build );
+ childSetVisible("build", no_build);
if (no_build)
{
childGetRect( "build", buttonRect );
// No Build Zone
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnBuild->setRect(r);
+ childSetRect( "build", r );
x += buttonRect.getWidth();
}
@@ -361,37 +368,46 @@ void LLStatusBar::refresh()
{
no_scripts = TRUE;
}
- mBtnScripts->setVisible( no_scripts );
+ childSetVisible("scripts", no_scripts);
if (no_scripts)
{
// No scripts
childGetRect( "scripts", buttonRect );
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnScripts->setRect(r);
+ childSetRect( "scripts", r );
x += buttonRect.getWidth();
}
BOOL no_region_push = (region && region->getRestrictPushObject());
BOOL no_push = no_region_push || (parcel && parcel->getRestrictPushObject());
- mBtnPush->setVisible( no_push );
+ childSetVisible("restrictpush", no_push);
if (no_push)
{
childGetRect( "restrictpush", buttonRect );
- // No Push Zone
r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnPush->setRect(r);
+ childSetRect( "restrictpush", r );
+ x += buttonRect.getWidth();
+ }
+
+ BOOL have_voice = gVoiceClient->getAreaVoiceDisabled() ? FALSE : TRUE;
+ childSetVisible("status_voice", have_voice);
+ if (have_voice)
+ {
+ childGetRect( "status_voice", buttonRect );
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ childSetRect( "status_voice", r );
x += buttonRect.getWidth();
}
BOOL canBuyLand = parcel
&& !parcel->isPublic()
&& gParcelMgr->canAgentBuyParcel(parcel, false);
- mBtnBuyLand->setVisible(canBuyLand);
+ childSetVisible("buyland", canBuyLand);
if (canBuyLand)
{
childGetRect( "buyland", buttonRect );
r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
- mBtnBuyLand->setRect(r);
+ childSetRect( "buyland", r );
x += buttonRect.getWidth();
}
@@ -460,7 +476,7 @@ void LLStatusBar::setVisibleForMouselook(bool visible)
mTextTime->setVisible(visible);
mSGBandwidth->setVisible(visible);
mSGPacketLoss->setVisible(visible);
- mBtnBuyCurrency->setVisible(visible);
+ childSetVisible("buycurrency", visible);
setBackgroundVisible(visible);
}
@@ -569,58 +585,55 @@ S32 LLStatusBar::getSquareMetersLeft() const
return mSquareMetersCredit - mSquareMetersCommitted;
}
-// static
-void LLStatusBar::onClickParcelInfo(void* data)
+static void onClickParcelInfo(void* data)
{
gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
LLFloaterLand::show();
}
-// static
-void LLStatusBar::onClickBalance(void* data)
+static void onClickBalance(void* data)
{
LLFloaterBuyCurrency::buyCurrency();
}
-// static
-void LLStatusBar::onClickBuyCurrency(void* data)
+static void onClickBuyCurrency(void* data)
{
LLFloaterBuyCurrency::buyCurrency();
}
-// static
-void LLStatusBar::onClickHealth(void* )
+static void onClickHealth(void* )
{
LLNotifyBox::showXml("NotSafe");
}
-// static
-void LLStatusBar::onClickScriptDebug(void*)
+static void onClickScriptDebug(void*)
{
LLFloaterScriptDebug::show(LLUUID::null);
}
-// static
-void LLStatusBar::onClickFly(void* )
+static void onClickFly(void* )
{
LLNotifyBox::showXml("NoFly");
}
-// static
-void LLStatusBar::onClickPush(void* )
+static void onClickPush(void* )
{
LLNotifyBox::showXml("PushRestricted");
}
-// static
-void LLStatusBar::onClickBuild(void*)
+static void onClickVoice(void* )
+{
+ LLNotifyBox::showXml("VoiceAvailablity");
+ //LLFirstUse::useVoice();
+}
+
+static void onClickBuild(void*)
{
LLNotifyBox::showXml("NoBuild");
}
-// static
-void LLStatusBar::onClickScripts(void*)
+static void onClickScripts(void*)
{
LLViewerRegion* region = gAgent.getRegion();
if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
@@ -637,8 +650,7 @@ void LLStatusBar::onClickScripts(void*)
}
}
-// static
-void LLStatusBar::onClickBuyLand(void*)
+static void onClickBuyLand(void*)
{
gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
gParcelMgr->startBuyLand();
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index 94f758f054..2bbf2bab2d 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -61,19 +61,7 @@ public:
S32 getSquareMetersCommitted() const;
S32 getSquareMetersLeft() const;
-protected:
- static void onClickParcelInfo(void*);
- static void onClickBalance(void*);
- static void onClickBuyCurrency(void*);
- static void onClickRegionInfo(void*);
- static void onClickHealth(void*);
- static void onClickFly(void*);
- static void onClickPush(void*);
- static void onClickBuild(void*);
- static void onClickScripts(void*);
- static void onClickBuyLand(void*);
- static void onClickScriptDebug(void*);
-
+protected:
// simple method to setup the part that holds the date
void setupDate();
@@ -82,15 +70,6 @@ protected:
LLTextBox *mTextHealth;
LLTextBox *mTextTime;
- LLButton *mBtnScriptOut;
- LLButton *mBtnHealth;
- LLButton *mBtnFly;
- LLButton *mBtnBuild;
- LLButton *mBtnScripts;
- LLButton *mBtnPush;
- LLButton *mBtnBuyLand;
-
-
LLTextBox* mTextParcelName;
LLStatGraph *mSGBandwidth;
diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp
index e6cfefa62d..879e99973d 100644
--- a/indra/newview/lltoolbar.cpp
+++ b/indra/newview/lltoolbar.cpp
@@ -26,6 +26,7 @@
#include "llvoavatar.h"
#include "lltooldraganddrop.h"
#include "llinventoryview.h"
+#include "llfloaterchatterbox.h"
#include "llfloaterfriends.h"
#include "llfloatersnapshot.h"
#include "lltoolmgr.h"
@@ -94,15 +95,12 @@ LLToolBar::LLToolBar(const std::string& name, const LLRect& r)
BOOL LLToolBar::postBuild()
{
- childSetAction("im_btn", onClickIM, this);
- childSetControlName("im_btn", "ShowIM");
+ childSetAction("communicate_btn", onClickCommunicate, this);
+ childSetControlName("communicate_btn", "ShowCommunicate");
childSetAction("chat_btn", onClickChat, this);
childSetControlName("chat_btn", "ChatVisible");
- childSetAction("friends_btn", onClickFriends, this);
- childSetControlName("friends_btn", "ShowFriends");
-
childSetAction("appearance_btn", onClickAppearance, this);
childSetControlName("appearance_btn", "");
@@ -313,24 +311,9 @@ void LLToolBar::refresh()
// static
-void LLToolBar::onClickIM(void* user_data)
+void LLToolBar::onClickCommunicate(void* user_data)
{
- if(gIMView->getFloaterOpen())
- {
- // this is if we want Ctrl-T to be simply a toggle
- // gIMView->setFloaterOpen( FALSE );
- // three-state behavior follows
- if(gFocusMgr.childHasKeyboardFocus(gIMView->getFloater()))
- {
- gIMView->setFloaterOpen( FALSE );
- } else {
- gIMView->getFloater()->setFocus( TRUE );
- }
- }
- else
- {
- gIMView->setFloaterOpen( TRUE );
- }
+ LLFloaterChatterBox::toggleInstance(LLSD());
}
@@ -340,14 +323,6 @@ void LLToolBar::onClickChat(void* user_data)
handle_chat(NULL);
}
-
-// static
-void LLToolBar::onClickFriends(void*)
-{
- LLFloaterFriends::toggle();
-}
-
-
// static
void LLToolBar::onClickAppearance(void*)
{
diff --git a/indra/newview/lltoolbar.h b/indra/newview/lltoolbar.h
index f9eee1d4fb..b6e6a073e4 100644
--- a/indra/newview/lltoolbar.h
+++ b/indra/newview/lltoolbar.h
@@ -47,9 +47,8 @@ public:
void refresh();
// callbacks
- static void onClickIM(void*);
+ static void onClickCommunicate(void*);
static void onClickChat(void* data);
- static void onClickFriends(void* data);
static void onClickAppearance(void* data);
static void onClickClothing(void* data);
static void onClickFly(void*);
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index 930d6fa5f2..72a8cefdbd 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1219,7 +1219,6 @@ BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,
return FALSE;
}
}
-std::cout << "ASSET ID: " << new_item->getAssetUUID() << "\n";
hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
}
else if(!item->getPermissions().allowOperationBy(PERM_TRANSFER,
@@ -1233,7 +1232,6 @@ std::cout << "ASSET ID: " << new_item->getAssetUUID() << "\n";
// *FIX: may want to make sure agent can paint hit_obj.
// make sure the object has the texture in it's inventory.
-std::cout << "ASSET ID: " << new_item->getAssetUUID() << "\n";
hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
}
return TRUE;
diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h
index 2203df4287..c93b62c75f 100644
--- a/indra/newview/llviewercontrol.h
+++ b/indra/newview/llviewercontrol.h
@@ -34,9 +34,11 @@ protected:
LLTextEditor* mComment;
};
+// These functions found in llcontroldef.cpp *TODO: clean this up!
//setting variables are declared in this function
void declare_settings();
void fixup_settings();
+void settings_setup_listeners();
// saved at end of session
extern LLControlGroup gSavedSettings;
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index bc0be1b5a2..abcdb8cdbd 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -59,6 +59,8 @@
#include "llfloater.h"
#include "llfloaterabout.h"
#include "llfloaterbuycurrency.h"
+#include "llfloateractivespeakers.h"
+#include "llfloateranimpreview.h"
#include "llfloateravatarinfo.h"
#include "llfloateravatartextures.h"
#include "llfloaterbuildoptions.h"
@@ -71,6 +73,7 @@
#include "llfloatercustomize.h"
#include "llfloaterdirectory.h"
#include "llfloatereditui.h"
+#include "llfloaterchatterbox.h"
#include "llfloaterfriends.h"
#include "llfloatergesture.h"
#include "llfloatergodtools.h"
@@ -2454,7 +2457,7 @@ void set_god_level(U8 god_level)
U8 old_god_level = gAgent.getGodLevel();
gAgent.setGodLevel( god_level );
show_debug_menus();
- gIMView->refresh();
+ gIMMgr->refresh();
gParcelMgr->notifyObservers();
// Some classifieds change visibility on god mode
@@ -2657,7 +2660,7 @@ void request_friendship(const LLUUID& dest_id)
}
if (!fullname.empty())
{
- LLFloaterFriends::requestFriendship(dest_id, fullname);
+ LLPanelFriends::requestFriendship(dest_id, fullname);
LLNotifyBox::showXml("OfferedFriendship", args);
}
else
@@ -5355,7 +5358,7 @@ class LLShowFloater : public view_listener_t
}
else if (floater_name == "friends")
{
- LLFloaterFriends::toggle(NULL);
+ LLFloaterMyFriends::toggleInstance(0);
}
else if (floater_name == "preferences")
{
@@ -5367,11 +5370,11 @@ class LLShowFloater : public view_listener_t
}
else if (floater_name == "chat history")
{
- LLFloaterChat::toggle(NULL);
+ LLFloaterChat::toggleInstance(LLSD());
}
else if (floater_name == "im")
{
- LLToolBar::onClickIM(NULL);
+ LLFloaterChatterBox::toggleInstance(LLSD());
}
else if (floater_name == "inventory")
{
@@ -5480,6 +5483,10 @@ class LLShowFloater : public view_listener_t
{
LLFloaterAbout::show(NULL);
}
+ else if (floater_name == "active speakers")
+ {
+ LLFloaterActiveSpeakers::toggleInstance(LLSD());
+ }
return true;
}
};
@@ -5493,7 +5500,7 @@ class LLFloaterVisible : public view_listener_t
bool new_value = false;
if (floater_name == "friends")
{
- new_value = LLFloaterFriends::visible(NULL);
+ new_value = LLFloaterMyFriends::instanceVisible(0);
}
else if (floater_name == "toolbar")
{
@@ -5505,7 +5512,7 @@ class LLFloaterVisible : public view_listener_t
}
else if (floater_name == "im")
{
- new_value = gIMView && gIMView->mTalkFloater && gIMView->mTalkFloater->getVisible();
+ new_value = LLFloaterMyFriends::instanceVisible(0);
}
else if (floater_name == "mute list")
{
@@ -5523,6 +5530,10 @@ class LLFloaterVisible : public view_listener_t
{
new_value = gDebugView->mStatViewp->getVisible();
}
+ else if (floater_name == "active speakers")
+ {
+ new_value = LLFloaterActiveSpeakers::instanceVisible(LLSD());
+ }
gMenuHolder->findControl(control_name)->setValue(new_value);
return true;
}
@@ -5639,19 +5650,7 @@ class LLShowAgentGroups : public view_listener_t
{
bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
{
- LLUUID agent_id;
- if (userdata.asString() == "agent")
- {
- agent_id = gAgent.getID();
- }
- else
- {
- agent_id = userdata.asUUID();
- }
- if(agent_id.notNull())
- {
- LLFloaterGroups::show(agent_id, LLFloaterGroups::AGENT_GROUPS);
- }
+ LLFloaterMyFriends::toggleInstance(1);
return true;
}
};
@@ -6151,10 +6150,10 @@ class LLAvatarSendIM : public view_listener_t
name.append( last->getString() );
}
- gIMView->setFloaterOpen(TRUE);
+ gIMMgr->setFloaterOpen(TRUE);
//EInstantMessage type = have_agent_callingcard(gLastHitObjectID)
// ? IM_SESSION_ADD : IM_SESSION_CARDLESS_START;
- gIMView->addSession(name,
+ gIMMgr->addSession(name,
IM_NOTHING_SPECIAL,
avatar->getID());
}
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 6a9b5a8031..7cf9ca9568 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -51,6 +51,7 @@
#include "llviewercontrol.h"
#include "lldrawpool.h"
#include "llfirstuse.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterbuycurrency.h"
#include "llfloaterbuyland.h"
#include "llfloaterchat.h"
@@ -1317,7 +1318,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
time_t timestamp = (time_t)t;
BOOL is_busy = gAgent.getBusy();
- BOOL is_muted = gMuteListp->isMuted(from_id, name);
+ BOOL is_muted = gMuteListp->isMuted(from_id, name, LLMute::flagTextChat);
BOOL is_linden = gMuteListp->isLinden(name);
BOOL is_owned_by_me = FALSE;
@@ -1368,7 +1369,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
{
// return a standard "busy" message, but only do it to online IM
// (i.e. not other auto responses and not store-and-forward IM)
- if (!gIMView->hasSession(session_id))
+ if (!gIMMgr->hasSession(session_id))
{
// if there is not a panel for this conversation (i.e. it is a new IM conversation
// initiated by the other party) then...
@@ -1393,14 +1394,10 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
snprintf(buffer, sizeof(buffer), "%s%s%s", name, separator_string, (message+message_offset)); /* Flawfinder: ignore */
- if(from_id == gAgentID)
- {
- from_id = LLUUID::null;
- }
llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
// add to IM panel, but do not bother the user
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name,
@@ -1450,15 +1447,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
formatted_time(timestamp, time_buf));
}
snprintf(buffer, sizeof(buffer), "%s%s%s%s", name, separator_string, saved,(message+message_offset)); /* Flawfinder: ignore */
- if(from_id == gAgentID)
- {
- from_id = LLUUID::null;
- }
+
llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
if (!is_muted || is_linden)
{
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name,
@@ -1489,14 +1483,14 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
case IM_TYPING_START:
{
LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
- gIMView->processIMTypingStart(im_info);
+ gIMMgr->processIMTypingStart(im_info);
}
break;
case IM_TYPING_STOP:
{
LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
- gIMView->processIMTypingStop(im_info);
+ gIMMgr->processIMTypingStop(im_info);
}
break;
@@ -1729,12 +1723,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
return;
}
- // System messages, specifically "Foo Bar has left this session"
- // are not shown unless you actually have that session open.
- // Band-aid. JC
- if (offline == IM_ONLINE
- && chat.mFromName == SYSTEM_FROM
- && !gIMView->hasSession(session_id))
+ // Only show messages if we have a session open (which
+ // should happen after you get an "invitation"
+ if ( !gIMMgr->hasSession(session_id) )
{
return;
}
@@ -1754,10 +1745,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
BOOL is_this_agent = FALSE;
if(from_id == gAgentID)
{
- from_id = LLUUID::null;
is_this_agent = TRUE;
}
- gIMView->addMessage(
+ gIMMgr->addMessage(
session_id,
from_id,
name,
@@ -1808,7 +1798,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
else
{
// original code resumes
- gIMView->addMessage(session_id, from_id, name, message);
+ gIMMgr->addMessage(session_id, from_id, name, message);
}
break;
@@ -2074,7 +2064,7 @@ void process_offer_callingcard(LLMessageSystem* msg, void**)
if(!source_name.empty())
{
if (gAgent.getBusy()
- || gMuteListp->isMuted(source_id, source_name))
+ || gMuteListp->isMuted(source_id, source_name, LLMute::flagTextChat))
{
// automatically decline offer
callingcard_offer_callback(1, (void*)offerdata);
@@ -2150,7 +2140,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
BOOL is_linden = FALSE;
if (gMuteListp)
{
- is_muted = gMuteListp->isMuted(from_id, from_name)
+ is_muted = gMuteListp->isMuted(from_id, from_name, LLMute::flagTextChat)
|| gMuteListp->isMuted(owner_id);
is_linden = gMuteListp->isLinden(from_name);
}
@@ -2174,16 +2164,15 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
gWorldPointer->mPartSim.addPartSource(psc);
}
- // only pay attention to other people chatting
+ // record last audible utterance
if (is_audible
- && (is_linden || (!is_muted && !is_busy))
- && chatter != gAgent.getAvatarObject())
+ && (is_linden || (!is_muted && !is_busy)))
{
- gAgent.heardChat(chat);
- if (ll_rand(2) == 0)
+ if (chat.mChatType != CHAT_TYPE_START
+ && chat.mChatType != CHAT_TYPE_STOP)
{
- gAgent.setLookAt(LOOKAT_TARGET_AUTO_LISTEN, chatter, LLVector3::zero);
- }
+ gAgent.heardChat(chat.mFromID);
+ }
}
is_owned_by_me = chatter->permYouOwner();
@@ -2214,6 +2203,8 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
// Look for the start of typing so we can put "..." in the bubbles.
if (CHAT_TYPE_START == chat.mChatType)
{
+ gLocalSpeakerMgr->setSpeakerTyping(from_id, TRUE);
+
// Might not have the avatar constructed yet, eg on login.
if (chatter && chatter->isAvatar())
{
@@ -2223,6 +2214,8 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
}
else if (CHAT_TYPE_STOP == chat.mChatType)
{
+ gLocalSpeakerMgr->setSpeakerTyping(from_id, FALSE);
+
// Might not have the avatar constructed yet, eg on login.
if (chatter && chatter->isAvatar())
{
@@ -2234,6 +2227,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
// We have a real utterance now, so can stop showing "..." and proceed.
if (chatter && chatter->isAvatar())
{
+ gLocalSpeakerMgr->setSpeakerTyping(from_id, FALSE);
((LLVOAvatar*)chatter)->stopTyping();
if (!is_muted && !is_busy)
@@ -2362,7 +2356,7 @@ void process_teleport_start(LLMessageSystem *msg, void**)
gTeleportDisplay = TRUE;
gAgent.setTeleportState( LLAgent::TELEPORT_START );
make_ui_sound("UISndTeleportOut");
-
+
// Don't call LLFirstUse::useTeleport here because this could be
// due to being killed, which would send you home, not to a Telehub
}
@@ -2743,7 +2737,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
{
avatarp->mFootPlane.clearVec();
}
-
+
// reset always run status
msg->newMessageFast(_PREHASH_SetAlwaysRun);
msg->nextBlockFast(_PREHASH_AgentData);
@@ -3207,7 +3201,7 @@ void process_sound_trigger(LLMessageSystem *msg, void **)
if (!gParcelMgr->canHearSound(pos_global)) return;
// Don't play sounds triggered by someone you muted.
- if (gMuteListp->isMuted(owner_id)) return;
+ if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return;
// Don't play sounds from an object you muted
if (gMuteListp->isMuted(object_id)) return;
@@ -3219,7 +3213,8 @@ void process_sound_trigger(LLMessageSystem *msg, void **)
return;
}
- gAudiop->triggerSound(sound_id, owner_id, gain, pos_global);
+ F32 volume = gain * gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(sound_id, owner_id, volume, pos_global);
}
void process_preload_sound(LLMessageSystem *msg, void **user_data)
@@ -3241,7 +3236,7 @@ void process_preload_sound(LLMessageSystem *msg, void **user_data)
if (!objectp) return;
if (gMuteListp->isMuted(object_id)) return;
- if (gMuteListp->isMuted(owner_id)) return;
+ if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return;
LLAudioSource *sourcep = objectp->getAudioSource(owner_id);
if (!sourcep) return;
@@ -3279,7 +3274,7 @@ void process_attached_sound(LLMessageSystem *msg, void **user_data)
if (gMuteListp->isMuted(object_id)) return;
- if (gMuteListp->isMuted(owner_id)) return;
+ if (gMuteListp->isMuted(owner_id, LLMute::flagObjectSounds)) return;
objectp->setAttachedSound(sound_id, owner_id, gain, flags);
}
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index e53f779473..bbb69594da 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -156,6 +156,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
mLatestRecvPacketID(0),
mData(NULL),
mAudioSourcep(NULL),
+ mAudioGain(1.f),
mAppAngle(0.f),
mPixelArea(1024.f),
mInventory(NULL),
@@ -2697,6 +2698,12 @@ void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent)
BOOL LLViewerObject::updateLOD()
{
+ // Update volume of looping sounds
+ if (mAudioSourcep && mAudioSourcep->isLoop())
+ {
+ F32 volume = mAudioGain * gSavedSettings.getF32("AudioLevelSFX");
+ mAudioSourcep->setGain(volume);
+ }
return FALSE;
}
@@ -4037,7 +4044,7 @@ void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& own
{
LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num);
//If the owner is muted, don't create the system
- if(gMuteListp->isMuted(owner_id)) return;
+ if(gMuteListp->isMuted(owner_id, LLMute::flagParticles)) return;
// We need to be able to deal with a particle source that hasn't changed, but still got an update!
if (pss)
@@ -4086,7 +4093,7 @@ void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_
{
LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp);
//If the owner is muted, don't create the system
- if(gMuteListp->isMuted(owner_id)) return;
+ if(gMuteListp->isMuted(owner_id, LLMute::flagParticles)) return;
// We need to be able to deal with a particle source that hasn't changed, but still got an update!
if (pss)
{
@@ -4204,7 +4211,9 @@ void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& ow
if (mAudioSourcep)
{
BOOL queue = flags & LL_SOUND_FLAG_QUEUE;
- mAudioSourcep->setGain(gain);
+ mAudioGain = gain;
+ F32 volume = gain * gSavedSettings.getF32("AudioLevelSFX");
+ mAudioSourcep->setGain(volume);
mAudioSourcep->setLoop(flags & LL_SOUND_FLAG_LOOP);
mAudioSourcep->setSyncMaster(flags & LL_SOUND_FLAG_SYNC_MASTER);
mAudioSourcep->setSyncSlave(flags & LL_SOUND_FLAG_SYNC_SLAVE);
@@ -4239,12 +4248,12 @@ void LLViewerObject::adjustAudioGain(const F32 gain)
{
return;
}
-
- if (!mAudioSourcep)
+ if (mAudioSourcep)
{
- return;
+ mAudioGain = gain;
+ F32 volume = mAudioGain * gSavedSettings.getF32("AudioLevelSFX");
+ mAudioSourcep->setGain(volume);
}
- mAudioSourcep->setGain(gain);
}
//----------------------------------------------------------------------------
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index f6aaadd40b..f52466cc2d 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -544,8 +544,9 @@ protected:
U8* mData;
LLPointer<LLViewerPartSourceScript> mPartSourcep; // Particle source associated with this object.
- LLAudioSourceVO *mAudioSourcep;
-
+ LLAudioSourceVO* mAudioSourcep;
+ F32 mAudioGain;
+
F32 mAppAngle; // Apparent visual arc in degrees
F32 mPixelArea; // Apparent area in pixels
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
index ed97cd3ec2..017641b278 100644
--- a/indra/newview/llviewerparcelmgr.cpp
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -1772,13 +1772,10 @@ void optionally_start_music(const LLString& music_url)
// now only play music when you enter a new parcel if the control is in PLAY state
// changed as part of SL-4878
- if ( gOverlayBar->getMusicRemoteControl ()->getTransportState () == LLMediaRemoteCtrl::Play )
+ if ( gOverlayBar && gOverlayBar->musicPlaying() )
{
- if (gAudiop)
- {
- gAudiop->startInternetStream(music_url.c_str());
- }
- };
+ LLOverlayBar::musicPlay(NULL);
+ }
}
}
@@ -1791,12 +1788,7 @@ void callback_start_music(S32 option, void* data)
{
gSavedSettings.setBOOL("AudioStreamingMusic", TRUE);
llinfos << "Starting first parcel music " << music_url << llendl;
- if (gAudiop)
- {
- gAudiop->startInternetStream(music_url->c_str());
- LLMediaRemoteCtrl* ctrl = gOverlayBar->getMusicRemoteControl();
- ctrl->setTransportState( LLMediaRemoteCtrl::Play, FALSE );
- }
+ LLOverlayBar::musicPlay(NULL);
}
else
{
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 4040a6d21b..c94aec8801 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1254,9 +1254,10 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
capabilityNames.append("UntrustedSimulatorMessage");
capabilityNames.append("ParcelVoiceInfoRequest");
capabilityNames.append("ChatSessionRequest");
+ capabilityNames.append("ProvisionVoiceAccountRequest");
llinfos << "posting to seed " << url << llendl;
-
+
LLHTTPClient::post(url, capabilityNames, BaseCapabilitiesComplete::build(this));
}
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
index 3969230197..2fff820602 100644
--- a/indra/newview/llviewertexteditor.cpp
+++ b/indra/newview/llviewertexteditor.cpp
@@ -1247,8 +1247,8 @@ void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item )
const F32 SOUND_GAIN = 1.0f;
if(gAudiop)
{
- gAudiop->triggerSound(
- item->getAssetUUID(), gAgentID, SOUND_GAIN, lpos_global);
+ F32 volume = SOUND_GAIN * gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(item->getAssetUUID(), gAgentID, volume, lpos_global);
}
showCopyToInvDialog( item );
}
@@ -1417,7 +1417,7 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF
LLViewerTextEditor* text_editor = new LLViewerTextEditor(name,
rect,
max_text_length,
- text,
+ "",
font,
allow_embedded_items);
@@ -1433,7 +1433,18 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF
node->getAttributeBOOL("hide_scrollbar",hide_scrollbar);
text_editor->setHideScrollbarForShortDocs(hide_scrollbar);
+ BOOL hide_border = !text_editor->mBorder->getVisible();
+ node->getAttributeBOOL("hide_border", hide_border);
+ text_editor->setBorderVisible(!hide_border);
+
+ BOOL parse_html = text_editor->mParseHTML;
+ node->getAttributeBOOL("allow_html", parse_html);
+ text_editor->setParseHTML(parse_html);
+
text_editor->initFromXML(node, parent);
+ // add text after all parameters have been set
+ text_editor->appendStyledText(text, FALSE, FALSE, NULL);
+
return text_editor;
}
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index dd2c1c6d76..dbce01246f 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -19,6 +19,8 @@
#include "llviewercamera.h"
//#include "imdebug.h"
+#include "llvoiceclient.h" // for push-to-talk button handling
+
#ifdef SABINRIG
#include "cbw.h"
#endif //SABINRIG
@@ -67,9 +69,11 @@
#include "llfeaturemanager.h"
#include "llfilepicker.h"
#include "llfloater.h"
+#include "llfloateractivespeakers.h"
#include "llfloaterbuildoptions.h"
#include "llfloaterbuyland.h"
#include "llfloaterchat.h"
+#include "llfloaterchatterbox.h"
#include "llfloatercustomize.h"
#include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor
#include "llfloaterland.h"
@@ -588,6 +592,7 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask
// Hide tooltips on mousedown
if( mToolTip )
{
+ mToolTipBlocked = TRUE;
mToolTip->setVisible( FALSE );
}
@@ -1088,6 +1093,21 @@ BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK m
return TRUE;
}
+BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ gVoiceClient->middleMouseState(true);
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ gVoiceClient->middleMouseState(false);
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask)
{
@@ -1104,7 +1124,8 @@ void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask
LLCoordGL prev_saved_mouse_point = mCurrentMousePoint;
LLCoordGL mouse_point(x, y);
saveLastMouse(mouse_point);
- BOOL mouse_actually_moved = (prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY);
+ BOOL mouse_actually_moved = !gFocusMgr.getMouseCapture() && // mouse is not currenty captured
+ ((prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY)); // mouse moved from last recorded position
gMouseIdleTimer.reset();
@@ -1216,6 +1237,9 @@ void LLViewerWindow::handleFocusLost(LLWindow *window)
BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
{
+ // Let the voice chat code check for its PTT key. Note that this never affects event processing.
+ gVoiceClient->keyDown(key, mask);
+
if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
{
gAgent.clearAFK();
@@ -1235,6 +1259,9 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask)
{
+ // Let the voice chat code check for its PTT key. Note that this never affects event processing.
+ gVoiceClient->keyUp(key, mask);
+
return FALSE;
}
@@ -1277,16 +1304,7 @@ BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
}
// Unmute audio
- if (!gSavedSettings.getBOOL("MuteAudio"))
- {
- if (gAudiop) gAudiop->setMuted(FALSE);
- F32 volume = gSavedSettings.getF32("MediaAudioVolume");
- if(LLMediaEngine::getInstance())
- {
- LLMediaEngine::getInstance()->setVolume(volume);
- LLMediaEngine::updateClass(volume);
- }
- }
+ audio_update_volume();
}
else
{
@@ -1301,14 +1319,7 @@ BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
stopGL();
}
// Mute audio
- if (gSavedSettings.getBOOL("MuteWhenMinimized"))
- {
- llinfos << "Muting audio on minimize" << llendl;
- if (gAudiop) gAudiop->setMuted(TRUE);
- F32 volume = 0.f;
- LLMediaEngine::getInstance()->setVolume(volume);
- LLMediaEngine::updateClass(volume);
- }
+ audio_update_volume();
}
return TRUE;
}
@@ -1708,7 +1719,7 @@ void LLViewerWindow::initBase()
LLRect notify_rect = full_window;
//notify_rect.mTop -= 24;
notify_rect.mBottom += STATUS_BAR_HEIGHT;
- gNotifyBoxView = new LLNotifyBoxView("notify", notify_rect, FALSE, FOLLOWS_ALL);
+ gNotifyBoxView = new LLNotifyBoxView("notify_container", notify_rect, FALSE, FOLLOWS_ALL);
mRootView->addChild(gNotifyBoxView, -2);
// Tooltips go above floaters
@@ -1862,16 +1873,12 @@ void LLViewerWindow::initWorldUI()
LLFloaterMove::show(NULL);
}
- // Must have one global chat floater so it can actually store
- // the history. JC
- gFloaterChat = new LLFloaterChat();
- gFloaterChat->setVisible( FALSE );
-
- if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) gFloaterChat->loadHistory();
+ gIMMgr = LLIMMgr::getInstance();
- gIMView = new LLIMView("gIMView", LLRect() );
- gIMView->setFollowsAll();
- mRootView->addChild(gIMView);
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
+ {
+ LLFloaterChat::getInstance(LLSD())->loadHistory();
+ }
LLRect morph_view_rect = full_window;
morph_view_rect.stretch( -STATUS_BAR_HEIGHT );
@@ -1921,6 +1928,7 @@ void LLViewerWindow::initWorldUI()
// sync bg color with menu bar
gStatusBar->setBackgroundColor( gMenuBarView->getBackgroundColor() );
+ LLFloaterChatterBox::createInstance(LLSD());
gViewerWindow->getRootView()->addChild(gStatusBar);
@@ -1950,13 +1958,12 @@ LLViewerWindow::~LLViewerWindow()
gFloaterTools = NULL;
gStatusBar = NULL;
gFloaterCamera = NULL;
- gIMView = NULL;
+ gIMMgr = NULL;
gHoverView = NULL;
gFloaterView = NULL;
gMorphView = NULL;
- gFloaterChat = NULL;
gFloaterMute = NULL;
gFloaterMap = NULL;
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index 91bf0d9918..bb60c8283e 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -65,6 +65,8 @@ public:
/*virtual*/ void handleQuit(LLWindow *window);
/*virtual*/ BOOL handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ void handleMouseLeave(LLWindow *window);
/*virtual*/ void handleResize(LLWindow *window, S32 x, S32 y);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 1af8efaf79..60cce34b72 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -96,6 +96,12 @@
//#include "vtune/vtuneapi.h"
+//Ventrella
+#include "llgesturemgr.h" //needed to trigger the voice gestculations
+#include "llvoicevisualizer.h"
+#include "llvoiceclient.h"
+//end Ventrella
+
// Direct imports, evil
extern LLSky gSky;
extern void set_avatar_character(void* charNameArg);
@@ -127,9 +133,7 @@ const F32 PELVIS_LAG_WALKING = 0.4f; // ...while walking
const F32 PELVIS_LAG_MOUSELOOK = 0.15f;
const F32 MOUSELOOK_PELVIS_FOLLOW_FACTOR = 0.5f;
-//Ventrella
const F32 PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON = 0.0001f; // not zero! - something gets divided by this!
-//end Ventrella
#define PELVIS_ROT_THRESHOLD_SLOW 60.0f // amount of deviation allowed between
#define PELVIS_ROT_THRESHOLD_FAST 2.0f // the pelvis and the view direction
@@ -201,10 +205,6 @@ const F32 CHAT_FADE_TIME = 8.0;
const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f;
const S32 MAX_BUBBLES = 7;
-
-const bool USING_VENTRELLA_AVATAR_MOTION_TEST = false;
-
-
S32 LLVOAvatar::sMaxVisible = 50;
LLVOAvatar::ETextureIndex LLVOAvatar::sBakedTextureIndices[BAKED_TEXTURE_COUNT] =
@@ -435,121 +435,16 @@ public:
// called after parameters have been set
// must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character)
- {
- //Ventrella
- // I'm replacing the code below because I need to change
- // the logic in order to add other body parts
- /*
+ {
mCharacter = character;
-
- if (!mChestState.setJoint( character->getJoint("mChest")))
- {
- return STATUS_FAILURE;
- }
-
- mChestState.setUsage(LLJointState::ROT);
-
- addJointState( &mChestState );
- return STATUS_SUCCESS;
- */
-
-
bool success = true;
if ( !mChestState.setJoint( character->getJoint( "mChest" ) ) ) { success = false; }
- if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
- {
- if ( !mNeckState.setJoint ( character->getJoint( "mNeck" )) ) { success = false; }
-
- if ( !mCollarLeftState.setJoint ( character->getJoint( "mCollarLeft" )) ) { success = false; }
- if ( !mShoulderLeftState.setJoint ( character->getJoint( "mShoulderLeft" )) ) { success = false; }
- if ( !mElbowLeftState.setJoint ( character->getJoint( "mElbowLeft" )) ) { success = false; }
- if ( !mWristLeftState.setJoint ( character->getJoint( "mWristLeft" )) ) { success = false; }
-
- if ( !mCollarRightState.setJoint ( character->getJoint( "mCollarRight" )) ) { success = false; }
- if ( !mShoulderRightState.setJoint ( character->getJoint( "mShoulderRight" )) ) { success = false; }
- if ( !mElbowRightState.setJoint ( character->getJoint( "mElbowRight" )) ) { success = false; }
- if ( !mWristRightState.setJoint ( character->getJoint( "mWristRight" )) ) { success = false; }
-
- if ( !mHipLeftState.setJoint ( character->getJoint( "mHipLeft" )) ) { success = false; }
- if ( !mKneeLeftState.setJoint ( character->getJoint( "mKneeLeft" )) ) { success = false; }
- if ( !mAnkleLeftState.setJoint ( character->getJoint( "mAnkleLeft" )) ) { success = false; }
-
- if ( !mHipRightState.setJoint ( character->getJoint( "mHipRight" )) ) { success = false; }
- if ( !mKneeRightState.setJoint ( character->getJoint( "mKneeRight" )) ) { success = false; }
- if ( !mAnkleRightState.setJoint ( character->getJoint( "mAnkleRight" )) ) { success = false; }
- }
-
if ( success )
{
mChestState.setUsage(LLJointState::ROT);
addJointState( &mChestState );
-
- if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
- {
- //-------------------------------------------
- // neck
- //-------------------------------------------
- mNeckState.setUsage(LLJointState::ROT);
- addJointState( &mNeckState );
-
- //-------------------------------------------
- // left arm
- //-------------------------------------------
- mCollarLeftState.setUsage(LLJointState::ROT);
- addJointState( &mCollarLeftState );
-
- mShoulderLeftState.setUsage(LLJointState::ROT);
- addJointState( &mShoulderLeftState );
-
- mElbowLeftState.setUsage(LLJointState::ROT);
- addJointState( &mElbowLeftState );
-
- mWristLeftState.setUsage(LLJointState::ROT);
- addJointState( &mWristLeftState );
-
-
- //-------------------------------------------
- // right arm
- //-------------------------------------------
- mCollarRightState.setUsage(LLJointState::ROT);
- addJointState( &mCollarRightState );
-
- mShoulderRightState.setUsage(LLJointState::ROT);
- addJointState( &mShoulderRightState );
-
- mElbowRightState.setUsage(LLJointState::ROT);
- addJointState( &mElbowRightState );
-
- mWristRightState.setUsage(LLJointState::ROT);
- addJointState( &mWristRightState );
-
- //-------------------------------------------
- // left leg
- //-------------------------------------------
- mHipLeftState.setUsage(LLJointState::ROT);
- addJointState( &mHipLeftState );
-
- mKneeLeftState.setUsage(LLJointState::ROT);
- addJointState( &mKneeLeftState );
-
- mAnkleLeftState.setUsage(LLJointState::ROT);
- addJointState( &mAnkleLeftState );
-
-
- //-------------------------------------------
- // right leg
- //-------------------------------------------
- mHipRightState.setUsage(LLJointState::ROT);
- addJointState( &mHipRightState );
-
- mKneeRightState.setUsage(LLJointState::ROT);
- addJointState( &mKneeRightState );
-
- mAnkleRightState.setUsage(LLJointState::ROT);
- addJointState( &mAnkleRightState );
- }
}
if ( success )
@@ -560,7 +455,6 @@ public:
{
return STATUS_FAILURE;
}
- //end Ventrella
}
// called when a motion is activated
@@ -579,37 +473,9 @@ public:
mChestState.setRotation(LLQuaternion(breathe_amt, LLVector3(0.f, 1.f, 0.f)));
- //Ventrella
- if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
- {
- F32 wave = ( sinf ( time * 2.0f ) * 0.5f );
-
- mChestState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
-
- mCollarLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
- mShoulderLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
- mElbowLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
- mWristLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
-
- mCollarRightState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
- mShoulderRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
- mElbowRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
- mWristRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
-
- mHipLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
- mKneeLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
- mAnkleLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
-
- mHipRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
- mKneeRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
- mAnkleRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
- }
- //end Ventrella
-
return TRUE;
}
-
// called when a motion is deactivated
virtual void onDeactivate() {}
@@ -617,26 +483,7 @@ public:
//-------------------------------------------------------------------------
// joint states to be animated
//-------------------------------------------------------------------------
- LLJointState mChestState;
-
- //Ventrella
- LLJointState mNeckState;
- LLJointState mCollarLeftState;
- LLJointState mShoulderLeftState;
- LLJointState mElbowLeftState;
- LLJointState mWristLeftState;
- LLJointState mCollarRightState;
- LLJointState mShoulderRightState;
- LLJointState mElbowRightState;
- LLJointState mWristRightState;
- LLJointState mHipLeftState;
- LLJointState mKneeLeftState;
- LLJointState mAnkleLeftState;
- LLJointState mHipRightState;
- LLJointState mKneeRightState;
- LLJointState mAnkleRightState;
- //end Ventrella
-
+ LLJointState mChestState;
F32 mBreatheRate;
LLCharacter* mCharacter;
};
@@ -799,6 +646,10 @@ LLVOAvatar::LLVOAvatar(
LLMemType mt(LLMemType::MTYPE_AVATAR);
//VTResume(); // VTune
+
+ // mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline
+ bool needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job
+ mVoiceVisualizer = ( LLVoiceVisualizer *)gHUDManager->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim );
lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl;
@@ -1049,9 +900,13 @@ LLVOAvatar::LLVOAvatar(
createMotion( ANIM_AGENT_CUSTOMIZE_DONE);
//VTPause(); // VTune
+
+ //Ventrella
+ mVoiceVisualizer->setVoiceEnabled( gVoiceClient->getVoiceEnabled( mID ) );
+ mCurrentGesticulationLevel = 0;
+ //END Ventrella
}
-
//------------------------------------------------------------------------
// LLVOAvatar::~LLVOAvatar()
//------------------------------------------------------------------------
@@ -1123,6 +978,8 @@ void LLVOAvatar::markDead()
sNumVisibleChatBubbles--;
}
+ mVoiceVisualizer->markDead();
+
mBeam = NULL;
LLViewerObject::markDead();
}
@@ -2439,7 +2296,84 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
updateCharacter(agent);
+ //Ventrella
+ bool voiceEnabled = gVoiceClient->getVoiceEnabled( mID ) && gVoiceClient->inProximalChannel();
+ // disable voice visualizer when in mouselook
+ mVoiceVisualizer->setVoiceEnabled( voiceEnabled && !(mIsSelf && gAgent.cameraMouselook()) );
+ if ( voiceEnabled )
+ {
+ //----------------------------------------------------------------
+ // Only do gesture triggering for your own avatar, and only when you're in a proximal channel.
+ //----------------------------------------------------------------
+ if( mIsSelf )
+ {
+ //----------------------------------------------------------------------------------------
+ // The following takes the voice signal and uses that to trigger gesticulations.
+ //----------------------------------------------------------------------------------------
+ int lastGesticulationLevel = mCurrentGesticulationLevel;
+ mCurrentGesticulationLevel = mVoiceVisualizer->getCurrentGesticulationLevel();
+
+ //---------------------------------------------------------------------------------------------------
+ // If "current gesticulation level" changes, we catch this, and trigger the new gesture
+ //---------------------------------------------------------------------------------------------------
+ if ( lastGesticulationLevel != mCurrentGesticulationLevel )
+ {
+ if ( mCurrentGesticulationLevel != VOICE_GESTICULATION_LEVEL_OFF )
+ {
+ LLString gestureString = "unInitialized";
+ if ( mCurrentGesticulationLevel == 0 ) { gestureString = "/voicelevel1"; }
+ else if ( mCurrentGesticulationLevel == 1 ) { gestureString = "/voicelevel2"; }
+ else if ( mCurrentGesticulationLevel == 2 ) { gestureString = "/voicelevel3"; }
+ else { printf( "oops - CurrentGesticulationLevel can be only 0, 1, or 2\n" ); }
+
+ // this is the call that Karl S. created for triggering gestures from within the code.
+ gGestureManager.triggerAndReviseString( gestureString );
+ }
+ }
+
+ } //if( mIsSelf )
+ //-----------------------------------------------------------------------------------------------------------------
+ // If the avatar is speaking, then the voice amplitude signal is passed to the voice visualizer.
+ // Also, here we trigger voice visualizer start and stop speaking, so it can animate the voice symbol.
+ //
+ // Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been
+ // "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking.
+ //-----------------------------------------------------------------------------------------------------------------
+ if ( gVoiceClient->getIsSpeaking( mID ) )
+ {
+ if ( ! mVoiceVisualizer->getCurrentlySpeaking() )
+ {
+ mVoiceVisualizer->setStartSpeaking();
+
+ //printf( "gAwayTimer.reset();\n" );
+ }
+
+ mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) );
+
+ if( mIsSelf )
+ {
+ gAgent.clearAFK();
+ }
+ }
+ else
+ {
+ if ( mVoiceVisualizer->getCurrentlySpeaking() )
+ {
+ mVoiceVisualizer->setStopSpeaking();
+ }
+ }
+
+ //--------------------------------------------------------------------------------------------
+ // here we get the approximate head position and set as sound source for the voice symbol
+ // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)
+ //--------------------------------------------------------------------------------------------
+ LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );
+ mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot.getWorldPosition() + headOffset );
+
+ }//if ( voiceEnabled )
+ //End Ventrella
+
if (LLVOAvatar::sJointDebug)
{
@@ -2449,7 +2383,6 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
LLJoint::sNumUpdates = 0;
LLJoint::sNumTouches = 0;
-
if (gNoRender)
{
return TRUE;
@@ -3252,14 +3185,7 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
LLVector3 pelvisDir( mRoot.getWorldMatrix().getFwdRow4().mV );
F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, PELVIS_ROT_THRESHOLD_SLOW, PELVIS_ROT_THRESHOLD_FAST);
-
- //Ventrella
- //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
- //{
- // pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, 1.0f, 1.0f);
- //}
- //end Ventrella
-
+
if (self_in_mouselook)
{
pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR;
@@ -3337,13 +3263,6 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
pelvis_lag_time = PELVIS_LAG_WALKING;
}
- //Ventrella
- //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
- //{
- // pelvis_lag_time = PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON;
- //}
- //end Ventrella
-
F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);
mRoot.setWorldRotation( slerp(u, mRoot.getWorldRotation(), wQv) );
@@ -3461,13 +3380,13 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED,
// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN );
- F32 gain = gSavedSettings.getF32("AudioLevelFootsteps");
+ F32 gain = .30f * gSavedSettings.getF32("AudioLevelAmbient");
LLUUID& step_sound_id = getStepSound();
LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent);
if (gParcelMgr && gParcelMgr->canHearSound(foot_pos_global)
- && gMuteListp && !gMuteListp->isMuted(getID()))
+ && gMuteListp && !gMuteListp->isMuted(getID(), LLMute::flagObjectSounds))
{
gAudiop->triggerSound(step_sound_id, getID(), gain, foot_pos_global);
}
@@ -3476,6 +3395,32 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
mRoot.updateWorldMatrixChildren();
+ // Send the speaker position to the spatialized voice system.
+ if(mIsSelf)
+ {
+ LLMatrix3 rot;
+ LLVector3d pos;
+#if 1
+ // character rotation (stable, shouldn't move with animations)
+ rot = mRoot.getWorldRotation().getMatrix3();
+#else
+ // actual head rotation (moves with animations, probably a bit too much)
+ rot.setRows(
+ LLVector3::x_axis * mSkullp->getWorldRotation(),
+ LLVector3::y_axis * mSkullp->getWorldRotation(),
+ LLVector3::z_axis * mSkullp->getWorldRotation());
+#endif
+
+ pos = getPositionGlobal();
+ pos += LLVector3d(mHeadOffset);
+
+ // MBW -- XXX -- Setting velocity to 0 for now. May figure it out later...
+ gVoiceClient->setAvatarPosition(
+ pos, // position
+ LLVector3::zero, // velocity
+ rot); // rotation matrix
+ }
+
if (!mDebugText.size() && mText.notNull())
{
mText->markDead();
@@ -4398,18 +4343,20 @@ BOOL LLVOAvatar::processSingleAnimationStateChange( const LLUUID& anim_id, BOOL
{
LLVector3d char_pos_global = gAgent.getPosGlobalFromAgent(getCharacterPosition());
if (gParcelMgr && gParcelMgr->canHearSound(char_pos_global)
- && gMuteListp && !gMuteListp->isMuted(getID()))
+ && gMuteListp && !gMuteListp->isMuted(getID(), LLMute::flagObjectSounds))
{
// RN: uncomment this to play on typing sound at fixed volume once sound engine is fixed
// to support both spatialized and non-spatialized instances of the same sound
//if (mIsSelf)
//{
- // gAudiop->triggerSound(LLUUID(gSavedSettings.getString("UISndTyping")), 0.8f);
+ // F32 volume = gain * gSavedSettings.getF32("AudioLevelUI")
+ // gAudiop->triggerSound(LLUUID(gSavedSettings.getString("UISndTyping")), volume);
//}
//else
{
LLUUID sound_id = LLUUID(gSavedSettings.getString("UISndTyping"));
- gAudiop->triggerSound(sound_id, getID(), 1.f, char_pos_global);
+ F32 volume = gSavedSettings.getF32("AudioLevelSFX");
+ gAudiop->triggerSound(sound_id, getID(), volume, char_pos_global);
}
}
}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index c98826d15b..0eac13206b 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -32,6 +32,11 @@
#include "llxmltree.h"
#include "llwearable.h"
+//Ventrella
+//#include "llvoiceclient.h"
+#include "llvoicevisualizer.h"
+//End Ventrella
+
const S32 VOAVATAR_SCRATCH_TEX_WIDTH = 512;
const S32 VOAVATAR_SCRATCH_TEX_HEIGHT = 512;
@@ -927,6 +932,17 @@ protected:
LLTexLayerSet* getLayerSet(ETextureIndex index) const;
LLHost getObjectHost() const;
S32 getLocalDiscardLevel( S32 index);
+
+//Ventrella
+ //-----------------------------------------------------------------------------------------------
+ // the Voice Visualizer is responsible for detecting the user's voice signal, and when the
+ // user speaks, it puts a voice symbol over the avatar's head, and triggering gesticulations
+ //-----------------------------------------------------------------------------------------------
+ private:
+ LLVoiceVisualizer * mVoiceVisualizer;
+ int mCurrentGesticulationLevel;
+//End Ventrella
+
};
#endif // LL_VO_AVATAR_H
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
new file mode 100644
index 0000000000..0dba3588e5
--- /dev/null
+++ b/indra/newview/llvoiceclient.cpp
@@ -0,0 +1,4056 @@
+/**
+ * @file llvoiceclient.cpp
+ * @brief Implementation of LLVoiceClient class which is the interface to the voice client process.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <boost/tokenizer.hpp>
+
+#include "llviewerprecompiledheaders.h"
+#include "llvoiceclient.h"
+
+#include "llsdutil.h"
+
+#include "llvoavatar.h"
+#include "llbufferstream.h"
+#include "llfile.h"
+#include "expat/expat.h"
+#include "llcallbacklist.h"
+#include "llviewerregion.h"
+#include "llviewernetwork.h" // for gUserServerChoice
+#include "llfloateractivespeakers.h" // for LLSpeakerMgr
+#include "llbase64.h"
+#include "llviewercontrol.h"
+#include "llkeyboard.h"
+#include "viewer.h" // for gDisconnected, gDisableVoice
+#include "llmutelist.h" // to check for muted avatars
+#include "llagent.h"
+#include "llcachename.h"
+#include "llimview.h" // for LLIMMgr
+#include "llimpanel.h" // for LLVoiceChannel
+#include "llparcel.h"
+#include "llviewerparcelmgr.h"
+#include "llfirstuse.h"
+#include "llviewerwindow.h"
+
+// for base64 decoding
+#include "apr-1/apr_base64.h"
+
+// for SHA1 hash
+#include "apr-1/apr_sha1.h"
+
+// If we are connecting to agni AND the user's last name is "Linden", join this channel instead of looking up the sim name.
+// If we are connecting to agni and the user's last name is NOT "Linden", disable voice.
+#define AGNI_LINDENS_ONLY_CHANNEL "SL"
+static bool sConnectingToAgni = false;
+F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
+
+const F32 SPEAKING_TIMEOUT = 1.f;
+
+const int VOICE_MAJOR_VERSION = 1;
+const int VOICE_MINOR_VERSION = 0;
+
+LLVoiceClient *gVoiceClient = NULL;
+
+// Don't retry connecting to the daemon more frequently than this:
+const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
+
+// Don't send positional updates more frequently than this:
+const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
+
+const F32 LOGIN_RETRY_SECONDS = 10.0f;
+const int MAX_LOGIN_RETRIES = 12;
+
+class LLViewerVoiceAccountProvisionResponder :
+ public LLHTTPClient::Responder
+{
+public:
+ LLViewerVoiceAccountProvisionResponder(int retries)
+ {
+ mRetries = retries;
+ }
+
+ virtual void error(U32 status, const std::string& reason)
+ {
+ if ( mRetries > 0 )
+ {
+ if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
+ mRetries - 1);
+ }
+ else
+ {
+ //TODO: throw an error message?
+ if ( gVoiceClient ) gVoiceClient->giveUp();
+ }
+ }
+
+ virtual void result(const LLSD& content)
+ {
+ if ( gVoiceClient )
+ {
+ gVoiceClient->login(
+ content["username"].asString(),
+ content["password"].asString());
+ }
+ }
+
+private:
+ int mRetries;
+};
+
+/**
+ * @class LLVivoxProtocolParser
+ * @brief This class helps construct new LLIOPipe specializations
+ * @see LLIOPipe
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLVivoxProtocolParser : public LLIOPipe
+{
+ LOG_CLASS(LLVivoxProtocolParser);
+public:
+ LLVivoxProtocolParser();
+ virtual ~LLVivoxProtocolParser();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+ std::string mInput;
+
+ // Expat control members
+ XML_Parser parser;
+ int responseDepth;
+ bool ignoringTags;
+ bool isEvent;
+ int ignoreDepth;
+
+ // Members for processing responses. The values are transient and only valid within a call to processResponse().
+ int returnCode;
+ int statusCode;
+ std::string statusString;
+ std::string uuidString;
+ std::string actionString;
+ std::string connectorHandle;
+ std::string accountHandle;
+ std::string sessionHandle;
+ std::string eventSessionHandle;
+
+ // Members for processing events. The values are transient and only valid within a call to processResponse().
+ std::string eventTypeString;
+ int state;
+ std::string uriString;
+ bool isChannel;
+ std::string nameString;
+ std::string audioMediaString;
+ std::string displayNameString;
+ int participantType;
+ bool isLocallyMuted;
+ bool isModeratorMuted;
+ bool isSpeaking;
+ int volume;
+ F32 energy;
+
+ // Members for processing text between tags
+ std::string textBuffer;
+ bool accumulateText;
+
+ void reset();
+
+ void processResponse(std::string tag);
+
+static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr);
+static void XMLCALL ExpatEndTag(void *data, const char *el);
+static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len);
+
+ void StartTag(const char *tag, const char **attr);
+ void EndTag(const char *tag);
+ void CharData(const char *buffer, int length);
+
+};
+
+LLVivoxProtocolParser::LLVivoxProtocolParser()
+{
+ parser = NULL;
+ parser = XML_ParserCreate(NULL);
+
+ reset();
+}
+
+void LLVivoxProtocolParser::reset()
+{
+ responseDepth = 0;
+ ignoringTags = false;
+ accumulateText = false;
+ textBuffer.clear();
+}
+
+//virtual
+LLVivoxProtocolParser::~LLVivoxProtocolParser()
+{
+ if (parser)
+ XML_ParserFree(parser);
+}
+
+// virtual
+LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ LLBufferStream istr(channels, buffer.get());
+ std::ostringstream ostr;
+ while (istr.good())
+ {
+ char buf[1024];
+ istr.read(buf, sizeof(buf));
+ mInput.append(buf, istr.gcount());
+ }
+
+ // MBW -- XXX -- This should no longer be necessary. Or even possible.
+ // We've read all the data out of the buffer. Make sure it doesn't accumulate.
+// buffer->clear();
+
+ // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser.
+ int start = 0;
+ int delim;
+ while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
+ {
+ // Turn this on to log incoming XML
+ if(0)
+ {
+ int foo = mInput.find("Set3DPosition", start);
+ int bar = mInput.find("ParticipantPropertiesEvent", start);
+ if(foo != std::string::npos && (foo < delim))
+ {
+ // This is a Set3DPosition response. Don't print it, since these are way too spammy.
+ }
+ else if(bar != std::string::npos && (bar < delim))
+ {
+ // This is a ParticipantPropertiesEvent response. Don't print it, since these are way too spammy.
+ }
+ else
+ {
+ llinfos << "parsing: " << mInput.substr(start, delim - start) << llendl;
+ }
+ }
+
+ // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
+ reset();
+
+ XML_ParserReset(parser, NULL);
+ XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag);
+ XML_SetCharacterDataHandler(parser, ExpatCharHandler);
+ XML_SetUserData(parser, this);
+ XML_Parse(parser, mInput.data() + start, delim - start, false);
+
+ start = delim + 3;
+ }
+
+ if(start != 0)
+ mInput = mInput.substr(start);
+
+// llinfos << "at end, mInput is: " << mInput << llendl;
+
+ if(!gVoiceClient->mConnected)
+ {
+ // If voice has been disabled, we just want to close the socket. This does so.
+ llinfos << "returning STATUS_STOP" << llendl;
+ return STATUS_STOP;
+ }
+
+ return STATUS_OK;
+}
+
+void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr)
+{
+ if (data)
+ {
+ LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
+ object->StartTag(el, attr);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el)
+{
+ if (data)
+ {
+ LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
+ object->EndTag(el);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len)
+{
+ if (data)
+ {
+ LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data;
+ object->CharData(s, len);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+
+void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
+{
+ // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags
+ textBuffer.clear();
+ // only accumulate text if we're not ignoring tags.
+ accumulateText = !ignoringTags;
+
+ if (responseDepth == 0)
+ {
+ isEvent = strcmp("Event", tag) == 0;
+
+ if (strcmp("Response", tag) == 0 || isEvent)
+ {
+ // Grab the attributes
+ while (*attr)
+ {
+ const char *key = *attr++;
+ const char *value = *attr++;
+
+ if (strcmp("requestId", key) == 0)
+ {
+ uuidString = value;
+ }
+ else if (strcmp("action", key) == 0)
+ {
+ actionString = value;
+ }
+ else if (strcmp("type", key) == 0)
+ {
+ eventTypeString = value;
+ }
+ }
+ }
+ //llinfos << tag << " (" << responseDepth << ")" << llendl;
+ }
+ else
+ {
+ if (ignoringTags)
+ {
+ //llinfos << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << llendl;
+ }
+ else
+ {
+ //llinfos << tag << " (" << responseDepth << ")" << llendl;
+
+ // Ignore the InputXml stuff so we don't get confused
+ if (strcmp("InputXml", tag) == 0)
+ {
+ ignoringTags = true;
+ ignoreDepth = responseDepth;
+ accumulateText = false;
+
+ //llinfos << "starting ignore, ignoreDepth is " << ignoreDepth << llendl;
+ }
+ else if (strcmp("CaptureDevices", tag) == 0)
+ {
+ gVoiceClient->clearCaptureDevices();
+ }
+ else if (strcmp("RenderDevices", tag) == 0)
+ {
+ gVoiceClient->clearRenderDevices();
+ }
+ }
+ }
+ responseDepth++;
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::EndTag(const char *tag)
+{
+ const char *string = textBuffer.c_str();
+ bool clearbuffer = true;
+
+ responseDepth--;
+
+ if (ignoringTags)
+ {
+ if (ignoreDepth == responseDepth)
+ {
+ //llinfos << "end of ignore" << llendl;
+ ignoringTags = false;
+ }
+ else
+ {
+ //llinfos << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << llendl;
+ }
+ }
+
+ if (!ignoringTags)
+ {
+ //llinfos << "processing tag " << tag << " (depth = " << responseDepth << ")" << llendl;
+
+ // Closing a tag. Finalize the text we've accumulated and reset
+ if (strcmp("ReturnCode", tag) == 0)
+ returnCode = strtol(string, NULL, 10);
+ else if (strcmp("StatusCode", tag) == 0)
+ statusCode = strtol(string, NULL, 10);
+ else if (strcmp("ConnectorHandle", tag) == 0)
+ connectorHandle = string;
+ else if (strcmp("AccountHandle", tag) == 0)
+ accountHandle = string;
+ else if (strcmp("SessionHandle", tag) == 0)
+ {
+ if (isEvent)
+ eventSessionHandle = string;
+ else
+ sessionHandle = string;
+ }
+ else if (strcmp("StatusString", tag) == 0)
+ statusString = string;
+ else if (strcmp("State", tag) == 0)
+ state = strtol(string, NULL, 10);
+ else if (strcmp("URI", tag) == 0)
+ uriString = string;
+ else if (strcmp("IsChannel", tag) == 0)
+ isChannel = strcmp(string, "true") == 0;
+ else if (strcmp("Name", tag) == 0)
+ nameString = string;
+ else if (strcmp("AudioMedia", tag) == 0)
+ audioMediaString = string;
+ else if (strcmp("ChannelName", tag) == 0)
+ nameString = string;
+ else if (strcmp("ParticipantURI", tag) == 0)
+ uriString = string;
+ else if (strcmp("DisplayName", tag) == 0)
+ displayNameString = string;
+ else if (strcmp("AccountName", tag) == 0)
+ nameString = string;
+ else if (strcmp("ParticipantTyppe", tag) == 0)
+ participantType = strtol(string, NULL, 10);
+ else if (strcmp("IsLocallyMuted", tag) == 0)
+ isLocallyMuted = strcmp(string, "true") == 0;
+ else if (strcmp("IsModeratorMuted", tag) == 0)
+ isModeratorMuted = strcmp(string, "true") == 0;
+ else if (strcmp("IsSpeaking", tag) == 0)
+ isSpeaking = strcmp(string, "true") == 0;
+ else if (strcmp("Volume", tag) == 0)
+ volume = strtol(string, NULL, 10);
+ else if (strcmp("Energy", tag) == 0)
+ energy = (F32)strtod(string, NULL);
+ else if (strcmp("MicEnergy", tag) == 0)
+ energy = (F32)strtod(string, NULL);
+ else if (strcmp("ChannelName", tag) == 0)
+ nameString = string;
+ else if (strcmp("ChannelURI", tag) == 0)
+ uriString = string;
+ else if (strcmp("ChannelListResult", tag) == 0)
+ {
+ gVoiceClient->addChannelMapEntry(nameString, uriString);
+ }
+ else if (strcmp("Device", tag) == 0)
+ {
+ // This closing tag shouldn't clear the accumulated text.
+ clearbuffer = false;
+ }
+ else if (strcmp("CaptureDevice", tag) == 0)
+ {
+ gVoiceClient->addCaptureDevice(textBuffer);
+ }
+ else if (strcmp("RenderDevice", tag) == 0)
+ {
+ gVoiceClient->addRenderDevice(textBuffer);
+ }
+
+ if(clearbuffer)
+ {
+ textBuffer.clear();
+ accumulateText= false;
+ }
+
+ if (responseDepth == 0)
+ {
+ // We finished all of the XML, process the data
+ processResponse(tag);
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::CharData(const char *buffer, int length)
+{
+ /*
+ This method is called for anything that isn't a tag, which can be text you
+ want that lies between tags, and a lot of stuff you don't want like file formatting
+ (tabs, spaces, CR/LF, etc).
+
+ Only copy text if we are in accumulate mode...
+ */
+ if (accumulateText)
+ textBuffer.append(buffer, length);
+}
+
+// --------------------------------------------------------------------------------
+
+void LLVivoxProtocolParser::processResponse(std::string tag)
+{
+// llinfos << tag << llendl;
+
+ if (isEvent)
+ {
+ if (eventTypeString == "LoginStateChangeEvent")
+ {
+ gVoiceClient->loginStateChangeEvent(accountHandle, statusCode, statusString, state);
+ }
+ else if (eventTypeString == "SessionNewEvent")
+ {
+ gVoiceClient->sessionNewEvent(accountHandle, eventSessionHandle, state, nameString, uriString);
+ }
+ else if (eventTypeString == "SessionStateChangeEvent")
+ {
+ gVoiceClient->sessionStateChangeEvent(uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString);
+ }
+ else if (eventTypeString == "ParticipantStateChangeEvent")
+ {
+ gVoiceClient->participantStateChangeEvent(uriString, statusCode, statusString, state, nameString, displayNameString, participantType);
+
+ }
+ else if (eventTypeString == "ParticipantPropertiesEvent")
+ {
+ gVoiceClient->participantPropertiesEvent(uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy);
+ }
+ else if (eventTypeString == "AuxAudioPropertiesEvent")
+ {
+ gVoiceClient->auxAudioPropertiesEvent(energy);
+ }
+ }
+ else
+ {
+ if (actionString == "Connector.Create.1")
+ {
+ gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle);
+ }
+ else if (actionString == "Account.Login.1")
+ {
+ gVoiceClient->loginResponse(statusCode, statusString, accountHandle);
+ }
+ else if (actionString == "Session.Create.1")
+ {
+ gVoiceClient->sessionCreateResponse(statusCode, statusString, sessionHandle);
+ }
+ else if (actionString == "Session.Connect.1")
+ {
+ gVoiceClient->sessionConnectResponse(statusCode, statusString);
+ }
+ else if (actionString == "Session.Terminate.1")
+ {
+ gVoiceClient->sessionTerminateResponse(statusCode, statusString);
+ }
+ else if (actionString == "Account.Logout.1")
+ {
+ gVoiceClient->logoutResponse(statusCode, statusString);
+ }
+ else if (actionString == "Connector.InitiateShutdown.1")
+ {
+ gVoiceClient->connectorShutdownResponse(statusCode, statusString);
+ }
+ else if (actionString == "Account.ChannelGetList.1")
+ {
+ gVoiceClient->channelGetListResponse(statusCode, statusString);
+ }
+/*
+ else if (actionString == "Connector.AccountCreate.1")
+ {
+
+ }
+ else if (actionString == "Connector.MuteLocalMic.1")
+ {
+
+ }
+ else if (actionString == "Connector.MuteLocalSpeaker.1")
+ {
+
+ }
+ else if (actionString == "Connector.SetLocalMicVolume.1")
+ {
+
+ }
+ else if (actionString == "Connector.SetLocalSpeakerVolume.1")
+ {
+
+ }
+ else if (actionString == "Session.ListenerSetPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.SpeakerSetPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.Set3DPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.AudioSourceSetPosition.1")
+ {
+
+ }
+ else if (actionString == "Session.GetChannelParticipants.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelCreate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelUpdate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelDelete.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelCreateAndInvite.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelFolderCreate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelFolderUpdate.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelFolderDelete.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelAddModerator.1")
+ {
+
+ }
+ else if (actionString == "Account.ChannelDeleteModerator.1")
+ {
+
+ }
+*/
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+class LLVoiceClientPrefsListener: public LLSimpleListener
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Note: Ignore the specific event value, look up the ones we want
+
+ gVoiceClient->setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat"));
+ gVoiceClient->setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled"));
+ std::string keyString = gSavedSettings.getString("PushToTalkButton");
+ gVoiceClient->setPTTKey(keyString);
+ gVoiceClient->setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
+ gVoiceClient->setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
+ std::string serverName = gSavedSettings.getString("VivoxDebugServerName");
+ gVoiceClient->setVivoxDebugServerName(serverName);
+
+ std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
+ gVoiceClient->setCaptureDevice(inputDevice);
+ std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
+ gVoiceClient->setRenderDevice(outputDevice);
+
+ return true;
+ }
+};
+static LLVoiceClientPrefsListener voice_prefs_listener;
+
+class LLVoiceClientMuteListObserver : public LLMuteListObserver
+{
+ /* virtual */ void onChange() { gVoiceClient->muteListChanged();}
+};
+static LLVoiceClientMuteListObserver mutelist_listener;
+static bool sMuteListListener_listening = false;
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+class LLVoiceClientCapResponder : public LLHTTPClient::Responder
+{
+public:
+ LLVoiceClientCapResponder(void){};
+
+ virtual void error(U32 status, const std::string& reason); // called with bad status codes
+ virtual void result(const LLSD& content);
+
+private:
+};
+
+void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
+{
+ llwarns << "LLVoiceClientCapResponder::error("
+ << status << ": " << reason << ")"
+ << llendl;
+}
+
+void LLVoiceClientCapResponder::result(const LLSD& content)
+{
+ LLSD::map_const_iterator iter;
+ for(iter = content.beginMap(); iter != content.endMap(); ++iter)
+ {
+ llinfos << "LLVoiceClientCapResponder::result got "
+ << iter->first << llendl;
+ }
+
+ if ( content.has("voice_credentials") )
+ {
+ LLSD voice_credentials = content["voice_credentials"];
+ std::string uri;
+ std::string credentials;
+
+ if ( voice_credentials.has("channel_uri") )
+ {
+ uri = voice_credentials["channel_uri"].asString();
+ }
+ if ( voice_credentials.has("channel_credentials") )
+ {
+ credentials =
+ voice_credentials["channel_credentials"].asString();
+ }
+
+ gVoiceClient->setSpatialChannel(uri, credentials);
+ }
+}
+
+
+
+#if LL_WINDOWS
+static HANDLE sGatewayHandle = 0;
+
+static bool isGatewayRunning()
+{
+ bool result = false;
+ if(sGatewayHandle != 0)
+ {
+ DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
+ if(waitresult != WAIT_OBJECT_0)
+ {
+ result = true;
+ }
+ }
+ return result;
+}
+static void killGateway()
+{
+ if(sGatewayHandle != 0)
+ {
+ TerminateProcess(sGatewayHandle,0);
+ }
+}
+
+#else // Mac and linux
+
+static pid_t sGatewayPID = 0;
+static bool isGatewayRunning()
+{
+ bool result = false;
+ if(sGatewayPID != 0)
+ {
+ // A kill with signal number 0 has no effect, just does error checking. It should return an error if the process no longer exists.
+ if(kill(sGatewayPID, 0) == 0)
+ {
+ result = true;
+ }
+ }
+ return result;
+}
+
+static void killGateway()
+{
+ if(sGatewayPID != 0)
+ {
+ kill(sGatewayPID, SIGTERM);
+ }
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+LLVoiceClient::LLVoiceClient()
+{
+ gVoiceClient = this;
+ mWriteInProgress = false;
+ mAreaVoiceDisabled = false;
+ mPTT = true;
+ mUserPTTState = false;
+ mMuteMic = false;
+ mSessionTerminateRequested = false;
+ mCommandCookie = 0;
+ mNonSpatialChannel = false;
+ mNextSessionSpatial = true;
+ mNextSessionNoReconnect = false;
+ mSessionP2P = false;
+ mCurrentParcelLocalID = 0;
+ mLoginRetryCount = 0;
+ mVivoxErrorStatusCode = 0;
+
+ mNextSessionResetOnClose = false;
+ mSessionResetOnClose = false;
+ mSpeakerVolume = 0;
+ mMicVolume = 0;
+
+ // Initial dirty state
+ mSpatialCoordsDirty = false;
+ mPTTDirty = true;
+ mVolumeDirty = true;
+ mSpeakerVolumeDirty = true;
+ mMicVolumeDirty = true;
+ mCaptureDeviceDirty = false;
+ mRenderDeviceDirty = false;
+
+ // Load initial state from prefs.
+ mVoiceEnabled = gSavedSettings.getBOOL("EnableVoiceChat");
+ mUsePTT = gSavedSettings.getBOOL("EnablePushToTalk");
+ std::string keyString = gSavedSettings.getString("PushToTalkButton");
+ setPTTKey(keyString);
+ mPTTIsToggle = gSavedSettings.getBOOL("PushToTalkToggle");
+ mEarLocation = gSavedSettings.getS32("VoiceEarLocation");
+ setVoiceVolume(gSavedSettings.getF32("AudioLevelVoice"));
+ std::string captureDevice = gSavedSettings.getString("VoiceInputAudioDevice");
+ setCaptureDevice(captureDevice);
+ std::string renderDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
+ setRenderDevice(renderDevice);
+
+ // Set up our listener to get updates on all prefs values we care about.
+ gSavedSettings.getControl("EnableVoiceChat")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("PTTCurrentlyEnabled")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("PushToTalkButton")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("PushToTalkToggle")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VoiceEarLocation")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VivoxDebugServerName")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VoiceInputAudioDevice")->addListener(&voice_prefs_listener);
+ gSavedSettings.getControl("VoiceOutputAudioDevice")->addListener(&voice_prefs_listener);
+
+ mTuningMode = false;
+ mTuningEnergy = 0.0f;
+ mTuningMicVolume = 0;
+ mTuningMicVolumeDirty = true;
+ mTuningSpeakerVolume = 0;
+ mTuningSpeakerVolumeDirty = true;
+ mTuningCaptureRunning = false;
+
+ // gMuteListp isn't set up at this point, so we defer this until later.
+// gMuteListp->addObserver(&mutelist_listener);
+
+ mParticipantMapChanged = false;
+
+ // stash the pump for later use
+ // This now happens when init() is called instead.
+ mPump = NULL;
+
+#if LL_DARWIN || LL_LINUX
+ // MBW -- XXX -- THIS DOES NOT BELONG HERE
+ // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
+ // This should cause us to ignore SIGPIPE and handle the error through proper channels.
+ // This should really be set up elsewhere. Where should it go?
+ signal(SIGPIPE, SIG_IGN);
+
+ // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
+ // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that.
+ signal(SIGCHLD, SIG_IGN);
+#endif
+
+ // set up state machine
+ setState(stateDisabled);
+
+ gIdleCallbacks.addFunction(idle, this);
+}
+
+//---------------------------------------------------
+
+LLVoiceClient::~LLVoiceClient()
+{
+}
+
+//----------------------------------------------
+
+
+
+void LLVoiceClient::init(LLPumpIO *pump)
+{
+ // constructor will set up gVoiceClient
+ LLVoiceClient::getInstance()->mPump = pump;
+}
+
+void LLVoiceClient::terminate()
+{
+ if(gVoiceClient)
+ {
+ gVoiceClient->sessionTerminateSendMessage();
+ gVoiceClient->logout();
+ gVoiceClient->connectorShutdown();
+ gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
+
+ // This will do unpleasant things on windows.
+// killGateway();
+
+ // Don't do this anymore -- LLSingleton will take care of deleting the object.
+// delete gVoiceClient;
+
+ // Hint to other code not to access the voice client anymore.
+ gVoiceClient = NULL;
+ }
+}
+
+
+/////////////////////////////
+// utility functions
+
+bool LLVoiceClient::writeString(const std::string &str)
+{
+ bool result = false;
+ if(mConnected)
+ {
+ apr_status_t err;
+ apr_size_t size = (apr_size_t)str.size();
+ apr_size_t written = size;
+
+// llinfos << "sending: " << str << llendl;
+
+ // MBW -- XXX -- check return code - sockets will fail (broken, etc.)
+ err = apr_socket_send(
+ mSocket->getSocket(),
+ (const char*)str.data(),
+ &written);
+
+ if(err == 0)
+ {
+ // Success.
+ result = true;
+ }
+ // MBW -- XXX -- handle partial writes (written is number of bytes written)
+ // Need to set socket to non-blocking before this will work.
+// else if(APR_STATUS_IS_EAGAIN(err))
+// {
+// //
+// }
+ else
+ {
+ // Assume any socket error means something bad. For now, just close the socket.
+ char buf[MAX_STRING];
+ llwarns << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << llendl;
+ daemonDied();
+ }
+ }
+
+ return result;
+}
+
+
+/////////////////////////////
+// session control messages
+void LLVoiceClient::connectorCreate()
+{
+ std::ostringstream stream;
+ std::string logpath;
+ std::string loglevel = "0";
+
+ // Transition to stateConnectorStarted when the connector handle comes back.
+ setState(stateConnectorStarting);
+
+ std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
+
+ if(savedLogLevel != "-1")
+ {
+ llinfos << "creating connector with logging enabled" << llendl;
+ loglevel = "10";
+ logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
+ }
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
+ << "<ClientName>V2 SDK</ClientName>"
+ << "<AccountManagementServer>" << mAccountServerURI << "</AccountManagementServer>"
+ << "<Logging>"
+ << "<Enabled>false</Enabled>"
+ << "<Folder>" << logpath << "</Folder>"
+ << "<FileNamePrefix>Connector</FileNamePrefix>"
+ << "<FileNameSuffix>.log</FileNameSuffix>"
+ << "<LogLevel>" << loglevel << "</LogLevel>"
+ << "</Logging>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::connectorShutdown()
+{
+ setState(stateConnectorStopping);
+
+ if(!mConnectorHandle.empty())
+ {
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "</Request>"
+ << "\n\n\n";
+
+ mConnectorHandle.clear();
+
+ writeString(stream.str());
+ }
+}
+
+void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID)
+{
+ mAccountFirstName = firstName;
+ mAccountLastName = lastName;
+
+ mAccountDisplayName = firstName;
+ mAccountDisplayName += " ";
+ mAccountDisplayName += lastName;
+
+ llinfos << "name \"" << mAccountDisplayName << "\" , ID " << agentID << llendl;
+
+ std::string userserver = gUserServerName;
+ LLString::toLower(userserver);
+ if((gUserServerChoice == USERSERVER_AGNI) ||
+ ((gUserServerChoice == USERSERVER_OTHER) && (userserver.find("agni") != std::string::npos)))
+ {
+ sConnectingToAgni = true;
+ }
+
+ // MBW -- XXX -- Enable this when the bhd.vivox.com server gets a real ssl cert.
+ if(sConnectingToAgni)
+ {
+ // Use the release account server
+ mAccountServerName = "bhr.vivox.com";
+ mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
+ }
+ else
+ {
+ // Use the development account server
+ mAccountServerName = gSavedSettings.getString("VivoxDebugServerName");
+ mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
+ }
+
+ mAccountName = nameFromID(agentID);
+}
+
+void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
+{
+ if ( gAgent.getRegion() && mVoiceEnabled )
+ {
+ std::string url =
+ gAgent.getRegion()->getCapability(
+ "ProvisionVoiceAccountRequest");
+
+ if ( url == "" ) return;
+
+ LLHTTPClient::post(
+ url,
+ LLSD(),
+ new LLViewerVoiceAccountProvisionResponder(retries));
+ }
+}
+
+void LLVoiceClient::login(
+ const std::string& accountName,
+ const std::string &password)
+{
+ if((getState() >= stateLoggingIn) && (getState() < stateLoggedOut))
+ {
+ // Already logged in. This is an internal error.
+ llerrs << "called from wrong state." << llendl;
+ }
+ else if ( accountName != mAccountName )
+ {
+ //TODO: error?
+ llinfos << "Wrong account name! " << accountName
+ << " instead of " << mAccountName << llendl;
+ }
+ else
+ {
+ mAccountPassword = password;
+ }
+}
+
+void LLVoiceClient::idle(void* user_data)
+{
+ LLVoiceClient* self = (LLVoiceClient*)user_data;
+ self->stateMachine();
+}
+
+const char *LLVoiceClient::state2string(LLVoiceClient::state inState)
+{
+ const char *result = "UNKNOWN";
+
+ // Prevent copy-paste errors when updating this list...
+#define CASE(x) case x: result = #x; break
+
+ switch(inState)
+ {
+ CASE(stateDisabled);
+ CASE(stateStart);
+ CASE(stateDaemonLaunched);
+ CASE(stateConnecting);
+ CASE(stateIdle);
+ CASE(stateConnectorStart);
+ CASE(stateConnectorStarting);
+ CASE(stateConnectorStarted);
+ CASE(stateMicTuningNoLogin);
+ CASE(stateLoginRetry);
+ CASE(stateLoginRetryWait);
+ CASE(stateNeedsLogin);
+ CASE(stateLoggingIn);
+ CASE(stateLoggedIn);
+ CASE(stateNoChannel);
+ CASE(stateMicTuningLoggedIn);
+ CASE(stateSessionCreate);
+ CASE(stateSessionConnect);
+ CASE(stateJoiningSession);
+ CASE(stateSessionJoined);
+ CASE(stateRunning);
+ CASE(stateLeavingSession);
+ CASE(stateSessionTerminated);
+ CASE(stateLoggingOut);
+ CASE(stateLoggedOut);
+ CASE(stateConnectorStopping);
+ CASE(stateConnectorStopped);
+ CASE(stateConnectorFailed);
+ CASE(stateConnectorFailedWaiting);
+ CASE(stateLoginFailed);
+ CASE(stateLoginFailedWaiting);
+ CASE(stateJoinSessionFailed);
+ CASE(stateJoinSessionFailedWaiting);
+ CASE(stateJail);
+ }
+
+#undef CASE
+
+ return result;
+}
+
+const char *LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
+{
+ const char *result = "UNKNOWN";
+
+ // Prevent copy-paste errors when updating this list...
+#define CASE(x) case x: result = #x; break
+
+ switch(inStatus)
+ {
+ CASE(STATUS_JOINING);
+ CASE(STATUS_JOINED);
+ CASE(STATUS_LEFT_CHANNEL);
+ CASE(ERROR_CHANNEL_FULL);
+ CASE(ERROR_CHANNEL_LOCKED);
+ CASE(ERROR_UNKNOWN);
+ default:
+ break;
+ }
+
+#undef CASE
+
+ return result;
+}
+
+void LLVoiceClient::setState(state inState)
+{
+ llinfos << "entering state " << state2string(inState) << llendl;
+
+ mState = inState;
+}
+
+void LLVoiceClient::stateMachine()
+{
+ if(gDisconnected)
+ {
+ // The viewer has been disconnected from the sim. Disable voice.
+ setVoiceEnabled(false);
+ }
+
+ if(!mVoiceEnabled)
+ {
+ if(getState() != stateDisabled)
+ {
+ // User turned off voice support. Send the cleanup messages, close the socket, and reset.
+ if(!mConnected)
+ {
+ // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
+ llinfos << "Disabling voice before connection to daemon, terminating." << llendl;
+ killGateway();
+ }
+
+ sessionTerminateSendMessage();
+ logout();
+ connectorShutdown();
+ closeSocket();
+ removeAllParticipants();
+
+ setState(stateDisabled);
+ }
+ }
+
+ // Check for parcel boundary crossing
+ {
+ LLViewerRegion *region = gAgent.getRegion();
+ LLParcel *parcel = NULL;
+
+ if(gParcelMgr)
+ {
+ parcel = gParcelMgr->getAgentParcel();
+ }
+
+ if(region && parcel)
+ {
+ S32 parcelLocalID = parcel->getLocalID();
+ std::string regionName = region->getName();
+ std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
+
+// llinfos << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << llendl;
+
+ // The region name starts out empty and gets filled in later.
+ // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
+ // If either is empty, wait for the next time around.
+ if(!regionName.empty() && !capURI.empty())
+ {
+ if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName))
+ {
+ // We have changed parcels. Initiate a parcel channel lookup.
+ mCurrentParcelLocalID = parcelLocalID;
+ mCurrentRegionName = regionName;
+
+ parcelChanged();
+ }
+ }
+ }
+ }
+
+ switch(getState())
+ {
+ case stateDisabled:
+ if(mVoiceEnabled && (!mAccountName.empty() || mTuningMode))
+ {
+ setState(stateStart);
+ }
+ break;
+
+ case stateStart:
+ if(gDisableVoice)
+ {
+ // Voice is locked out, we must not launch the vivox daemon.
+ setState(stateJail);
+ }
+ else if(!isGatewayRunning())
+ {
+ if(true)
+ {
+ // Launch the voice daemon
+ std::string exe_path = gDirUtilp->getAppRODataDir();
+ exe_path += gDirUtilp->getDirDelimiter();
+#if LL_WINDOWS
+ exe_path += "SLVoice.exe";
+#else
+ // This will be the same for mac and linux
+ exe_path += "SLVoice";
+#endif
+ // See if the vivox executable exists
+ llstat s;
+ if(!LLFile::stat(exe_path.c_str(), &s))
+ {
+ // vivox executable exists. Build the command line and launch the daemon.
+ std::string args = " -p tcp -h -c";
+ std::string cmd;
+ std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
+
+ if(loglevel.empty())
+ {
+ loglevel = "-1"; // turn logging off completely
+ }
+
+ args += " -ll ";
+ args += loglevel;
+
+// llinfos << "Args for SLVoice: " << args << llendl;
+
+#if LL_WINDOWS
+ PROCESS_INFORMATION pinfo;
+ STARTUPINFOA sinfo;
+ memset(&sinfo, 0, sizeof(sinfo));
+ std::string exe_dir = gDirUtilp->getAppRODataDir();
+ cmd = "SLVoice.exe";
+ cmd += args;
+
+ // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
+ char *args2 = new char[args.size() + 1];
+ strcpy(args2, args.c_str());
+
+ if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo))
+ {
+// DWORD dwErr = GetLastError();
+ }
+ else
+ {
+ // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
+ // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
+ sGatewayHandle = pinfo.hProcess;
+ CloseHandle(pinfo.hThread); // stops leaks - nothing else
+ }
+
+ delete args2;
+#else // LL_WINDOWS
+ // This should be the same for mac and linux
+ {
+ std::vector<std::string> arglist;
+ arglist.push_back(exe_path.c_str());
+
+ // Split the argument string into separate strings for each argument
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(args, sep);
+ tokenizer::iterator token_iter;
+
+ for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ arglist.push_back(*token_iter);
+ }
+
+ // create an argv vector for the child process
+ char **fakeargv = new char*[arglist.size() + 1];
+ int i;
+ for(i=0; i < arglist.size(); i++)
+ fakeargv[i] = const_cast<char*>(arglist[i].c_str());
+
+ fakeargv[i] = NULL;
+
+ pid_t id = vfork();
+ if(id == 0)
+ {
+ // child
+ execv(exe_path.c_str(), fakeargv);
+
+ // If we reach this point, the exec failed.
+ // Use _exit() instead of exit() per the vfork man page.
+ _exit(0);
+ }
+
+ // parent
+ delete[] fakeargv;
+ sGatewayPID = id;
+ }
+#endif // LL_WINDOWS
+ mDaemonHost = LLHost("127.0.0.1", 44124);
+ }
+ else
+ {
+ llinfos << exe_path << "not found." << llendl
+ }
+ }
+ else
+ {
+ // We can connect to a client gateway running on another host. This is useful for testing.
+ // To do this, launch the gateway on a nearby host like this:
+ // vivox-gw.exe -p tcp -i 0.0.0.0:44124
+ // and put that host's IP address here.
+ mDaemonHost = LLHost("127.0.0.1", 44124);
+ }
+
+ mUpdateTimer.start();
+ mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+
+ setState(stateDaemonLaunched);
+
+ // Dirty the states we'll need to sync with the daemon when it comes up.
+ mPTTDirty = true;
+ mSpeakerVolumeDirty = true;
+ // These only need to be set if they're not default (i.e. empty string).
+ mCaptureDeviceDirty = !mCaptureDevice.empty();
+ mRenderDeviceDirty = !mRenderDevice.empty();
+ }
+ break;
+
+ case stateDaemonLaunched:
+// llinfos << "Connecting to vivox daemon" << llendl;
+ if(mUpdateTimer.hasExpired())
+ {
+ mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+
+ if(!mSocket)
+ {
+ mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+ }
+
+ mConnected = mSocket->blockingConnect(mDaemonHost);
+ if(mConnected)
+ {
+ setState(stateConnecting);
+ }
+ else
+ {
+ // If the connect failed, the socket may have been put into a bad state. Delete it.
+ closeSocket();
+ }
+ }
+ break;
+
+ case stateConnecting:
+ // Can't do this until we have the pump available.
+ if(mPump)
+ {
+ // MBW -- Note to self: pumps and pipes examples in
+ // indra/test/io.cpp
+ // indra/test/llpipeutil.{cpp|h}
+
+ // Attach the pumps and pipes
+
+ LLPumpIO::chain_t readChain;
+
+ readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
+ readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
+
+ mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
+
+ setState(stateIdle);
+ }
+
+ break;
+
+ case stateIdle:
+ // Initial devices query
+ getCaptureDevicesSendMessage();
+ getRenderDevicesSendMessage();
+
+ mLoginRetryCount = 0;
+
+ setState(stateConnectorStart);
+
+ break;
+
+ case stateConnectorStart:
+ if(!mVoiceEnabled)
+ {
+ // We were never logged in. This will shut down the connector.
+ setState(stateLoggedOut);
+ }
+ else if(!mAccountServerURI.empty())
+ {
+ connectorCreate();
+ }
+ else if(mTuningMode)
+ {
+ setState(stateMicTuningNoLogin);
+ }
+ break;
+
+ case stateConnectorStarting: // waiting for connector handle
+ // connectorCreateResponse() will transition from here to stateConnectorStarted.
+ break;
+
+ case stateConnectorStarted: // connector handle received
+ if(!mVoiceEnabled)
+ {
+ // We were never logged in. This will shut down the connector.
+ setState(stateLoggedOut);
+ }
+ else if(!mAccountName.empty())
+ {
+ LLViewerRegion *region = gAgent.getRegion();
+
+ if(region)
+ {
+ if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
+ {
+ if ( mAccountPassword.empty() )
+ {
+ requestVoiceAccountProvision();
+ }
+ setState(stateNeedsLogin);
+ }
+ }
+ }
+ break;
+
+ case stateMicTuningNoLogin:
+ case stateMicTuningLoggedIn:
+ {
+ // Both of these behave essentially the same. The only difference is where the exit transition goes to.
+ if(mTuningMode && mVoiceEnabled && !mSessionTerminateRequested)
+ {
+ if(!mTuningCaptureRunning)
+ {
+ // duration parameter is currently unused, per Mike S.
+ tuningCaptureStartSendMessage(10000);
+ }
+
+ if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty || mCaptureDeviceDirty || mRenderDeviceDirty)
+ {
+ std::ostringstream stream;
+
+ if(mTuningMicVolumeDirty)
+ {
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
+ << "<Level>" << mTuningMicVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mTuningSpeakerVolumeDirty)
+ {
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
+ << "<Level>" << mTuningSpeakerVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mCaptureDeviceDirty)
+ {
+ buildSetCaptureDevice(stream);
+ }
+
+ if(mRenderDeviceDirty)
+ {
+ buildSetRenderDevice(stream);
+ }
+
+ mTuningMicVolumeDirty = false;
+ mTuningSpeakerVolumeDirty = false;
+ mCaptureDeviceDirty = false;
+ mRenderDeviceDirty = false;
+
+ if(!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+ }
+ }
+ else
+ {
+ // transition out of mic tuning
+ if(mTuningCaptureRunning)
+ {
+ tuningCaptureStopSendMessage();
+ }
+
+ if(getState() == stateMicTuningNoLogin)
+ {
+ setState(stateConnectorStart);
+ }
+ else
+ {
+ setState(stateNoChannel);
+ }
+ }
+ }
+ break;
+
+ case stateLoginRetry:
+ if(mLoginRetryCount == 0)
+ {
+ // First retry -- display a message to the user
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
+ }
+
+ mLoginRetryCount++;
+
+ if(mLoginRetryCount > MAX_LOGIN_RETRIES)
+ {
+ llinfos << "too many login retries, giving up." << llendl;
+ setState(stateLoginFailed);
+ }
+ else
+ {
+ llinfos << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << llendl;
+ mUpdateTimer.start();
+ mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
+ setState(stateLoginRetryWait);
+ }
+ break;
+
+ case stateLoginRetryWait:
+ if(mUpdateTimer.hasExpired())
+ {
+ setState(stateNeedsLogin);
+ }
+ break;
+
+ case stateNeedsLogin:
+ if(!mAccountPassword.empty())
+ {
+ setState(stateLoggingIn);
+ loginSendMessage();
+ }
+ break;
+
+ case stateLoggingIn: // waiting for account handle
+ // loginResponse() will transition from here to stateLoggedIn.
+ break;
+
+ case stateLoggedIn: // account handle received
+ // Initial kick-off of channel lookup logic
+ parcelChanged();
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+
+ // Set up the mute list observer if it hasn't been set up already.
+ if((!sMuteListListener_listening) && (gMuteListp))
+ {
+ gMuteListp->addObserver(&mutelist_listener);
+ sMuteListListener_listening = true;
+ }
+
+ setState(stateNoChannel);
+ break;
+
+ case stateNoChannel:
+ if(mSessionTerminateRequested || !mVoiceEnabled)
+ {
+ // MBW -- XXX -- Is this the right way out of this state?
+ setState(stateSessionTerminated);
+ }
+ else if(mTuningMode)
+ {
+ setState(stateMicTuningLoggedIn);
+ }
+ else if(!mNextSessionHandle.empty())
+ {
+ setState(stateSessionConnect);
+ }
+ else if(!mNextSessionURI.empty())
+ {
+ setState(stateSessionCreate);
+ }
+ break;
+
+ case stateSessionCreate:
+ sessionCreateSendMessage();
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+ setState(stateJoiningSession);
+ break;
+
+ case stateSessionConnect:
+ sessionConnectSendMessage();
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+ setState(stateJoiningSession);
+ break;
+
+ case stateJoiningSession: // waiting for session handle
+ // sessionCreateResponse() will transition from here to stateSessionJoined.
+ if(!mVoiceEnabled)
+ {
+ // User bailed out during connect -- jump straight to teardown.
+ setState(stateSessionTerminated);
+ }
+ else if(mSessionTerminateRequested)
+ {
+ if(!mSessionHandle.empty())
+ {
+ // Only allow direct exits from this state in p2p calls (for cancelling an invite).
+ // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+ if(mSessionP2P)
+ {
+ sessionTerminateSendMessage();
+ setState(stateSessionTerminated);
+ }
+ }
+ }
+ break;
+
+ case stateSessionJoined: // session handle received
+ // MBW -- XXX -- It appears that I need to wait for BOTH the Session.Create response and the SessionStateChangeEvent with state 4
+ // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
+ // For now, the Session.Create response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
+ // This is a cheap way to make sure both have happened before proceeding.
+ if(!mSessionHandle.empty())
+ {
+ // Events that need to happen when a session is joined could go here.
+ // Maybe send initial spatial data?
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+
+ // Dirty state that may need to be sync'ed with the daemon.
+ mPTTDirty = true;
+ mSpeakerVolumeDirty = true;
+ mSpatialCoordsDirty = true;
+
+ setState(stateRunning);
+
+ // Start the throttle timer
+ mUpdateTimer.start();
+ mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+ }
+ else if(!mVoiceEnabled)
+ {
+ // User bailed out during connect -- jump straight to teardown.
+ setState(stateSessionTerminated);
+ }
+ else if(mSessionTerminateRequested)
+ {
+ // Only allow direct exits from this state in p2p calls (for cancelling an invite).
+ // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+ if(mSessionP2P)
+ {
+ sessionTerminateSendMessage();
+ setState(stateSessionTerminated);
+ }
+ }
+ break;
+
+ case stateRunning: // steady state
+ // sessionTerminateSendMessage() will transition from here to stateLeavingSession
+
+ // Disabling voice or disconnect requested.
+ if(!mVoiceEnabled || mSessionTerminateRequested)
+ {
+ sessionTerminateSendMessage();
+ }
+ else
+ {
+
+ // Figure out whether the PTT state needs to change
+ {
+ bool newPTT;
+ if(mUsePTT)
+ {
+ // If configured to use PTT, track the user state.
+ newPTT = mUserPTTState;
+ }
+ else
+ {
+ // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak).
+ newPTT = true;
+ }
+
+ if(mMuteMic)
+ {
+ // This always overrides any other PTT setting.
+ newPTT = false;
+ }
+
+ // Dirty if state changed.
+ if(newPTT != mPTT)
+ {
+ mPTT = newPTT;
+ mPTTDirty = true;
+ }
+ }
+
+ if(mNonSpatialChannel)
+ {
+ // When in a non-spatial channel, never send positional updates.
+ mSpatialCoordsDirty = false;
+ }
+ else
+ {
+ // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
+ enforceTether();
+ }
+
+ // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
+ // or every 10hz, whichever is sooner.
+ if(mVolumeDirty || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
+ {
+ mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+ sendPositionalUpdate();
+ }
+ }
+ break;
+
+ case stateLeavingSession: // waiting for terminate session response
+ // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
+ break;
+
+ case stateSessionTerminated:
+ // Always reset the terminate request flag when we get here.
+ mSessionTerminateRequested = false;
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+
+ if(mVoiceEnabled)
+ {
+ // SPECIAL CASE: if going back to spatial but in a parcel with an empty URI, transfer the non-spatial flag now.
+ // This fixes the case where you come out of a group chat in a parcel with voice disabled, and get stuck unable to rejoin spatial chat thereafter.
+ if(mNextSessionSpatial && mNextSessionURI.empty())
+ {
+ mNonSpatialChannel = !mNextSessionSpatial;
+ }
+
+ // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
+ setState(stateNoChannel);
+ }
+ else
+ {
+ // Shutting down voice, continue with disconnecting.
+ logout();
+ }
+
+ break;
+
+ case stateLoggingOut: // waiting for logout response
+ // The handler for the Account.Logout response will transition from here to stateLoggedOut.
+ break;
+ case stateLoggedOut: // logout response received
+ // shut down the connector
+ connectorShutdown();
+ break;
+
+ case stateConnectorStopping: // waiting for connector stop
+ // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
+ break;
+
+ case stateConnectorStopped: // connector stop received
+ // Clean up and reset everything.
+ closeSocket();
+ removeAllParticipants();
+ setState(stateDisabled);
+ break;
+
+ case stateConnectorFailed:
+ setState(stateConnectorFailedWaiting);
+ break;
+ case stateConnectorFailedWaiting:
+ break;
+
+ case stateLoginFailed:
+ setState(stateLoginFailedWaiting);
+ break;
+ case stateLoginFailedWaiting:
+ // No way to recover from these. Yet.
+ break;
+
+ case stateJoinSessionFailed:
+ // Transition to error state. Send out any notifications here.
+ llwarns << "stateJoinSessionFailed: (" << mVivoxErrorStatusCode << "): " << mVivoxErrorStatusString << llendl;
+ notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
+ setState(stateJoinSessionFailedWaiting);
+ break;
+
+ case stateJoinSessionFailedWaiting:
+ // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
+ // Region crossings may leave this state and try the join again.
+ if(mSessionTerminateRequested)
+ {
+ setState(stateSessionTerminated);
+ }
+ break;
+
+ case stateJail:
+ // We have given up. Do nothing.
+ break;
+ }
+
+ if(mParticipantMapChanged)
+ {
+ mParticipantMapChanged = false;
+ notifyObservers();
+ }
+
+}
+
+void LLVoiceClient::closeSocket(void)
+{
+ mSocket.reset();
+ mConnected = false;
+}
+
+void LLVoiceClient::loginSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<AccountName>" << mAccountName << "</AccountName>"
+ << "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
+ << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::logout()
+{
+ mAccountPassword = "";
+ setState(stateLoggingOut);
+ logoutSendMessage();
+}
+
+void LLVoiceClient::logoutSendMessage()
+{
+ if(!mAccountHandle.empty())
+ {
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">"
+ << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+ << "</Request>"
+ << "\n\n\n";
+
+ mAccountHandle.clear();
+
+ writeString(stream.str());
+ }
+}
+
+void LLVoiceClient::channelGetListSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ChannelGetList.1\">"
+ << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::sessionCreateSendMessage()
+{
+ llinfos << "requesting join: " << mNextSessionURI << llendl;
+
+ mSessionURI = mNextSessionURI;
+ mNonSpatialChannel = !mNextSessionSpatial;
+ mSessionResetOnClose = mNextSessionResetOnClose;
+ mNextSessionResetOnClose = false;
+ if(mNextSessionNoReconnect)
+ {
+ // Clear the stashed URI so it can't reconnect
+ mNextSessionURI.clear();
+ }
+ // Only p2p sessions are created with "no reconnect".
+ mSessionP2P = mNextSessionNoReconnect;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Create.1\">"
+ << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+ << "<URI>" << mSessionURI << "</URI>";
+
+ static const std::string allowed_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-._~";
+
+ if(!mNextSessionHash.empty())
+ {
+ stream
+ << "<Password>" << LLURI::escape(mNextSessionHash, allowed_chars) << "</Password>"
+ << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
+ }
+
+ stream
+ << "<Name>" << mChannelName << "</Name>"
+ << "</Request>\n\n\n";
+ writeString(stream.str());
+}
+
+void LLVoiceClient::sessionConnectSendMessage()
+{
+ llinfos << "connecting to session handle: " << mNextSessionHandle << llendl;
+
+ mSessionHandle = mNextSessionHandle;
+ mSessionURI = mNextP2PSessionURI;
+ mNextSessionHandle.clear(); // never want to re-use these.
+ mNextP2PSessionURI.clear();
+ mNonSpatialChannel = !mNextSessionSpatial;
+ mSessionResetOnClose = mNextSessionResetOnClose;
+ mNextSessionResetOnClose = false;
+ // Joining by session ID is only used to answer p2p invitations, so we know this is a p2p session.
+ mSessionP2P = true;
+
+ std::ostringstream stream;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Connect.1\">"
+ << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
+ << "<AudioMedia>default</AudioMedia>"
+ << "</Request>\n\n\n";
+ writeString(stream.str());
+}
+
+void LLVoiceClient::sessionTerminate()
+{
+ mSessionTerminateRequested = true;
+}
+
+void LLVoiceClient::sessionTerminateSendMessage()
+{
+ llinfos << "leaving session: " << mSessionURI << llendl;
+
+ switch(getState())
+ {
+ case stateNoChannel:
+ // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
+ // Skip the join failed transition state so we don't send out error notifications.
+ setState(stateJoinSessionFailedWaiting);
+ break;
+ case stateJoiningSession:
+ case stateSessionJoined:
+ case stateRunning:
+ if(!mSessionHandle.empty())
+ {
+ sessionTerminateByHandle(mSessionHandle);
+ setState(stateLeavingSession);
+ }
+ else
+ {
+ llwarns << "called with no session handle" << llendl;
+ setState(stateSessionTerminated);
+ }
+ break;
+ case stateJoinSessionFailed:
+ case stateJoinSessionFailedWaiting:
+ setState(stateSessionTerminated);
+ break;
+
+ default:
+ llwarns << "called from unknown state" << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::sessionTerminateByHandle(std::string &sessionHandle)
+{
+ llinfos << "Sending Session.Terminate with handle " << sessionHandle << llendl;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
+ << "<SessionHandle>" << sessionHandle << "</SessionHandle>"
+ << "</Request>"
+ << "\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::getCaptureDevicesSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::getRenderDevicesSendMessage()
+{
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::clearCaptureDevices()
+{
+ // MBW -- XXX -- do something here
+ llinfos << "called" << llendl;
+ mCaptureDevices.clear();
+}
+
+void LLVoiceClient::addCaptureDevice(const std::string& name)
+{
+ // MBW -- XXX -- do something here
+ llinfos << name << llendl;
+
+ mCaptureDevices.push_back(name);
+}
+
+LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices()
+{
+ return &mCaptureDevices;
+}
+
+void LLVoiceClient::setCaptureDevice(const std::string& name)
+{
+ if(name == "Default")
+ {
+ if(!mCaptureDevice.empty())
+ {
+ mCaptureDevice.clear();
+ mCaptureDeviceDirty = true;
+ }
+ }
+ else
+ {
+ if(mCaptureDevice != name)
+ {
+ mCaptureDevice = name;
+ mCaptureDeviceDirty = true;
+ }
+ }
+}
+
+void LLVoiceClient::clearRenderDevices()
+{
+ // MBW -- XXX -- do something here
+ llinfos << "called" << llendl;
+ mRenderDevices.clear();
+}
+
+void LLVoiceClient::addRenderDevice(const std::string& name)
+{
+ // MBW -- XXX -- do something here
+ llinfos << name << llendl;
+ mRenderDevices.push_back(name);
+}
+
+LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices()
+{
+ return &mRenderDevices;
+}
+
+void LLVoiceClient::setRenderDevice(const std::string& name)
+{
+ if(name == "Default")
+ {
+ if(!mRenderDevice.empty())
+ {
+ mRenderDevice.clear();
+ mRenderDeviceDirty = true;
+ }
+ }
+ else
+ {
+ if(mRenderDevice != name)
+ {
+ mRenderDevice = name;
+ mRenderDeviceDirty = true;
+ }
+ }
+
+}
+
+void LLVoiceClient::tuningStart()
+{
+ mTuningMode = true;
+ if(getState() >= stateNoChannel)
+ {
+ sessionTerminate();
+ }
+}
+
+void LLVoiceClient::tuningStop()
+{
+ mTuningMode = false;
+}
+
+bool LLVoiceClient::inTuningMode()
+{
+ bool result = false;
+ switch(getState())
+ {
+ case stateMicTuningNoLogin:
+ case stateMicTuningLoggedIn:
+ result = true;
+ default:
+ break;
+ }
+ return result;
+}
+
+void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
+{
+ if(!inTuningMode())
+ return;
+
+ mTuningAudioFile = name;
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">"
+ << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
+ << "<Loop>" << (loop?"1":"0") << "</Loop>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::tuningRenderStopSendMessage()
+{
+ if(!inTuningMode())
+ return;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">"
+ << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+}
+
+void LLVoiceClient::tuningCaptureStartSendMessage(int duration)
+{
+ if(!inTuningMode())
+ return;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
+ << "<Duration>" << duration << "</Duration>"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+
+ mTuningCaptureRunning = true;
+}
+
+void LLVoiceClient::tuningCaptureStopSendMessage()
+{
+ if(!inTuningMode())
+ return;
+
+ std::ostringstream stream;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">"
+ << "</Request>\n\n\n";
+
+ writeString(stream.str());
+
+ mTuningCaptureRunning = false;
+}
+
+void LLVoiceClient::tuningSetMicVolume(float volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mTuningMicVolume)
+ {
+ mTuningMicVolume = scaledVolume;
+ mTuningMicVolumeDirty = true;
+ }
+}
+
+void LLVoiceClient::tuningSetSpeakerVolume(float volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mTuningSpeakerVolume)
+ {
+ mTuningSpeakerVolume = ((int)(volume * 100.0f)) - 100;
+ mTuningSpeakerVolumeDirty = true;
+ }
+}
+
+float LLVoiceClient::tuningGetEnergy(void)
+{
+ return mTuningEnergy;
+}
+
+bool LLVoiceClient::deviceSettingsAvailable()
+{
+ bool result = true;
+
+ if(!mConnected)
+ result = false;
+
+ if(mRenderDevices.empty())
+ result = false;
+
+ return result;
+}
+
+void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
+{
+ if(clearCurrentList)
+ {
+ clearCaptureDevices();
+ clearRenderDevices();
+ }
+ getCaptureDevicesSendMessage();
+ getRenderDevicesSendMessage();
+}
+
+void LLVoiceClient::daemonDied()
+{
+ // The daemon died, so the connection is gone. Reset everything and start over.
+ llwarns << "Connection to vivox daemon lost. Resetting state."<< llendl;
+
+ closeSocket();
+ removeAllParticipants();
+
+ // Try to relaunch the daemon
+ setState(stateDisabled);
+}
+
+void LLVoiceClient::giveUp()
+{
+ // All has failed. Clean up and stop trying.
+ closeSocket();
+ removeAllParticipants();
+
+ setState(stateJail);
+}
+
+void LLVoiceClient::sendPositionalUpdate(void)
+{
+ std::ostringstream stream;
+
+ if(mSpatialCoordsDirty)
+ {
+ LLVector3 l, u, a;
+
+ // Always send both speaker and listener positions together.
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"
+ << "<SessionHandle>" << mSessionHandle << "</SessionHandle>";
+
+ stream << "<SpeakerPosition>";
+
+ l = mAvatarRot.getLeftRow();
+ u = mAvatarRot.getUpRow();
+ a = mAvatarRot.getFwdRow();
+
+// llinfos << "Sending speaker position " << mSpeakerPosition << llendl;
+
+ stream
+ << "<Position>"
+ << "<X>" << mAvatarPosition[VX] << "</X>"
+ << "<Y>" << mAvatarPosition[VZ] << "</Y>"
+ << "<Z>" << mAvatarPosition[VY] << "</Z>"
+ << "</Position>"
+ << "<Velocity>"
+ << "<X>" << mAvatarVelocity[VX] << "</X>"
+ << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
+ << "<Z>" << mAvatarVelocity[VY] << "</Z>"
+ << "</Velocity>"
+ << "<AtOrientation>"
+ << "<X>" << l.mV[VX] << "</X>"
+ << "<Y>" << u.mV[VX] << "</Y>"
+ << "<Z>" << a.mV[VX] << "</Z>"
+ << "</AtOrientation>"
+ << "<UpOrientation>"
+ << "<X>" << l.mV[VZ] << "</X>"
+ << "<Y>" << u.mV[VY] << "</Y>"
+ << "<Z>" << a.mV[VZ] << "</Z>"
+ << "</UpOrientation>"
+ << "<LeftOrientation>"
+ << "<X>" << l.mV [VY] << "</X>"
+ << "<Y>" << u.mV [VZ] << "</Y>"
+ << "<Z>" << a.mV [VY] << "</Z>"
+ << "</LeftOrientation>";
+
+ stream << "</SpeakerPosition>";
+
+ stream << "<ListenerPosition>";
+
+ LLVector3d earPosition;
+ LLVector3 earVelocity;
+ LLMatrix3 earRot;
+
+ switch(mEarLocation)
+ {
+ case earLocCamera:
+ default:
+ earPosition = mCameraPosition;
+ earVelocity = mCameraVelocity;
+ earRot = mCameraRot;
+ break;
+
+ case earLocAvatar:
+ earPosition = mAvatarPosition;
+ earVelocity = mAvatarVelocity;
+ earRot = mAvatarRot;
+ break;
+
+ case earLocMixed:
+ earPosition = mAvatarPosition;
+ earVelocity = mAvatarVelocity;
+ earRot = mCameraRot;
+ break;
+ }
+
+ l = earRot.getLeftRow();
+ u = earRot.getUpRow();
+ a = earRot.getFwdRow();
+
+// llinfos << "Sending listener position " << mListenerPosition << llendl;
+
+ stream
+ << "<Position>"
+ << "<X>" << earPosition[VX] << "</X>"
+ << "<Y>" << earPosition[VZ] << "</Y>"
+ << "<Z>" << earPosition[VY] << "</Z>"
+ << "</Position>"
+ << "<Velocity>"
+ << "<X>" << earVelocity[VX] << "</X>"
+ << "<Y>" << earVelocity[VZ] << "</Y>"
+ << "<Z>" << earVelocity[VY] << "</Z>"
+ << "</Velocity>"
+ << "<AtOrientation>"
+ << "<X>" << l.mV[VX] << "</X>"
+ << "<Y>" << u.mV[VX] << "</Y>"
+ << "<Z>" << a.mV[VX] << "</Z>"
+ << "</AtOrientation>"
+ << "<UpOrientation>"
+ << "<X>" << l.mV[VZ] << "</X>"
+ << "<Y>" << u.mV[VY] << "</Y>"
+ << "<Z>" << a.mV[VZ] << "</Z>"
+ << "</UpOrientation>"
+ << "<LeftOrientation>"
+ << "<X>" << l.mV [VY] << "</X>"
+ << "<Y>" << u.mV [VZ] << "</Y>"
+ << "<Z>" << a.mV [VY] << "</Z>"
+ << "</LeftOrientation>";
+
+ stream << "</ListenerPosition>";
+
+ stream << "</Request>\n\n\n";
+ }
+
+ if(mPTTDirty)
+ {
+ // Send a local mute command.
+ // NOTE that the state of "PTT" is the inverse of "local mute".
+ // (i.e. when PTT is true, we send a mute command with "false", and vice versa)
+
+// llinfos << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << (mPTT?"false":"true") << "</Value>"
+ << "</Request>\n\n\n";
+
+ }
+
+ if(mVolumeDirty)
+ {
+ participantMap::iterator iter = mParticipantMap.begin();
+
+ for(; iter != mParticipantMap.end(); iter++)
+ {
+ participantState *p = iter->second;
+
+ if(p->mVolumeDirty)
+ {
+ int volume = p->mOnMuteList?0:p->mUserVolume;
+
+ llinfos << "Setting volume for avatar " << p->mAvatarID << " to " << volume << llendl;
+
+ // Send a mute/unumte command for the user (actually "volume for me").
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
+ << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
+ << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
+ << "<Volume>" << volume << "</Volume>"
+ << "</Request>\n\n\n";
+
+ p->mVolumeDirty = false;
+ }
+ }
+ }
+
+ if(mSpeakerMuteDirty)
+ {
+ const char *muteval = ((mSpeakerVolume == -100)?"true":"false");
+ llinfos << "Setting speaker mute to " << muteval << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << muteval << "</Value>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mSpeakerVolumeDirty)
+ {
+ llinfos << "Setting speaker volume to " << mSpeakerVolume << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << mSpeakerVolume << "</Value>"
+ << "</Request>\n\n\n";
+ }
+
+ if(mMicVolumeDirty)
+ {
+ llinfos << "Setting mic volume to " << mMicVolume << llendl;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>" << mMicVolume << "</Value>"
+ << "</Request>\n\n\n";
+ }
+
+
+ // MBW -- XXX -- Maybe check to make sure the capture/render devices are in the current list here?
+ if(mCaptureDeviceDirty)
+ {
+ buildSetCaptureDevice(stream);
+ }
+
+ if(mRenderDeviceDirty)
+ {
+ buildSetRenderDevice(stream);
+ }
+
+ mSpatialCoordsDirty = false;
+ mPTTDirty = false;
+ mVolumeDirty = false;
+ mSpeakerVolumeDirty = false;
+ mMicVolumeDirty = false;
+ mSpeakerMuteDirty = false;
+ mCaptureDeviceDirty = false;
+ mRenderDeviceDirty = false;
+
+ if(!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+}
+
+void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
+{
+ llinfos << "Setting input device = \"" << mCaptureDevice << "\"" << llendl;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
+ << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
+ << "</Request>"
+ << "\n\n\n";
+}
+
+void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
+{
+ llinfos << "Setting output device = \"" << mRenderDevice << "\"" << llendl;
+
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
+ << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
+ << "</Request>"
+ << "\n\n\n";
+}
+
+/////////////////////////////
+// Response/Event handlers
+
+void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Connector.Create response failure: " << statusString << llendl;
+ setState(stateConnectorFailed);
+ }
+ else
+ {
+ // Connector created, move forward.
+ mConnectorHandle = connectorHandle;
+ if(getState() == stateConnectorStarting)
+ {
+ setState(stateConnectorStarted);
+ }
+ }
+}
+
+void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle)
+{
+ llinfos << "Account.Login response (" << statusCode << "): " << statusString << llendl;
+
+ // Status code of 20200 means "bad password". We may want to special-case that at some point.
+
+ if ( statusCode == 401 )
+ {
+ // Login failure which is probably caused by the delay after a user's password being updated.
+ llinfos << "Account.Login response failure (" << statusCode << "): " << statusString << llendl;
+ setState(stateLoginRetry);
+ }
+ else if(statusCode != 0)
+ {
+ llwarns << "Account.Login response failure (" << statusCode << "): " << statusString << llendl;
+ setState(stateLoginFailed);
+ }
+ else
+ {
+ // Login succeeded, move forward.
+ mAccountHandle = accountHandle;
+ // MBW -- XXX -- This needs to wait until the LoginStateChangeEvent is received.
+// if(getState() == stateLoggingIn)
+// {
+// setState(stateLoggedIn);
+// }
+ }
+}
+
+void LLVoiceClient::channelGetListResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Account.ChannelGetList response failure: " << statusString << llendl;
+ switchChannel();
+ }
+ else
+ {
+ // Got the channel list, try to do a lookup.
+ std::string uri = findChannelURI(mChannelName);
+ if(uri.empty())
+ {
+ // Lookup failed, can't join a channel for this area.
+ llinfos << "failed to map channel name: " << mChannelName << llendl;
+ }
+ else
+ {
+ // We have a sip URL for this area.
+ llinfos << "mapped channel " << mChannelName << " to URI "<< uri << llendl;
+ }
+
+ // switchChannel with an empty uri string will do the right thing (leave channel and not rejoin)
+ switchChannel(uri);
+ }
+}
+
+void LLVoiceClient::sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Session.Create response failure (" << statusCode << "): " << statusString << llendl;
+// if(statusCode == 1015)
+// {
+// if(getState() == stateJoiningSession)
+// {
+// // this happened during a real join. Going to sessionTerminated should cause a retry in appropriate cases.
+// llwarns << "session handle \"" << sessionHandle << "\", mSessionStateEventHandle \"" << mSessionStateEventHandle << "\""<< llendl;
+// if(!sessionHandle.empty())
+// {
+// // This session is bad. Terminate it.
+// mSessionHandle = sessionHandle;
+// sessionTerminateByHandle(sessionHandle);
+// setState(stateLeavingSession);
+// }
+// else if(!mSessionStateEventHandle.empty())
+// {
+// mSessionHandle = mSessionStateEventHandle;
+// sessionTerminateByHandle(mSessionStateEventHandle);
+// setState(stateLeavingSession);
+// }
+// else
+// {
+// setState(stateSessionTerminated);
+// }
+// }
+// else
+// {
+// // We didn't think we were in the middle of a join. Don't change state.
+// llwarns << "Not in stateJoiningSession, ignoring" << llendl;
+// }
+// }
+// else
+ {
+ mVivoxErrorStatusCode = statusCode;
+ mVivoxErrorStatusString = statusString;
+ setState(stateJoinSessionFailed);
+ }
+ }
+ else
+ {
+ llinfos << "Session.Create response received (success), session handle is " << sessionHandle << llendl;
+ if(getState() == stateJoiningSession)
+ {
+ // This is also grabbed in the SessionStateChangeEvent handler, but it might be useful to have it early...
+ mSessionHandle = sessionHandle;
+ }
+ else
+ {
+ // We should never get a session.create response in any state except stateJoiningSession. Things are out of sync. Kill this session.
+ sessionTerminateByHandle(sessionHandle);
+ }
+ }
+}
+
+void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Session.Connect response failure (" << statusCode << "): " << statusString << llendl;
+// if(statusCode == 1015)
+// {
+// llwarns << "terminating existing session" << llendl;
+// sessionTerminate();
+// }
+// else
+ {
+ mVivoxErrorStatusCode = statusCode;
+ mVivoxErrorStatusString = statusString;
+ setState(stateJoinSessionFailed);
+ }
+ }
+ else
+ {
+ llinfos << "Session.Connect response received (success)" << llendl;
+ }
+}
+
+void LLVoiceClient::sessionTerminateResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Session.Terminate response failure: (" << statusCode << "): " << statusString << llendl;
+ if(getState() == stateLeavingSession)
+ {
+ // This is probably "(404): Server reporting Failure. Not a member of this conference."
+ // Do this so we don't get stuck.
+ setState(stateSessionTerminated);
+ }
+ }
+
+}
+
+void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Account.Logout response failure: " << statusString << llendl;
+ // MBW -- XXX -- Should this ever fail? do we care if it does?
+ }
+
+ if(getState() == stateLoggingOut)
+ {
+ setState(stateLoggedOut);
+ }
+}
+
+void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
+{
+ if(statusCode != 0)
+ {
+ llwarns << "Connector.InitiateShutdown response failure: " << statusString << llendl;
+ // MBW -- XXX -- Should this ever fail? do we care if it does?
+ }
+
+ mConnected = false;
+
+ if(getState() == stateConnectorStopping)
+ {
+ setState(stateConnectorStopped);
+ }
+}
+
+void LLVoiceClient::sessionStateChangeEvent(
+ std::string &uriString,
+ int statusCode,
+ std::string &statusString,
+ std::string &sessionHandle,
+ int state,
+ bool isChannel,
+ std::string &nameString)
+{
+ switch(state)
+ {
+ case 4: // I see this when joining the session
+ llinfos << "joined session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << llendl;
+
+ // Session create succeeded, move forward.
+ mSessionStateEventHandle = sessionHandle;
+ mSessionStateEventURI = uriString;
+ if(sessionHandle == mSessionHandle)
+ {
+ // This is the session we're joining.
+ if(getState() == stateJoiningSession)
+ {
+ setState(stateSessionJoined);
+ //RN: the uriString being returned by vivox here is actually your account uri, not the channel
+ // you are attempting to join, so ignore it
+ //llinfos << "received URI " << uriString << "(previously " << mSessionURI << ")" << llendl;
+ //mSessionURI = uriString;
+ }
+ }
+ else if(sessionHandle == mNextSessionHandle)
+ {
+// llinfos << "received URI " << uriString << ", name " << nameString << " for next session (handle " << mNextSessionHandle << ")" << llendl;
+ }
+ else
+ {
+ llwarns << "joining unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << llendl;
+ // MBW -- XXX -- Should we send a Session.Terminate here?
+ }
+
+ break;
+ case 5: // I see this when leaving the session
+ llinfos << "left session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << llendl;
+
+ // Set the session handle to the empty string. If we get back to stateJoiningSession, we'll want to wait for the new session handle.
+ if(sessionHandle == mSessionHandle)
+ {
+ // MBW -- XXX -- I think this is no longer necessary, now that we've got mNextSessionURI/mNextSessionHandle
+ // mSessionURI.clear();
+ // clear the session handle here just for sanity.
+ mSessionHandle.clear();
+ if(mSessionResetOnClose)
+ {
+ mSessionResetOnClose = false;
+ mNonSpatialChannel = false;
+ mNextSessionSpatial = true;
+ parcelChanged();
+ }
+
+ removeAllParticipants();
+
+ switch(getState())
+ {
+ case stateJoiningSession:
+ case stateSessionJoined:
+ case stateRunning:
+ case stateLeavingSession:
+ case stateJoinSessionFailed:
+ case stateJoinSessionFailedWaiting:
+ // normal transition
+ llinfos << "left session " << sessionHandle << "in state " << state2string(getState()) << llendl;
+ setState(stateSessionTerminated);
+ break;
+
+ case stateSessionTerminated:
+ // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
+ llwarns << "left session " << sessionHandle << "in state " << state2string(getState()) << llendl;
+ break;
+
+ default:
+ llwarns << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << llendl;
+ setState(stateSessionTerminated);
+ break;
+ }
+
+ // store status values for later notification of observers
+ mVivoxErrorStatusCode = statusCode;
+ mVivoxErrorStatusString = statusString;
+ }
+ else
+ {
+ llinfos << "leaving unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << llendl;
+ }
+
+ mSessionStateEventHandle.clear();
+ mSessionStateEventURI.clear();
+ break;
+ default:
+ llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::loginStateChangeEvent(
+ std::string &accountHandle,
+ int statusCode,
+ std::string &statusString,
+ int state)
+{
+ llinfos << "state is " << state << llendl;
+ /*
+ According to Mike S., status codes for this event are:
+ login_state_logged_out=0,
+ login_state_logged_in = 1,
+ login_state_logging_in = 2,
+ login_state_logging_out = 3,
+ login_state_resetting = 4,
+ login_state_error=100
+ */
+
+ switch(state)
+ {
+ case 1:
+ if(getState() == stateLoggingIn)
+ {
+ setState(stateLoggedIn);
+ }
+ break;
+
+ default:
+// llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::sessionNewEvent(
+ std::string &accountHandle,
+ std::string &eventSessionHandle,
+ int state,
+ std::string &nameString,
+ std::string &uriString)
+{
+// llinfos << "state is " << state << llendl;
+
+ switch(state)
+ {
+ case 0:
+ {
+ llinfos << "session handle = " << eventSessionHandle << ", name = " << nameString << ", uri = " << uriString << llendl;
+
+ LLUUID caller_id;
+ if(IDFromName(nameString, caller_id))
+ {
+ gIMMgr->inviteToSession(LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, caller_id),
+ LLString::null,
+ caller_id,
+ LLString::null,
+ IM_SESSION_P2P_INVITE,
+ eventSessionHandle);
+ }
+ else
+ {
+ llwarns << "Could not generate caller id from uri " << uriString << llendl;
+ }
+ }
+ break;
+
+ default:
+ llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::participantStateChangeEvent(
+ std::string &uriString,
+ int statusCode,
+ std::string &statusString,
+ int state,
+ std::string &nameString,
+ std::string &displayNameString,
+ int participantType)
+{
+ participantState *participant = NULL;
+ llinfos << "state is " << state << llendl;
+
+ switch(state)
+ {
+ case 7: // I see this when a participant joins
+ participant = addParticipant(uriString);
+ if(participant)
+ {
+ participant->mName = nameString;
+ llinfos << "added participant \"" << participant->mName
+ << "\" (" << participant->mAvatarID << ")"<< llendl;
+ }
+ break;
+ case 9: // I see this when a participant leaves
+ participant = findParticipant(uriString);
+ if(participant)
+ {
+ removeParticipant(participant);
+ }
+ break;
+ default:
+// llwarns << "unknown state: " << state << llendl;
+ break;
+ }
+}
+
+void LLVoiceClient::participantPropertiesEvent(
+ std::string &uriString,
+ int statusCode,
+ std::string &statusString,
+ bool isLocallyMuted,
+ bool isModeratorMuted,
+ bool isSpeaking,
+ int volume,
+ F32 energy)
+{
+ participantState *participant = findParticipant(uriString);
+ if(participant)
+ {
+ participant->mPTT = !isLocallyMuted;
+ participant->mIsSpeaking = isSpeaking;
+ if (isSpeaking)
+ {
+ participant->mSpeakingTimeout.reset();
+ }
+ participant->mPower = energy;
+ participant->mVolume = volume;
+ }
+ else
+ {
+ llwarns << "unknown participant: " << uriString << llendl;
+ }
+}
+
+void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
+{
+// llinfos << "got energy " << energy << llendl;
+ mTuningEnergy = energy;
+}
+
+void LLVoiceClient::muteListChanged()
+{
+ // The user's mute list has been updated. Go through the current participant list and sync it with the mute list.
+
+ participantMap::iterator iter = mParticipantMap.begin();
+
+ for(; iter != mParticipantMap.end(); iter++)
+ {
+ participantState *p = iter->second;
+
+ // Check to see if this participant is on the mute list already
+ updateMuteState(p);
+ }
+}
+
+/////////////////////////////
+// Managing list of participants
+LLVoiceClient::participantState::participantState(const std::string &uri) :
+ mURI(uri), mPTT(false), mIsSpeaking(false), mPower(0.0), mServiceType(serviceTypeUnknown),
+ mOnMuteList(false), mUserVolume(100), mVolumeDirty(false), mAvatarIDValid(false)
+{
+}
+
+LLVoiceClient::participantState *LLVoiceClient::addParticipant(const std::string &uri)
+{
+ participantState *result = NULL;
+
+ participantMap::iterator iter = mParticipantMap.find(uri);
+
+ if(iter != mParticipantMap.end())
+ {
+ // Found a matching participant already in the map.
+ result = iter->second;
+ }
+
+ if(!result)
+ {
+ // participant isn't already in one list or the other.
+ result = new participantState(uri);
+ mParticipantMap.insert(participantMap::value_type(uri, result));
+ mParticipantMapChanged = true;
+
+ // Try to do a reverse transform on the URI to get the GUID back.
+ {
+ LLUUID id;
+ if(IDFromName(uri, id))
+ {
+ result->mAvatarIDValid = true;
+ result->mAvatarID = id;
+
+ updateMuteState(result);
+ }
+ }
+
+ llinfos << "participant \"" << result->mURI << "\" added." << llendl;
+ }
+
+ return result;
+}
+
+void LLVoiceClient::updateMuteState(participantState *p)
+{
+ if(p->mAvatarIDValid)
+ {
+ bool isMuted = gMuteListp->isMuted(p->mAvatarID, LLMute::flagVoiceChat);
+ if(p->mOnMuteList != isMuted)
+ {
+ p->mOnMuteList = isMuted;
+ p->mVolumeDirty = true;
+ mVolumeDirty = true;
+ }
+ }
+}
+
+void LLVoiceClient::removeParticipant(LLVoiceClient::participantState *participant)
+{
+ if(participant)
+ {
+ participantMap::iterator iter = mParticipantMap.find(participant->mURI);
+
+ llinfos << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << llendl;
+
+ mParticipantMap.erase(iter);
+ delete participant;
+ mParticipantMapChanged = true;
+ }
+}
+
+void LLVoiceClient::removeAllParticipants()
+{
+ llinfos << "called" << llendl;
+
+ while(!mParticipantMap.empty())
+ {
+ removeParticipant(mParticipantMap.begin()->second);
+ }
+}
+
+LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
+{
+ return &mParticipantMap;
+}
+
+
+LLVoiceClient::participantState *LLVoiceClient::findParticipant(const std::string &uri)
+{
+ participantState *result = NULL;
+
+ // Don't find any participants if we're not connected. This is so that we don't continue to get stale data
+ // after the daemon dies.
+ if(mConnected)
+ {
+ participantMap::iterator iter = mParticipantMap.find(uri);
+
+ if(iter != mParticipantMap.end())
+ {
+ result = iter->second;
+ }
+ }
+
+ return result;
+}
+
+
+LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvatar *avatar)
+{
+ participantState * result = NULL;
+
+ // You'd think this would work, but it doesn't...
+// std::string uri = sipURIFromAvatar(avatar);
+
+ // Currently, the URI is just the account name.
+ std::string loginName = nameFromAvatar(avatar);
+ result = findParticipant(loginName);
+
+ if(result != NULL)
+ {
+ if(!result->mAvatarIDValid)
+ {
+ result->mAvatarID = avatar->getID();
+ result->mAvatarIDValid = true;
+
+ // We just figured out the avatar ID, so the participant list has "changed" from the perspective of anyone who uses that to identify participants.
+ mParticipantMapChanged = true;
+
+ updateMuteState(result);
+ }
+
+
+ }
+
+ return result;
+}
+
+LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
+{
+ participantState * result = NULL;
+
+ // Currently, the URI is just the account name.
+ std::string loginName = nameFromID(id);
+ result = findParticipant(loginName);
+
+ return result;
+}
+
+
+void LLVoiceClient::clearChannelMap(void)
+{
+ mChannelMap.clear();
+}
+
+void LLVoiceClient::addChannelMapEntry(std::string &name, std::string &uri)
+{
+// llinfos << "Adding channel name mapping: " << name << " -> " << uri << llendl;
+ mChannelMap.insert(channelMap::value_type(name, uri));
+}
+
+std::string LLVoiceClient::findChannelURI(std::string &name)
+{
+ std::string result;
+
+ channelMap::iterator iter = mChannelMap.find(name);
+
+ if(iter != mChannelMap.end())
+ {
+ result = iter->second;
+ }
+
+ return result;
+}
+
+void LLVoiceClient::parcelChanged()
+{
+ if(getState() >= stateLoggedIn)
+ {
+ // If the user is logged in, start a channel lookup.
+ llinfos << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << llendl;
+
+ std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
+ LLSD data;
+ data["method"] = "call";
+ LLHTTPClient::post(
+ url,
+ data,
+ new LLVoiceClientCapResponder);
+ }
+ else
+ {
+ // The transition to stateLoggedIn needs to kick this off again.
+ llinfos << "not logged in yet, deferring" << llendl;
+ }
+}
+
+void LLVoiceClient::switchChannel(
+ std::string uri,
+ bool spatial,
+ bool noReconnect,
+ std::string hash)
+{
+ bool needsSwitch = false;
+
+ llinfos << "called in state " << state2string(getState()) << " with uri \"" << uri << "\"" << llendl;
+
+ switch(getState())
+ {
+ case stateJoinSessionFailed:
+ case stateJoinSessionFailedWaiting:
+ case stateNoChannel:
+ // Always switch to the new URI from these states.
+ needsSwitch = true;
+ break;
+
+ default:
+ if(mSessionTerminateRequested)
+ {
+ // If a terminate has been requested, we need to compare against where the URI we're already headed to.
+ if(mNextSessionURI != uri)
+ needsSwitch = true;
+ }
+ else
+ {
+ // Otherwise, compare against the URI we're in now.
+ if(mSessionURI != uri)
+ needsSwitch = true;
+ }
+ break;
+ }
+
+ if(needsSwitch)
+ {
+ mNextSessionURI = uri;
+ mNextSessionHash = hash;
+ mNextSessionHandle.clear();
+ mNextP2PSessionURI.clear();
+ mNextSessionSpatial = spatial;
+ mNextSessionNoReconnect = noReconnect;
+
+ if(uri.empty())
+ {
+ // Leave any channel we may be in
+ llinfos << "leaving channel" << llendl;
+ }
+ else
+ {
+ llinfos << "switching to channel " << uri << llendl;
+ }
+
+ if(getState() <= stateNoChannel)
+ {
+ // We're already set up to join a channel, just needed to fill in the session URI
+ }
+ else
+ {
+ // State machine will come around and rejoin if uri/handle is not empty.
+ sessionTerminate();
+ }
+ }
+}
+
+void LLVoiceClient::joinSession(std::string handle, std::string uri)
+{
+ mNextSessionURI.clear();
+ mNextSessionHash.clear();
+ mNextP2PSessionURI = uri;
+ mNextSessionHandle = handle;
+ mNextSessionSpatial = false;
+ mNextSessionNoReconnect = false;
+
+ if(getState() <= stateNoChannel)
+ {
+ // We're already set up to join a channel, just needed to fill in the session handle
+ }
+ else
+ {
+ // State machine will come around and rejoin if uri/handle is not empty.
+ sessionTerminate();
+ }
+}
+
+void LLVoiceClient::setNonSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials)
+{
+ switchChannel(uri, false, false, credentials);
+}
+
+void LLVoiceClient::setSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials)
+{
+ mSpatialSessionURI = uri;
+ mAreaVoiceDisabled = mSpatialSessionURI.empty();
+
+ llinfos << "got spatial channel uri: \"" << uri << "\"" << llendl;
+
+ if(mNonSpatialChannel || !mNextSessionSpatial)
+ {
+ // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels.
+ llinfos << "in non-spatial chat, not switching channels" << llendl;
+ }
+ else
+ {
+ switchChannel(mSpatialSessionURI, true, false, credentials);
+ }
+}
+
+void LLVoiceClient::callUser(LLUUID &uuid)
+{
+ std::string userURI = sipURIFromID(uuid);
+
+ switchChannel(userURI, false, true);
+}
+
+void LLVoiceClient::answerInvite(std::string &sessionHandle, LLUUID& other_user_id)
+{
+ joinSession(sessionHandle, sipURIFromID(other_user_id));
+}
+
+void LLVoiceClient::declineInvite(std::string &sessionHandle)
+{
+ sessionTerminateByHandle(sessionHandle);
+}
+
+void LLVoiceClient::leaveNonSpatialChannel()
+{
+ switchChannel(mSpatialSessionURI);
+}
+
+std::string LLVoiceClient::getCurrentChannel()
+{
+ if((getState() == stateRunning) && !mSessionTerminateRequested)
+ {
+ return mSessionURI;
+ }
+
+ return "";
+}
+
+bool LLVoiceClient::inProximalChannel()
+{
+ bool result = false;
+
+ if((getState() == stateRunning) && !mSessionTerminateRequested)
+ {
+ result = !mNonSpatialChannel;
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
+{
+ std::string result;
+ result = "sip:";
+ result += nameFromID(id);
+ result += "@";
+ result += mAccountServerName;
+
+ return result;
+}
+
+std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
+{
+ std::string result;
+ if(avatar)
+ {
+ result = "sip:";
+ result += nameFromID(avatar->getID());
+ result += "@";
+ result += mAccountServerName;
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
+{
+ std::string result;
+ if(avatar)
+ {
+ result = nameFromID(avatar->getID());
+ }
+ return result;
+}
+
+std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
+{
+ std::string result;
+ U8 rawuuid[UUID_BYTES + 1];
+ uuid.toCompressedString((char*)rawuuid);
+
+ // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
+ result = "x";
+
+ // Base64 encode and replace the pieces of base64 that are less compatible
+ // with e-mail local-parts.
+ // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
+ result += LLBase64::encode(rawuuid, UUID_BYTES);
+ LLString::replaceChar(result, '+', '-');
+ LLString::replaceChar(result, '/', '_');
+
+ // If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
+ // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
+
+ return result;
+}
+
+bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
+{
+ bool result = false;
+
+ // This will only work if the name is of the proper form.
+ // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
+ // "xFnPP04IpREWNkuw1cOXlhw=="
+
+ if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
+ {
+ // The name appears to have the right form.
+
+ // Reverse the transforms done by nameFromID
+ std::string temp = name;
+ LLString::replaceChar(temp, '-', '+');
+ LLString::replaceChar(temp, '_', '/');
+
+ U8 rawuuid[UUID_BYTES + 1];
+ int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1);
+ if(len == UUID_BYTES)
+ {
+ // The decode succeeded. Stuff the bits into the result's UUID
+ // MBW -- XXX -- there's no analogue of LLUUID::toCompressedString that allows you to set a UUID from binary data.
+ // The data field is public, so we cheat thusly:
+ memcpy(uuid.mData, rawuuid, UUID_BYTES);
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar)
+{
+ return avatar->getFullname();
+}
+
+std::string LLVoiceClient::sipURIFromName(std::string &name)
+{
+ std::string result;
+ result = "sip:";
+ result += name;
+ result += "@";
+ result += mAccountServerName;
+
+// LLString::toLower(result);
+
+ return result;
+}
+
+/////////////////////////////
+// Sending updates of current state
+
+void LLVoiceClient::enforceTether(void)
+{
+ LLVector3d tethered = mCameraRequestedPosition;
+
+ // constrain 'tethered' to within 50m of mAvatarPosition.
+ {
+ F32 max_dist = 50.0f;
+ LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
+ F32 camera_distance = (F32)camera_offset.magVec();
+ if(camera_distance > max_dist)
+ {
+ tethered = mAvatarPosition +
+ (max_dist / camera_distance) * camera_offset;
+ }
+ }
+
+ if(dist_vec(mCameraPosition, tethered) > 0.1)
+ {
+ mCameraPosition = tethered;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+{
+ mCameraRequestedPosition = position;
+
+ if(mCameraVelocity != velocity)
+ {
+ mCameraVelocity = velocity;
+ mSpatialCoordsDirty = true;
+ }
+
+ if(mCameraRot != rot)
+ {
+ mCameraRot = rot;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
+{
+ if(dist_vec(mAvatarPosition, position) > 0.1)
+ {
+ mAvatarPosition = position;
+ mSpatialCoordsDirty = true;
+ }
+
+ if(mAvatarVelocity != velocity)
+ {
+ mAvatarVelocity = velocity;
+ mSpatialCoordsDirty = true;
+ }
+
+ if(mAvatarRot != rot)
+ {
+ mAvatarRot = rot;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name)
+{
+ bool result = false;
+
+ if(region)
+ {
+ name = region->getName();
+ }
+
+ if(!name.empty())
+ result = true;
+
+ return result;
+}
+
+void LLVoiceClient::leaveChannel(void)
+{
+ if(getState() == stateRunning)
+ {
+// llinfos << "leaving channel for teleport/logout" << llendl;
+ mChannelName.clear();
+ sessionTerminate();
+ }
+}
+
+void LLVoiceClient::setMuteMic(bool muted)
+{
+ mMuteMic = muted;
+}
+
+void LLVoiceClient::setUserPTTState(bool ptt)
+{
+ mUserPTTState = ptt;
+}
+
+bool LLVoiceClient::getUserPTTState()
+{
+ return mUserPTTState;
+}
+
+void LLVoiceClient::toggleUserPTTState(void)
+{
+ mUserPTTState = !mUserPTTState;
+}
+
+void LLVoiceClient::setVoiceEnabled(bool enabled)
+{
+ if (enabled != mVoiceEnabled)
+ {
+ mVoiceEnabled = enabled;
+ if (enabled)
+ {
+ LLVoiceChannel::getCurrentVoiceChannel()->activate();
+ }
+ else
+ {
+ // for now, leave active channel, to auto join when turning voice back on
+ //LLVoiceChannel::getCurrentVoiceChannel->deactivate();
+ }
+ }
+}
+
+bool LLVoiceClient::voiceEnabled()
+{
+ return gSavedSettings.getBOOL("EnableVoiceChat") && !gDisableVoice;
+}
+
+void LLVoiceClient::setUsePTT(bool usePTT)
+{
+ if(usePTT && !mUsePTT)
+ {
+ // When the user turns on PTT, reset the current state.
+ mUserPTTState = false;
+ }
+ mUsePTT = usePTT;
+}
+
+void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
+{
+ if(!PTTIsToggle && mPTTIsToggle)
+ {
+ // When the user turns off toggle, reset the current state.
+ mUserPTTState = false;
+ }
+
+ mPTTIsToggle = PTTIsToggle;
+}
+
+
+void LLVoiceClient::setPTTKey(std::string &key)
+{
+ if(key == "MiddleMouse")
+ {
+ mPTTIsMiddleMouse = true;
+ }
+ else
+ {
+ mPTTIsMiddleMouse = false;
+ if(!LLKeyboard::keyFromString(key, &mPTTKey))
+ {
+ // If the call failed, don't match any key.
+ key = KEY_NONE;
+ }
+ }
+}
+
+void LLVoiceClient::setEarLocation(S32 loc)
+{
+ if(mEarLocation != loc)
+ {
+ llinfos << "Setting mEarLocation to " << loc << llendl;
+
+ mEarLocation = loc;
+ mSpatialCoordsDirty = true;
+ }
+}
+
+void LLVoiceClient::setVoiceVolume(F32 volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mSpeakerVolume)
+ {
+ if((scaledVolume == -100) || (mSpeakerVolume == -100))
+ {
+ mSpeakerMuteDirty = true;
+ }
+
+ mSpeakerVolume = scaledVolume;
+ mSpeakerVolumeDirty = true;
+ }
+}
+
+void LLVoiceClient::setMicGain(F32 volume)
+{
+ int scaledVolume = ((int)(volume * 100.0f)) - 100;
+ if(scaledVolume != mMicVolume)
+ {
+ mMicVolume = scaledVolume;
+ mMicVolumeDirty = true;
+ }
+}
+
+void LLVoiceClient::setVivoxDebugServerName(std::string &serverName)
+{
+ if(!mAccountServerName.empty())
+ {
+ // The name has been filled in already, which means we know whether we're connecting to agni or not.
+ if(!sConnectingToAgni)
+ {
+ // Only use the setting if we're connecting to a development grid -- always use bhr when on agni.
+ mAccountServerName = serverName;
+ }
+ }
+}
+
+void LLVoiceClient::keyDown(KEY key, MASK mask)
+{
+// llinfos << "key is " << LLKeyboard::stringFromKey(key) << llendl;
+
+ if (gKeyboard->getKeyRepeated(key))
+ {
+ // ignore auto-repeat keys
+ return;
+ }
+
+ if(!mPTTIsMiddleMouse)
+ {
+ if(mPTTIsToggle)
+ {
+ if(key == mPTTKey)
+ {
+ toggleUserPTTState();
+ }
+ }
+ else if(mPTTKey != KEY_NONE)
+ {
+ setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
+ }
+ }
+}
+void LLVoiceClient::keyUp(KEY key, MASK mask)
+{
+ if(!mPTTIsMiddleMouse)
+ {
+ if(!mPTTIsToggle && (mPTTKey != KEY_NONE))
+ {
+ setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
+ }
+ }
+}
+void LLVoiceClient::middleMouseState(bool down)
+{
+ if(mPTTIsMiddleMouse)
+ {
+ if(mPTTIsToggle)
+ {
+ if(down)
+ {
+ toggleUserPTTState();
+ }
+ }
+ else
+ {
+ setUserPTTState(down);
+ }
+ }
+}
+
+/////////////////////////////
+// Accessors for data related to nearby speakers
+BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
+{
+ BOOL result = FALSE;
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ // I'm not sure what the semantics of this should be.
+ // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
+ result = TRUE;
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
+ {
+ participant->mIsSpeaking = FALSE;
+ }
+ result = participant->mIsSpeaking;
+ }
+
+ return result;
+}
+
+F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
+{
+ F32 result = 0;
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mPower;
+ }
+
+ return result;
+}
+
+
+LLString LLVoiceClient::getDisplayName(const LLUUID& id)
+{
+ LLString result;
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mDisplayName;
+ }
+
+ return result;
+}
+
+
+BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ // I'm not sure what the semantics of this should be.
+ // Does "using PTT" mean they're configured with a push-to-talk button?
+ // For now, we know there's no PTT mechanism in place, so nobody is using it.
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getPTTPressed(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mPTT;
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
+{
+ BOOL result = FALSE;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mOnMuteList;
+ }
+
+ return result;
+}
+
+// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
+// internal = 400 * external^2
+F32 LLVoiceClient::getUserVolume(const LLUUID& id)
+{
+ F32 result = 0.0f;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ S32 ires = participant->mUserVolume; // 0-400
+ result = sqrtf(((F32)ires) / 400.f);
+ }
+
+ return result;
+}
+
+void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
+{
+ participantState *participant = findParticipantByID(id);
+ if (participant)
+ {
+ // volume can amplify by as much as 4x!
+ S32 ivol = (S32)(400.f * volume * volume);
+ participant->mUserVolume = llclamp(ivol, 0, 400);
+ participant->mVolumeDirty = TRUE;
+ mVolumeDirty = TRUE;
+ }
+}
+
+
+
+LLVoiceClient::serviceType LLVoiceClient::getServiceType(const LLUUID& id)
+{
+ serviceType result = serviceTypeUnknown;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mServiceType;
+ }
+
+ return result;
+}
+
+std::string LLVoiceClient::getGroupID(const LLUUID& id)
+{
+ std::string result;
+
+ participantState *participant = findParticipantByID(id);
+ if(participant)
+ {
+ result = participant->mGroupID;
+ }
+
+ return result;
+}
+
+BOOL LLVoiceClient::getAreaVoiceDisabled()
+{
+ return mAreaVoiceDisabled;
+}
+
+void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
+{
+ mObservers.insert(observer);
+}
+
+void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
+{
+ mObservers.erase(observer);
+}
+
+void LLVoiceClient::notifyObservers()
+{
+ for (observer_set_t::iterator it = mObservers.begin();
+ it != mObservers.end();
+ )
+ {
+ LLVoiceClientParticipantObserver* observer = *it;
+ observer->onChange();
+ // In case onChange() deleted an entry.
+ it = mObservers.upper_bound(observer);
+ }
+}
+
+void LLVoiceClient::addStatusObserver(LLVoiceClientStatusObserver* observer)
+{
+ mStatusObservers.insert(observer);
+}
+
+void LLVoiceClient::removeStatusObserver(LLVoiceClientStatusObserver* observer)
+{
+ mStatusObservers.erase(observer);
+}
+
+void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
+{
+ if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
+ {
+ switch(mVivoxErrorStatusCode)
+ {
+ case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break;
+ case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break;
+ }
+
+ // Reset the error code to make sure it won't be reused later by accident.
+ mVivoxErrorStatusCode = 0;
+ }
+
+ if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
+ //NOT_FOUND || TEMPORARILY_UNAVAILABLE || REQUEST_TIMEOUT
+ && (mVivoxErrorStatusCode == 404 || mVivoxErrorStatusCode == 480 || mVivoxErrorStatusCode == 408))
+ {
+ // call failed because other user was not available
+ // treat this as an error case
+ status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+
+ // Reset the error code to make sure it won't be reused later by accident.
+ mVivoxErrorStatusCode = 0;
+ }
+
+ llinfos << " " << LLVoiceClientStatusObserver::status2string(status) << ", session URI " << mSessionURI << llendl;
+
+ for (status_observer_set_t::iterator it = mStatusObservers.begin();
+ it != mStatusObservers.end();
+ )
+ {
+ LLVoiceClientStatusObserver* observer = *it;
+ observer->onChange(status, mSessionURI, !mNonSpatialChannel);
+ // In case onError() deleted an entry.
+ it = mStatusObservers.upper_bound(observer);
+ }
+
+}
+
+//static
+void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* user_data)
+{
+ participantState* statep = gVoiceClient->findParticipantByID(id);
+
+ if (statep)
+ {
+ statep->mDisplayName = llformat("%s %s", first, last);
+ }
+
+ gVoiceClient->notifyObservers();
+}
+
+class LLViewerParcelVoiceInfo : public LLHTTPNode
+{
+ virtual void post(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ //the parcel you are in has changed something about its
+ //voice information
+
+ if ( input.has("body") )
+ {
+ LLSD body = input["body"];
+
+ //body has "region_name" (str), "parcel_local_id"(int),
+ //"voice_credentials" (map).
+
+ //body["voice_credentials"] has "channel_uri" (str),
+ //body["voice_credentials"] has "channel_credentials" (str)
+ if ( body.has("voice_credentials") )
+ {
+ LLSD voice_credentials = body["voice_credentials"];
+ std::string uri;
+ std::string credentials;
+
+ if ( voice_credentials.has("channel_uri") )
+ {
+ uri = voice_credentials["channel_uri"].asString();
+ }
+ if ( voice_credentials.has("channel_credentials") )
+ {
+ credentials =
+ voice_credentials["channel_credentials"].asString();
+ }
+
+ gVoiceClient->setSpatialChannel(uri, credentials);
+ }
+ }
+ }
+};
+
+class LLViewerRequiredVoiceVersion : public LLHTTPNode
+{
+ static BOOL sAlertedUser;
+ virtual void post(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ //You received this messsage (most likely on region cross or
+ //teleport)
+ if ( input.has("body") && input["body"].has("major_version") )
+ {
+ int major_voice_version =
+ input["body"]["major_version"].asInteger();
+// int minor_voice_version =
+// input["body"]["minor_version"].asInteger();
+
+ if (gVoiceClient &&
+ (major_voice_version > VOICE_MAJOR_VERSION) )
+ {
+ if (!sAlertedUser)
+ {
+ //sAlertedUser = TRUE;
+ gViewerWindow->alertXml("VoiceVersionMismatch");
+ gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
+ }
+ }
+ }
+ }
+};
+BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE;
+
+LLHTTPRegistration<LLViewerParcelVoiceInfo>
+ gHTTPRegistrationMessageParcelVoiceInfo(
+ "/message/ParcelVoiceInfo");
+
+LLHTTPRegistration<LLViewerRequiredVoiceVersion>
+ gHTTPRegistrationMessageRequiredVoiceVersion(
+ "/message/RequiredVoiceVersion");
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
new file mode 100644
index 0000000000..d45b113e63
--- /dev/null
+++ b/indra/newview/llvoiceclient.h
@@ -0,0 +1,503 @@
+/**
+ * @file llvoiceclient.h
+ * @brief Declaration of LLVoiceClient class which is the interface to the voice client process.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_VOICE_CLIENT_H
+#define LL_VOICE_CLIENT_H
+
+// This would create a circular reference -- just do a forward definition of necessary class names.
+//#include "llvoavatar.h"
+class LLVOAvatar;
+class LLVivoxProtocolParser;
+
+#include "lliopipe.h"
+#include "llpumpio.h"
+#include "llchainio.h"
+#include "lliosocket.h"
+#include "v3math.h"
+#include "llframetimer.h"
+#include "llviewerregion.h"
+
+class LLVoiceClientParticipantObserver
+{
+public:
+ virtual ~LLVoiceClientParticipantObserver() { }
+ virtual void onChange() = 0;
+};
+
+class LLVoiceClientStatusObserver
+{
+public:
+ typedef enum e_voice_status_type
+ {
+ STATUS_LOGIN_RETRY,
+ STATUS_LOGGED_IN,
+ STATUS_JOINING,
+ STATUS_JOINED,
+ STATUS_LEFT_CHANNEL,
+ BEGIN_ERROR_STATUS,
+ ERROR_CHANNEL_FULL,
+ ERROR_CHANNEL_LOCKED,
+ ERROR_NOT_AVAILABLE,
+ ERROR_UNKNOWN
+ } EStatusType;
+
+ virtual ~LLVoiceClientStatusObserver() { }
+ virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) = 0;
+
+ static const char *status2string(EStatusType inStatus);
+};
+
+class LLVoiceClient: public LLSingleton<LLVoiceClient>
+{
+ LOG_CLASS(LLVoiceClient);
+ public:
+ LLVoiceClient();
+ ~LLVoiceClient();
+
+ public:
+ static void init(LLPumpIO *pump); // Call this once at application startup (creates connector)
+ static void terminate(); // Call this to clean up during shutdown
+
+ protected:
+ bool writeString(const std::string &str);
+
+ public:
+
+ enum serviceType
+ {
+ serviceTypeUnknown, // Unknown, returned if no data on the avatar is available
+ serviceTypeA, // spatialized local chat
+ serviceTypeB, // remote multi-party chat
+ serviceTypeC // one-to-one and small group chat
+ };
+ static F32 OVERDRIVEN_POWER_LEVEL;
+
+ /////////////////////////////
+ // session control messages
+ void connect();
+
+ void connectorCreate();
+ void connectorShutdown();
+
+ void requestVoiceAccountProvision(S32 retries = 3);
+ void userAuthorized(
+ const std::string& firstName,
+ const std::string& lastName,
+ const LLUUID &agentID);
+ void login(const std::string& accountName, const std::string &password);
+ void loginSendMessage();
+ void logout();
+ void logoutSendMessage();
+
+ void channelGetListSendMessage();
+ void sessionCreateSendMessage();
+ void sessionConnectSendMessage();
+ void sessionTerminate();
+ void sessionTerminateSendMessage();
+ void sessionTerminateByHandle(std::string &sessionHandle);
+
+ void getCaptureDevicesSendMessage();
+ void getRenderDevicesSendMessage();
+
+ void clearCaptureDevices();
+ void addCaptureDevice(const std::string& name);
+ void setCaptureDevice(const std::string& name);
+
+ void clearRenderDevices();
+ void addRenderDevice(const std::string& name);
+ void setRenderDevice(const std::string& name);
+
+ void tuningStart();
+ void tuningStop();
+ bool inTuningMode();
+
+ void tuningRenderStartSendMessage(const std::string& name, bool loop);
+ void tuningRenderStopSendMessage();
+
+ void tuningCaptureStartSendMessage(int duration);
+ void tuningCaptureStopSendMessage();
+
+ void tuningSetMicVolume(float volume);
+ void tuningSetSpeakerVolume(float volume);
+ float tuningGetEnergy(void);
+
+ // This returns true when it's safe to bring up the "device settings" dialog in the prefs.
+ // i.e. when the daemon is running and connected, and the device lists are populated.
+ bool deviceSettingsAvailable();
+
+ // Requery the vivox daemon for the current list of input/output devices.
+ // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
+ // (use this if you want to know when it's done).
+ // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim.
+ void refreshDeviceLists(bool clearCurrentList = true);
+
+ // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch.
+ void daemonDied();
+
+ // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away.
+ void giveUp();
+
+ /////////////////////////////
+ // Response/Event handlers
+ void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle);
+ void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle);
+ void channelGetListResponse(int statusCode, std::string &statusString);
+ void sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle);
+ void sessionConnectResponse(int statusCode, std::string &statusString);
+ void sessionTerminateResponse(int statusCode, std::string &statusString);
+ void logoutResponse(int statusCode, std::string &statusString);
+ void connectorShutdownResponse(int statusCode, std::string &statusString);
+
+ void loginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
+ void sessionNewEvent(std::string &accountHandle, std::string &eventSessionHandle, int state, std::string &nameString, std::string &uriString);
+ void sessionStateChangeEvent(std::string &uriString, int statusCode, std::string &statusString, std::string &sessionHandle, int state, bool isChannel, std::string &nameString);
+ void participantStateChangeEvent(std::string &uriString, int statusCode, std::string &statusString, int state, std::string &nameString, std::string &displayNameString, int participantType);
+ void participantPropertiesEvent(std::string &uriString, int statusCode, std::string &statusString, bool isLocallyMuted, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy);
+ void auxAudioPropertiesEvent(F32 energy);
+
+ void muteListChanged();
+
+ /////////////////////////////
+ // Sending updates of current state
+ void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
+ void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot);
+ bool channelFromRegion(LLViewerRegion *region, std::string &name);
+ void leaveChannel(void); // call this on logout or teleport begin
+
+
+ void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state.
+ void setUserPTTState(bool ptt);
+ bool getUserPTTState();
+ void toggleUserPTTState(void);
+ void setVoiceEnabled(bool enabled);
+ static bool voiceEnabled();
+ void setUsePTT(bool usePTT);
+ void setPTTIsToggle(bool PTTIsToggle);
+ void setPTTKey(std::string &key);
+ void setEarLocation(S32 loc);
+ void setVoiceVolume(F32 volume);
+ void setMicGain(F32 volume);
+ void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)
+ void setVivoxDebugServerName(std::string &serverName);
+
+ // PTT key triggering
+ void keyDown(KEY key, MASK mask);
+ void keyUp(KEY key, MASK mask);
+ void middleMouseState(bool down);
+
+ /////////////////////////////
+ // Accessors for data related to nearby speakers
+ BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar
+ BOOL getIsSpeaking(const LLUUID& id);
+ F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is...
+ BOOL getPTTPressed(const LLUUID& id); // This is the inverse of the "locally muted" property.
+ BOOL getOnMuteList(const LLUUID& id);
+ F32 getUserVolume(const LLUUID& id);
+ LLString getDisplayName(const LLUUID& id);
+
+ // MBW -- XXX -- Not sure how to get this data out of the TVC
+ BOOL getUsingPTT(const LLUUID& id);
+ serviceType getServiceType(const LLUUID& id); // type of chat the user is involved in (see bHear scope doc for definitions of A/B/C)
+ std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable)
+
+ /////////////////////////////
+ BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled.
+ // Use this to determine whether to show a "no speech" icon in the menu bar.
+
+ struct participantState
+ {
+ public:
+ participantState(const std::string &uri);
+ std::string mURI;
+ std::string mName;
+ std::string mDisplayName;
+ bool mPTT;
+ bool mIsSpeaking;
+ LLFrameTimer mSpeakingTimeout;
+ F32 mLastSpokeTimestamp;
+ F32 mPower;
+ int mVolume;
+ serviceType mServiceType;
+ std::string mGroupID;
+ bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted)
+ int mUserVolume;
+ bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed)
+ bool mAvatarIDValid;
+ LLUUID mAvatarID;
+ };
+ typedef std::map<std::string, participantState*> participantMap;
+
+ participantState *findParticipant(const std::string &uri);
+ participantState *findParticipantByAvatar(LLVOAvatar *avatar);
+ participantState *findParticipantByID(const LLUUID& id);
+
+ participantMap *getParticipantList(void);
+
+ void addObserver(LLVoiceClientParticipantObserver* observer);
+ void removeObserver(LLVoiceClientParticipantObserver* observer);
+
+ void addStatusObserver(LLVoiceClientStatusObserver* observer);
+ void removeStatusObserver(LLVoiceClientStatusObserver* observer);
+
+ static void onAvatarNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* user_data);
+ typedef std::vector<std::string> deviceList;
+
+ deviceList *getCaptureDevices();
+ deviceList *getRenderDevices();
+
+ void setNonSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials);
+ void setSpatialChannel(
+ const std::string &uri,
+ const std::string &credentials);
+ void callUser(LLUUID &uuid);
+ void answerInvite(std::string &sessionHandle, LLUUID& other_user_id);
+ void declineInvite(std::string &sessionHandle);
+ void leaveNonSpatialChannel();
+
+ // Returns the URI of the current channel, or an empty string if not currently in a channel.
+ // NOTE that it will return an empty string if it's in the process of joining a channel.
+ std::string getCurrentChannel();
+
+ // returns true iff the user is currently in a proximal (local spatial) channel.
+ // Note that gestures should only fire if this returns true.
+ bool inProximalChannel();
+
+ std::string sipURIFromID(const LLUUID &id);
+
+ private:
+
+ // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages.
+ // Note: if you change this list, please make corresponding changes to LLVoiceClient::state2string().
+ enum state
+ {
+ stateDisabled, // Voice is turned off.
+ stateStart, // Class is initialized, socket is created
+ stateDaemonLaunched, // Daemon has been launched
+ stateConnecting, // connect() call has been issued
+ stateIdle, // socket is connected, ready for messaging
+ stateConnectorStart, // connector needs to be started
+ stateConnectorStarting, // waiting for connector handle
+ stateConnectorStarted, // connector handle received
+ stateMicTuningNoLogin, // mic tuning before login
+ stateLoginRetry, // need to retry login (failed due to changing password)
+ stateLoginRetryWait, // waiting for retry timer
+ stateNeedsLogin, // send login request
+ stateLoggingIn, // waiting for account handle
+ stateLoggedIn, // account handle received
+ stateNoChannel, //
+ stateMicTuningLoggedIn, // mic tuning for a logged in user
+ stateSessionCreate, // need to send Session.Create command
+ stateSessionConnect, // need to send Session.Connect command
+ stateJoiningSession, // waiting for session handle
+ stateSessionJoined, // session handle received
+ stateRunning, // in session, steady state
+ stateLeavingSession, // waiting for terminate session response
+ stateSessionTerminated, // waiting for terminate session response
+
+ stateLoggingOut, // waiting for logout response
+ stateLoggedOut, // logout response received
+ stateConnectorStopping, // waiting for connector stop
+ stateConnectorStopped, // connector stop received
+
+ // We go to this state if the login fails because the account needs to be provisioned.
+
+ // error states. No way to recover from these yet.
+ stateConnectorFailed,
+ stateConnectorFailedWaiting,
+ stateLoginFailed,
+ stateLoginFailedWaiting,
+ stateJoinSessionFailed,
+ stateJoinSessionFailedWaiting,
+
+ stateJail // Go here when all else has failed. Nothing will be retried, we're done.
+ };
+
+ state mState;
+ bool mSessionTerminateRequested;
+ bool mNonSpatialChannel;
+
+ void setState(state inState);
+ state getState(void) { return mState; };
+ static const char *state2string(state inState);
+
+ void stateMachine();
+ static void idle(void *user_data);
+
+ LLHost mDaemonHost;
+ LLSocket::ptr_t mSocket;
+ bool mConnected;
+
+ void closeSocket(void);
+
+ LLPumpIO *mPump;
+ friend class LLVivoxProtocolParser;
+
+ std::string mAccountName;
+ std::string mAccountPassword;
+ std::string mAccountDisplayName;
+ std::string mAccountFirstName;
+ std::string mAccountLastName;
+
+ std::string mNextP2PSessionURI; // URI of the P2P session to join next
+ std::string mNextSessionURI; // URI of the session to join next
+ std::string mNextSessionHandle; // Session handle of the session to join next
+ std::string mNextSessionHash; // Password hash for the session to join next
+ bool mNextSessionSpatial; // Will next session be a spatial chat?
+ bool mNextSessionNoReconnect; // Next session should not auto-reconnect (i.e. user -> user chat)
+ bool mNextSessionResetOnClose; // If this is true, go back to spatial chat when the next session terminates.
+
+ std::string mSessionStateEventHandle; // session handle received in SessionStateChangeEvents
+ std::string mSessionStateEventURI; // session URI received in SessionStateChangeEvents
+
+ bool mTuningMode;
+ float mTuningEnergy;
+ std::string mTuningAudioFile;
+ int mTuningMicVolume;
+ bool mTuningMicVolumeDirty;
+ int mTuningSpeakerVolume;
+ bool mTuningSpeakerVolumeDirty;
+ bool mTuningCaptureRunning;
+
+ std::string mSpatialSessionURI;
+
+ bool mSessionResetOnClose;
+
+ int mVivoxErrorStatusCode;
+ std::string mVivoxErrorStatusString;
+
+ std::string mChannelName; // Name of the channel to be looked up
+ bool mAreaVoiceDisabled;
+ std::string mSessionURI; // URI of the session we're in.
+ bool mSessionP2P; // true if this session is a p2p call
+
+ S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings
+ std::string mCurrentRegionName; // Used to detect parcel boundary crossings
+
+ std::string mConnectorHandle; // returned by "Create Connector" message
+ std::string mAccountHandle; // returned by login message
+ std::string mSessionHandle; // returned by ?
+ U32 mCommandCookie;
+
+ std::string mAccountServerName;
+ std::string mAccountServerURI;
+
+ int mLoginRetryCount;
+
+ participantMap mParticipantMap;
+ bool mParticipantMapChanged;
+
+ deviceList mCaptureDevices;
+ deviceList mRenderDevices;
+
+ std::string mCaptureDevice;
+ std::string mRenderDevice;
+ bool mCaptureDeviceDirty;
+ bool mRenderDeviceDirty;
+
+ participantState *addParticipant(const std::string &uri);
+ // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted.
+ // Take care not to use the pointer again after that.
+ void removeParticipant(participantState *participant);
+ void removeAllParticipants();
+
+ void updateMuteState(participantState *participant);
+
+ typedef std::map<std::string, std::string> channelMap;
+ channelMap mChannelMap;
+
+ // These are used by the parser when processing a channel list response.
+ void clearChannelMap(void);
+ void addChannelMapEntry(std::string &name, std::string &uri);
+ std::string findChannelURI(std::string &name);
+
+ // This should be called when the code detects we have changed parcels.
+ // It initiates the call to the server that gets the parcel channel.
+ void parcelChanged();
+
+ void switchChannel(std::string uri = "", bool spatial = true, bool noReconnect = false, std::string hash = "");
+ void joinSession(std::string handle, std::string uri);
+
+ std::string nameFromAvatar(LLVOAvatar *avatar);
+ std::string nameFromID(const LLUUID &id);
+ bool IDFromName(const std::string name, LLUUID &uuid);
+ std::string displayNameFromAvatar(LLVOAvatar *avatar);
+ std::string sipURIFromAvatar(LLVOAvatar *avatar);
+ std::string sipURIFromName(std::string &name);
+
+ void sendPositionalUpdate(void);
+
+ void buildSetCaptureDevice(std::ostringstream &stream);
+ void buildSetRenderDevice(std::ostringstream &stream);
+
+ void enforceTether(void);
+
+ bool mSpatialCoordsDirty;
+
+ LLVector3d mCameraPosition;
+ LLVector3d mCameraRequestedPosition;
+ LLVector3 mCameraVelocity;
+ LLMatrix3 mCameraRot;
+
+ LLVector3d mAvatarPosition;
+ LLVector3 mAvatarVelocity;
+ LLMatrix3 mAvatarRot;
+
+ bool mPTTDirty;
+ bool mPTT;
+
+ bool mUsePTT;
+ bool mPTTIsMiddleMouse;
+ KEY mPTTKey;
+ bool mPTTIsToggle;
+ bool mUserPTTState;
+ bool mMuteMic;
+
+ // Set to true when the mute state of someone in the participant list changes.
+ // The code will have to walk the list to find the changed participant(s).
+ bool mVolumeDirty;
+
+ enum
+ {
+ earLocCamera = 0, // ear at camera
+ earLocAvatar, // ear at avatar
+ earLocMixed // ear at avatar location/camera direction
+ };
+
+ S32 mEarLocation;
+
+ bool mSpeakerVolumeDirty;
+ bool mSpeakerMuteDirty;
+ int mSpeakerVolume;
+
+ int mMicVolume;
+ bool mMicVolumeDirty;
+
+ bool mVoiceEnabled;
+ bool mWriteInProgress;
+ std::string mWriteString;
+ size_t mWriteOffset;
+
+ LLTimer mUpdateTimer;
+
+ typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
+ observer_set_t mObservers;
+
+ void notifyObservers();
+
+ typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t;
+ status_observer_set_t mStatusObservers;
+
+ void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status);
+};
+
+extern LLVoiceClient *gVoiceClient;
+
+#endif //LL_VOICE_CLIENT_H
+
+
diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp
new file mode 100644
index 0000000000..fb3fd8af15
--- /dev/null
+++ b/indra/newview/llvoicevisualizer.cpp
@@ -0,0 +1,439 @@
+/**
+ * @file llvoicevisualizer.cpp
+ * @brief Draws in-world speaking indicators.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//----------------------------------------------------------------------
+// Voice Visualizer
+// author: JJ Ventrella
+// (information about this stuff can be found in "llvoicevisualizer.h")
+//----------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+#include "llviewercontrol.h"
+#include "llglheaders.h"
+#include "llsphere.h"
+#include "llvoicevisualizer.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llimagegl.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llvoiceclient.h"
+
+//brent's wave image
+//29de489d-0491-fb00-7dab-f9e686d31e83
+
+
+//--------------------------------------------------------------------------------------
+// sound symbol constants
+//--------------------------------------------------------------------------------------
+const F32 HEIGHT_ABOVE_HEAD = 0.3f; // how many meters vertically above the av's head the voice symbol will appear
+const F32 RED_THRESHOLD = LLVoiceClient::OVERDRIVEN_POWER_LEVEL; // value above which speaking amplitude causes the voice symbol to turn red
+const F32 GREEN_THRESHOLD = 0.2f; // value above which speaking amplitude causes the voice symbol to turn green
+const F32 FADE_OUT_DURATION = 0.4f; // how many seconds it takes for a pair of waves to fade away
+const F32 EXPANSION_RATE = 1.0f; // how many seconds it takes for the waves to expand to twice their original size
+const F32 EXPANSION_MAX = 1.5f; // maximum size scale to which the waves can expand before popping back to 1.0
+const F32 WAVE_WIDTH_SCALE = 0.03f; // base width of the waves
+const F32 WAVE_HEIGHT_SCALE = 0.02f; // base height of the waves
+const F32 BASE_BRIGHTNESS = 0.7f; // gray level of the voice indicator when quiet (below green threshold)
+const F32 DOT_SIZE = 0.05f; // size of the dot billboard texture
+const F32 DOT_OPACITY = 0.7f; // how opaque the dot is
+const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a function of speaking amplitude
+
+//--------------------------------------------------------------------------------------
+// gesticulation constants
+//--------------------------------------------------------------------------------------
+const F32 DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE = 0.2f;
+const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f;
+
+//--------------------------------------------------------------------------------------
+// other constants
+//--------------------------------------------------------------------------------------
+const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code.
+const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL
+
+//-----------------------------------------------
+// constructor
+//-----------------------------------------------
+LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
+:LLHUDEffect( type )
+{
+ mCurrentTime = mTimer.getTotalSeconds();
+ mPreviousTime = mCurrentTime;
+ mVoiceSourceWorldPosition = LLVector3( 0.0f, 0.0f, 0.0f );
+ mSpeakingAmplitude = 0.0f;
+ mCurrentlySpeaking = false;
+ mVoiceEnabled = false;
+ mMinGesticulationAmplitude = DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE;
+ mMaxGesticulationAmplitude = DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE;
+ mSoundSymbol.mActive = true;
+ mSoundSymbol.mPosition = LLVector3( 0.0f, 0.0f, 0.0f );
+
+ mTimer.reset();
+
+ LLUUID sound_level_img[] =
+ {
+ LLUUID(gSavedSettings.getString("VoiceImageLevel0")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel1")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel2")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel3")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel4")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel5")),
+ LLUUID(gSavedSettings.getString("VoiceImageLevel6"))
+ };
+
+ for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++)
+ {
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ mSoundSymbol.mTexture [i] = gImageList.getUIImageByID(sound_level_img[i]);
+ mSoundSymbol.mWaveActive [i] = false;
+ mSoundSymbol.mWaveOpacity [i] = 1.0f;
+ mSoundSymbol.mWaveExpansion [i] = 1.0f;
+ }
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setMinGesticulationAmplitude( F32 m )
+{
+ mMinGesticulationAmplitude = m;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setMaxGesticulationAmplitude( F32 m )
+{
+ mMaxGesticulationAmplitude = m;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setVoiceEnabled( bool v )
+{
+ mVoiceEnabled = v;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setStartSpeaking()
+{
+ mCurrentlySpeaking = true;
+ mSoundSymbol.mActive = true;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+bool LLVoiceVisualizer::getCurrentlySpeaking()
+{
+ return mCurrentlySpeaking;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setStopSpeaking()
+{
+ mCurrentlySpeaking = false;
+ mSpeakingAmplitude = 0.0f;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setSpeakingAmplitude( F32 a )
+{
+ mSpeakingAmplitude = a;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+// this method is inherited from HUD Effect
+//---------------------------------------------------
+void LLVoiceVisualizer::render()
+{
+ if ( ! mVoiceEnabled )
+ {
+ return;
+ }
+
+ if ( mSoundSymbol.mActive )
+ {
+ mPreviousTime = mCurrentTime;
+ mCurrentTime = mTimer.getTotalSeconds();
+
+ //---------------------------------------------------------------
+ // set the sound symbol position over the source (avatar's head)
+ //---------------------------------------------------------------
+ mSoundSymbol.mPosition = mVoiceSourceWorldPosition + WORLD_UPWARD_DIRECTION * HEIGHT_ABOVE_HEAD;
+
+ //---------------------------------------------------------------
+ // some gl state
+ //---------------------------------------------------------------
+ LLGLEnable tex( GL_TEXTURE_2D );
+ LLGLEnable blend( GL_BLEND );
+
+ //-------------------------------------------------------------
+ // create coordinates of the geometry for the dot
+ //-------------------------------------------------------------
+ LLVector3 l = gCamera->getLeftAxis() * DOT_SIZE;
+ LLVector3 u = gCamera->getUpAxis() * DOT_SIZE;
+
+ LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u;
+ LLVector3 bottomRight = mSoundSymbol.mPosition - l - u;
+ LLVector3 topLeft = mSoundSymbol.mPosition + l + u;
+ LLVector3 topRight = mSoundSymbol.mPosition - l + u;
+
+ //-----------------------------
+ // bind texture 0 (the dot)
+ //-----------------------------
+ mSoundSymbol.mTexture[0]->bind();
+
+ //-------------------------------------------------------------
+ // now render the dot
+ //-------------------------------------------------------------
+ glColor4fv( LLColor4( 1.0f, 1.0f, 1.0f, DOT_OPACITY ).mV );
+
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 0, 0 ); glVertex3fv( bottomLeft.mV );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 1, 1 ); glVertex3fv( topRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+
+
+ //--------------------------------------------------------------------------------------
+ // if currently speaking, trigger waves (1 through 6) based on speaking amplitude
+ //--------------------------------------------------------------------------------------
+ if ( mCurrentlySpeaking )
+ {
+ F32 min = 0.2f;
+ F32 max = 0.7f;
+ F32 fraction = ( mSpeakingAmplitude - min ) / ( max - min );
+
+ // in case mSpeakingAmplitude > max....
+ if ( fraction > 1.0f )
+ {
+ fraction = 1.0f;
+ }
+
+ S32 level = 1 + (int)( fraction * ( NUM_VOICE_SYMBOL_WAVES - 2 ) );
+
+ for (int i=0; i<level+1; i++)
+ {
+ mSoundSymbol.mWaveActive [i] = true;
+ mSoundSymbol.mWaveOpacity [i] = 1.0f;
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ }
+
+ } // if currently speaking
+
+ //---------------------------------------------------
+ // determine color
+ //---------------------------------------------------
+ F32 red = 0.0f;
+ F32 green = 0.0f;
+ F32 blue = 0.0f;
+ if ( mSpeakingAmplitude < RED_THRESHOLD )
+ {
+ if ( mSpeakingAmplitude < GREEN_THRESHOLD )
+ {
+ red = BASE_BRIGHTNESS;
+ green = BASE_BRIGHTNESS;
+ blue = BASE_BRIGHTNESS;
+ }
+ else
+ {
+ //---------------------------------------------------
+ // fade from gray to bright green
+ //---------------------------------------------------
+ F32 fraction = ( mSpeakingAmplitude - GREEN_THRESHOLD ) / ( 1.0f - GREEN_THRESHOLD );
+ red = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS );
+ green = BASE_BRIGHTNESS + fraction * ( 1.0f - BASE_BRIGHTNESS );
+ blue = BASE_BRIGHTNESS - ( fraction * BASE_BRIGHTNESS );
+ }
+ }
+ else
+ {
+ //---------------------------------------------------
+ // redish
+ //---------------------------------------------------
+ red = 1.0f;
+ green = 0.2f;
+ blue = 0.2f;
+ }
+
+ for (int i=0; i<NUM_VOICE_SYMBOL_WAVES; i++)
+ {
+ if ( mSoundSymbol.mWaveActive[i] )
+ {
+ F32 fadeOutFraction = (F32)( mCurrentTime - mSoundSymbol.mWaveFadeOutStartTime[i] ) / FADE_OUT_DURATION;
+
+ mSoundSymbol.mWaveOpacity[i] = 1.0f - fadeOutFraction;
+
+ if ( mSoundSymbol.mWaveOpacity[i] < 0.0f )
+ {
+ mSoundSymbol.mWaveFadeOutStartTime [i] = mCurrentTime;
+ mSoundSymbol.mWaveOpacity [i] = 0.0f;
+ mSoundSymbol.mWaveActive [i] = false;
+ }
+
+ //----------------------------------------------------------------------------------
+ // This is where we calculate the expansion of the waves - that is, the
+ // rate at which they are scaled greater than 1.0 so that they grow over time.
+ //----------------------------------------------------------------------------------
+ F32 timeSlice = (F32)( mCurrentTime - mPreviousTime );
+ F32 waveSpeed = mSpeakingAmplitude * WAVE_MOTION_RATE;
+ mSoundSymbol.mWaveExpansion[i] *= ( 1.0f + EXPANSION_RATE * timeSlice * waveSpeed );
+
+ if ( mSoundSymbol.mWaveExpansion[i] > EXPANSION_MAX )
+ {
+ mSoundSymbol.mWaveExpansion[i] = 1.0f;
+ }
+
+ //----------------------------------------------------------------------------------
+ // create geometry for the wave billboard textures
+ //----------------------------------------------------------------------------------
+ F32 width = i * WAVE_WIDTH_SCALE * mSoundSymbol.mWaveExpansion[i];
+ F32 height = i * WAVE_HEIGHT_SCALE * mSoundSymbol.mWaveExpansion[i];
+
+ LLVector3 l = gCamera->getLeftAxis() * width;
+ LLVector3 u = gCamera->getUpAxis() * height;
+
+ LLVector3 bottomLeft = mSoundSymbol.mPosition + l - u;
+ LLVector3 bottomRight = mSoundSymbol.mPosition - l - u;
+ LLVector3 topLeft = mSoundSymbol.mPosition + l + u;
+ LLVector3 topRight = mSoundSymbol.mPosition - l + u;
+
+ glColor4fv( LLColor4( red, green, blue, mSoundSymbol.mWaveOpacity[i] ).mV );
+ mSoundSymbol.mTexture[i]->bind();
+
+ //---------------------------------------------------
+ // now, render the mofo
+ //---------------------------------------------------
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 0, 0 ); glVertex3fv( bottomLeft.mV );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+ glBegin( GL_TRIANGLE_STRIP );
+ glTexCoord2i( 1, 0 ); glVertex3fv( bottomRight.mV );
+ glTexCoord2i( 1, 1 ); glVertex3fv( topRight.mV );
+ glTexCoord2i( 0, 1 ); glVertex3fv( topLeft.mV );
+ glEnd();
+
+ } //if ( mSoundSymbol.mWaveActive[i] )
+
+ }// for loop
+
+ }//if ( mSoundSymbol.mActive )
+
+}//---------------------------------------------------
+
+
+
+
+
+//---------------------------------------------------
+void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p )
+{
+ mVoiceSourceWorldPosition = p;
+
+}//---------------------------------------------------
+
+//---------------------------------------------------
+VoiceGesticulationLevel LLVoiceVisualizer::getCurrentGesticulationLevel()
+{
+ VoiceGesticulationLevel gesticulationLevel = VOICE_GESTICULATION_LEVEL_OFF; //default
+
+ //-----------------------------------------------------------------------------------------
+ // Within the range of gesticulation amplitudes, the sound signal is split into
+ // three equal amplitude regimes, each specifying one of three gesticulation levels.
+ //-----------------------------------------------------------------------------------------
+ F32 range = mMaxGesticulationAmplitude - mMinGesticulationAmplitude;
+
+ if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.66666f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_HIGH; }
+ else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.33333f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_MEDIUM; }
+ else if ( mSpeakingAmplitude > mMinGesticulationAmplitude + range * 0.00000f ) { gesticulationLevel = VOICE_GESTICULATION_LEVEL_LOW; }
+
+ return gesticulationLevel;
+
+}//---------------------------------------------------
+
+
+
+//------------------------------------
+// Destructor
+//------------------------------------
+LLVoiceVisualizer::~LLVoiceVisualizer()
+{
+}//----------------------------------------------
+
+
+//---------------------------------------------------
+// "packData" is inherited from HUDEffect
+//---------------------------------------------------
+void LLVoiceVisualizer::packData(LLMessageSystem *mesgsys)
+{
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // TODO -- pack the relevant data for voice effects
+ // we'll come up with some cool configurations....TBD
+ //U8 packed_data[41];
+ //mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 41);
+ U8 packed_data = 0;
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, &packed_data, 1);
+}
+
+
+//---------------------------------------------------
+// "unpackData" is inherited from HUDEffect
+//---------------------------------------------------
+void LLVoiceVisualizer::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ // TODO -- find the speaker, unpack binary data, set the properties of this effect
+ /*
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != 1)
+ {
+ llwarns << "Voice effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 1, blocknum);
+ */
+}
+
+
+//------------------------------------------------------------------
+// this method is inherited from HUD Effect
+//------------------------------------------------------------------
+void LLVoiceVisualizer::markDead()
+{
+ mCurrentlySpeaking = false;
+ mVoiceEnabled = false;
+ mSoundSymbol.mActive = false;
+
+}//------------------------------------------------------------------
+
+
+
+
+
+
+
+
diff --git a/indra/newview/llvoicevisualizer.h b/indra/newview/llvoicevisualizer.h
new file mode 100644
index 0000000000..bd54bd435b
--- /dev/null
+++ b/indra/newview/llvoicevisualizer.h
@@ -0,0 +1,119 @@
+/**
+ * @file llvoicevisualizer.h
+ * @brief Draws in-world speaking indicators.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//--------------------------------------------------------------------
+//
+// VOICE VISUALIZER
+// author: JJ Ventrella, Linden Lab
+// (latest update to this info: Jan 18, 2007)
+//
+// The Voice Visualizer is responsible for taking realtime signals from actual users speaking and
+// visualizing this speech in two forms:
+//
+// (1) as a dynamic sound symbol (also referred to as the "voice indicator" that appears over the avatar's head
+// (2) as gesticulation events that are used to trigger avatr gestures
+//
+// The input for the voice visualizer is a continual stream of voice amplitudes.
+
+//-----------------------------------------------------------------------------
+#ifndef LL_VOICE_VISUALIZER_H
+#define LL_VOICE_VISUALIZER_H
+
+#include "llhudeffect.h"
+
+//-----------------------------------------------------------------------------------------------
+// The values of voice gesticulation represent energy levels for avatar animation, based on
+// amplitude surge events parsed from the voice signal. These are made available so that
+// the appropriate kind of avatar animation can be triggered, and thereby simulate the physical
+// motion effects of speech. It is recommended that multiple body parts be animated as well as
+// lips, such as head, shoulders, and hands, with large gestures used when the energy level is high.
+//-----------------------------------------------------------------------------------------------
+enum VoiceGesticulationLevel
+{
+ VOICE_GESTICULATION_LEVEL_OFF = -1,
+ VOICE_GESTICULATION_LEVEL_LOW = 0,
+ VOICE_GESTICULATION_LEVEL_MEDIUM,
+ VOICE_GESTICULATION_LEVEL_HIGH,
+ NUM_VOICE_GESTICULATION_LEVELS
+};
+
+const static int NUM_VOICE_SYMBOL_WAVES = 7;
+
+//----------------------------------------------------
+// LLVoiceVisualizer class
+//----------------------------------------------------
+class LLVoiceVisualizer : public LLHUDEffect
+{
+ //---------------------------------------------------
+ // public methods
+ //---------------------------------------------------
+ public:
+ LLVoiceVisualizer ( const U8 type ); //constructor
+ ~LLVoiceVisualizer(); //destructor
+
+ friend class LLHUDObject;
+
+ void setVoiceSourceWorldPosition( const LLVector3 &p ); // this should be the position of the speaking avatar's head
+ void setMinGesticulationAmplitude( F32 ); // the lower range of meaningful amplitude for setting gesticulation level
+ void setMaxGesticulationAmplitude( F32 ); // the upper range of meaningful amplitude for setting gesticulation level
+ void setStartSpeaking(); // tell me when the av starts speaking
+ void setVoiceEnabled( bool ); // tell me whether or not the user is voice enabled
+ void setSpeakingAmplitude( F32 ); // tell me how loud the av is speaking (ranges from 0 to 1)
+ void setStopSpeaking(); // tell me when the av stops speaking
+ bool getCurrentlySpeaking(); // the get for the above set
+ VoiceGesticulationLevel getCurrentGesticulationLevel(); // based on voice amplitude, I'll give you the current "energy level" of avatar speech
+
+ void render(); // inherited from HUD Effect
+ void packData(LLMessageSystem *mesgsys); // inherited from HUD Effect
+ void unpackData(LLMessageSystem *mesgsys, S32 blocknum); // inherited from HUD Effect
+ void markDead(); // inherited from HUD Effect
+
+ //----------------------------------------------------------------------------------------------
+ // "setMaxGesticulationAmplitude" and "setMinGesticulationAmplitude" allow for the tuning of the
+ // gesticulation level detector to be responsive to different kinds of signals. For instance, we
+ // may find that the average voice amplitude rarely exceeds 0.7 (in a range from 0 to 1), and
+ // therefore we may want to set 0.7 as the max, so we can more easily catch all the variance
+ // within that range. Also, we may find that there is often noise below a certain range like 0.1,
+ // and so we would want to set 0.1 as the min so as not to accidentally use this as signal.
+ //----------------------------------------------------------------------------------------------
+ void setMaxGesticulationAmplitude();
+ void setMinGesticulationAmplitude();
+
+ //---------------------------------------------------
+ // private members
+ //---------------------------------------------------
+ private:
+
+ struct SoundSymbol
+ {
+ F32 mWaveExpansion [ NUM_VOICE_SYMBOL_WAVES ];
+ bool mWaveActive [ NUM_VOICE_SYMBOL_WAVES ];
+ F64 mWaveFadeOutStartTime [ NUM_VOICE_SYMBOL_WAVES ];
+ F32 mWaveOpacity [ NUM_VOICE_SYMBOL_WAVES ];
+ LLPointer<LLImageGL> mTexture [ NUM_VOICE_SYMBOL_WAVES ];
+ bool mActive;
+ LLVector3 mPosition;
+ };
+
+ LLFrameTimer mTimer; // so I can ask the current time in seconds
+ F64 mCurrentTime; // current time in seconds, captured every step
+ F64 mPreviousTime; // copy of "current time" from last frame
+ SoundSymbol mSoundSymbol; // the sound symbol that appears over the avatar's head
+ bool mVoiceEnabled; // if off, no rendering should happen
+ bool mCurrentlySpeaking; // is the user currently speaking?
+ LLVector3 mVoiceSourceWorldPosition; // give this to me every step - I need it to update the sound symbol
+ F32 mSpeakingAmplitude; // this should be set as often as possible when the user is speaking
+ F32 mMaxGesticulationAmplitude; // this is the upper-limit of the envelope of detectable gesticulation leves
+ F32 mMinGesticulationAmplitude; // this is the lower-limit of the envelope of detectable gesticulation leves
+
+};//-----------------------------------------------------------------
+ // end of LLVoiceVisualizer class
+//------------------------------------------------------------------
+
+#endif //LL_VOICE_VISUALIZER_H
+
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index a126237e00..57b046c082 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -780,6 +780,8 @@ BOOL LLVOVolume::updateLOD()
mLODChanged = TRUE;
}
+ lod_changed |= LLViewerObject::updateLOD();
+
return lod_changed;
}
@@ -1108,7 +1110,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
mSculptChanged = FALSE;
mFaceMappingChanged = FALSE;
- return TRUE;
+ return LLViewerObject::updateGeometry(drawable);
}
void LLVOVolume::updateFaceSize(S32 idx)
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index 6c99d99732..ab95b9eda6 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -292,8 +292,7 @@ void LLWorldMapView::draw()
const S32 half_height = height / 2;
LLVector3d camera_global = gAgent.getCameraPositionGlobal();
- LLGLEnable scissor_test(GL_SCISSOR_TEST);
- LLUI::setScissorRegionLocal(LLRect(0, height, width, 0));
+ LLLocalClipRect clip(getLocalRect());
{
LLGLSNoTexture no_texture;
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index c93b302312..9136a06c24 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -69,25 +69,42 @@ class ViewerManifest(LLManifest):
self.path("lsl_guide.html")
self.path("gpu_table.txt")
+ def channel_unique(self):
+ return self.args['channel'].replace("Second Life", "").strip()
+ def channel_oneword(self):
+ return "".join(self.channel_unique().split())
+ def channel_lowerword(self):
+ return self.channel_oneword().lower()
+
def flags_list(self):
""" Convenience function that returns the command-line flags for the grid"""
- if(self.args['grid'] == ''):
- return ""
- elif(self.args['grid'] == 'firstlook'):
- return '-settings settings_firstlook.xml'
- else:
- return ("-settings settings_beta.xml --%(grid)s -helperuri http://preview-%(grid)s.secondlife.com/helpers/" % {'grid':self.args['grid']})
+ channel_flags = ''
+ grid_flags = ''
+ if not self.default_grid():
+ if self.default_channel():
+ # beta grid viewer
+ channel_flags = '-settings settings_beta.xml'
+ grid_flags = "--%(grid)s -helperuri http://preview-%(grid)s.secondlife.com/helpers/" % {'grid':self.args['grid']}
+
+ if not self.default_channel():
+ # some channel on some grid
+ channel_flags = '-settings settings_%s.xml -channel "%s"' % (self.channel_lowerword(), self.args['channel'])
+ return " ".join((grid_flags, channel_flags)).strip()
def login_url(self):
""" Convenience function that returns the appropriate login url for the grid"""
if(self.args.get('login_url')):
return self.args['login_url']
else:
- if(self.args['grid'] == ''):
- return 'http://secondlife.com/app/login/'
- elif(self.args['grid'] == 'firstlook'):
- return 'http://secondlife.com/app/login/firstlook/'
+ if(self.default_grid()):
+ if(self.default_channel()):
+ # agni release
+ return 'http://secondlife.com/app/login/'
+ else:
+ # first look (or other) on agni
+ return 'http://secondlife.com/app/login/%s/' % self.channel_lowerword()
else:
+ # beta grid
return 'http://secondlife.com/app/login/beta/'
def replace_login_url(self):
@@ -97,14 +114,14 @@ class ViewerManifest(LLManifest):
class WindowsManifest(ViewerManifest):
def final_exe(self):
- # *NOTE: these are the only two executable names that the crash reporter recognizes
- if self.args['grid'] == '':
- return "SecondLife.exe"
- elif self.args['grid'] == 'firstlook':
- return "SecondLifeFirstLook.exe"
+ if self.default_channel():
+ if self.default_grid():
+ return "SecondLife.exe"
+ else:
+ return "SecondLifePreview.exe"
else:
- return "SecondLifePreview.exe"
- # return "SecondLifePreview%s.exe" % (self.args['grid'], )
+ return ''.join(self.args['channel'].split()) + '.exe'
+
def construct(self):
super(WindowsManifest, self).construct()
@@ -154,6 +171,20 @@ class WindowsManifest(ViewerManifest):
self.path("res/*/*")
self.end_prefix()
+ # Vivox runtimes
+ if self.prefix(src="vivox-runtime/i686-win32", dst=""):
+ self.path("SLVoice.exe")
+ self.path("SLVoiceAgent.exe")
+ self.path("libeay32.dll")
+ self.path("srtp.dll")
+ self.path("ssleay32.dll")
+ self.path("tntk.dll")
+ self.path("alut.dll")
+ self.path("vivoxsdk.dll")
+ self.path("ortp.dll")
+ self.path("wrap_oal.dll")
+ self.end_prefix()
+
# pull in the crash logger and updater from other projects
self.path(src="../win_crash_logger/win_crash_logger.exe", dst="win_crash_logger.exe")
self.path(src="../win_updater/updater.exe", dst="updater.exe")
@@ -203,49 +234,75 @@ class WindowsManifest(ViewerManifest):
return result
def package_finish(self):
- version_vars_template = """
+ # a standard map of strings for replacing in the templates
+ substitution_strings = {
+ 'version' : '.'.join(self.args['version']),
+ 'version_short' : '.'.join(self.args['version'][:-1]),
+ 'version_dashes' : '-'.join(self.args['version']),
+ 'final_exe' : self.final_exe(),
+ 'grid':self.args['grid'],
+ 'grid_caps':self.args['grid'].upper(),
+ # escape quotes becase NSIS doesn't handle them well
+ 'flags':self.flags_list().replace('"', '$\\"'),
+ 'channel':self.args['channel'],
+ 'channel_oneword':self.channel_oneword(),
+ 'channel_unique':self.channel_unique(),
+ }
+
+ version_vars = """
!define INSTEXE "%(final_exe)s"
!define VERSION "%(version_short)s"
!define VERSION_LONG "%(version)s"
!define VERSION_DASHES "%(version_dashes)s"
- """
- if(self.args['grid'] == ''):
- installer_file = "Second Life %(version_dashes)s Setup.exe"
- grid_vars_template = """
- OutFile "%(outfile)s"
- !define INSTFLAGS "%(flags)s"
- !define INSTNAME "SecondLife"
- !define SHORTCUT "Second Life"
- !define URLNAME "secondlife"
- Caption "Second Life ${VERSION}"
- """
+ """ % substitution_strings
+ if self.default_channel():
+ if self.default_grid():
+ # release viewer
+ installer_file = "Second Life %(version_dashes)s Setup.exe"
+ grid_vars_template = """
+ OutFile "%(installer_file)s"
+ !define INSTFLAGS "%(flags)s"
+ !define INSTNAME "SecondLife"
+ !define SHORTCUT "Second Life"
+ !define URLNAME "secondlife"
+ Caption "Second Life ${VERSION}"
+ """
+ else:
+ # beta grid viewer
+ installer_file = "Second Life %(version_dashes)s (%(grid_caps)s) Setup.exe"
+ grid_vars_template = """
+ OutFile "%(installer_file)s"
+ !define INSTFLAGS "%(flags)s"
+ !define INSTNAME "SecondLife%(grid_caps)s"
+ !define SHORTCUT "Second Life (%(grid_caps)s)"
+ !define URLNAME "secondlife%(grid)s"
+ !define UNINSTALL_SETTINGS 1
+ Caption "Second Life %(grid)s ${VERSION}"
+ """
else:
- installer_file = "Second Life %(version_dashes)s (%(grid_caps)s) Setup.exe"
+ # some other channel on some grid
+ installer_file = "Second Life %(version_dashes)s %(channel_unique)s Setup.exe"
grid_vars_template = """
- OutFile "%(outfile)s"
+ OutFile "%(installer_file)s"
!define INSTFLAGS "%(flags)s"
- !define INSTNAME "SecondLife%(grid_caps)s"
- !define SHORTCUT "Second Life (%(grid_caps)s)"
- !define URLNAME "secondlife%(grid)s"
+ !define INSTNAME "SecondLife%(channel_oneword)s"
+ !define SHORTCUT "%(channel)s"
+ !define URLNAME "secondlife"
!define UNINSTALL_SETTINGS 1
- Caption "Second Life %(grid)s ${VERSION}"
+ Caption "%(channel)s ${VERSION}"
"""
if(self.args.has_key('installer_name')):
installer_file = self.args['installer_name']
else:
- installer_file = installer_file % {'version_dashes' : '-'.join(self.args['version']),
- 'grid_caps' : self.args['grid'].upper()}
- tempfile = "../secondlife_setup.nsi"
- # the following is an odd sort of double-string replacement
+ installer_file = installer_file % substitution_strings
+ substitution_strings['installer_file'] = installer_file
+
+ tempfile = "../secondlife_setup_tmp.nsi"
+ # the following replaces strings in the nsi template
+ # it also does python-style % substitution
self.replace_in("installers/windows/installer_template.nsi", tempfile, {
- "%%VERSION%%":version_vars_template%{'version_short' : '.'.join(self.args['version'][:-1]),
- 'version' : '.'.join(self.args['version']),
- 'version_dashes' : '-'.join(self.args['version']),
- 'final_exe' : self.final_exe()},
- "%%GRID_VARS%%":grid_vars_template%{'grid':self.args['grid'],
- 'grid_caps':self.args['grid'].upper(),
- 'outfile':installer_file,
- 'flags':self.flags_list()},
+ "%%VERSION%%":version_vars,
+ "%%GRID_VARS%%":grid_vars_template % substitution_strings,
"%%INSTALL_FILES%%":self.nsi_file_commands(True),
"%%DELETE_FILES%%":self.nsi_file_commands(False)})
@@ -307,16 +364,26 @@ class DarwinManifest(ViewerManifest):
def package_finish(self):
+ channel_standin = 'Second Life' # hah, our default channel is not usable on its own
+ if not self.default_channel():
+ channel_standin = self.args['channel']
+
imagename="SecondLife_" + '_'.join(self.args['version'])
- if(self.args['grid'] != ''):
- imagename = imagename + '_' + self.args['grid'].upper()
+ if self.default_channel():
+ if not self.default_grid():
+ # beta case
+ imagename = imagename + '_' + self.args['grid'].upper()
+ else:
+ # first look, etc
+ imagename = imagename + '_' + self.channel_oneword().upper()
sparsename = imagename + ".sparseimage"
finalname = imagename + ".dmg"
# make sure we don't have stale files laying about
self.remove(sparsename, finalname)
- self.run_command('hdiutil create "%(sparse)s" -volname "Second Life" -fs HFS+ -type SPARSE -megabytes 300' % {'sparse':sparsename})
+ self.run_command('hdiutil create "%(sparse)s" -volname "Second Life" -fs HFS+ -type SPARSE -megabytes 300' % {
+ 'sparse':sparsename})
# mount the image and get the name of the mount point and device node
hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"')
@@ -324,15 +391,17 @@ class DarwinManifest(ViewerManifest):
volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip()
# Copy everything in to the mounted .dmg
- # TODO change name of .app once mac_updater can handle it.
- for s,d in {
- self.get_dst_prefix():"Second Life.app",
- "lsl_guide.html":"Linden Scripting Language Guide.html",
- "releasenotes.txt":"Release Notes.txt",
- "installers/darwin/mac_image_hidden":".hidden",
- "installers/darwin/mac_image_background.tga":"background.tga",
- "installers/darwin/mac_image_DS_Store":".DS_Store"}.items():
+ if self.default_channel() and not self.default_grid():
+ app_name = "Second Life " + self.args['grid']
+ else:
+ app_name = channel_standin.strip()
+ for s,d in {self.get_dst_prefix():app_name + ".app",
+ "lsl_guide.html":"Linden Scripting Language Guide.html",
+ "releasenotes.txt":"Release Notes.txt",
+ "installers/darwin/mac_image_hidden":".hidden",
+ "installers/darwin/mac_image_background.tga":"background.tga",
+ "installers/darwin/mac_image_DS_Store":".DS_Store"}.items():
print "Copying to dmg", s, d
self.copy_action(self.src_path_of(s), os.path.join(volpath, d))