llfloateractivespeakers.cpp 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
  1. /**
  2. * @file llfloateractivespeakers.cpp
  3. * @brief Management interface for muting and controlling volume of residents
  4. * currently speaking
  5. *
  6. * $LicenseInfo:firstyear=2005&license=viewergpl$
  7. *
  8. * Copyright (c) 2005-2009, Linden Research, Inc.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. #include "llfloateractivespeakers.h"
  35. #include "llbutton.h"
  36. #include "llcachename.h"
  37. #include "llcorehttputil.h"
  38. #include "llscrolllistctrl.h"
  39. #include "llsdutil.h"
  40. #include "llsliderctrl.h"
  41. #include "lltextbox.h"
  42. #include "lluictrlfactory.h"
  43. #include "llagent.h"
  44. #include "llappviewer.h"
  45. #include "llfloateravatarinfo.h"
  46. #include "llfloaterim.h"
  47. #include "llfloaterobjectiminfo.h"
  48. #include "llfloatervoicedevicesettings.h"
  49. #include "llimmgr.h"
  50. #include "llmutelist.h"
  51. //MK
  52. #include "mkrlinterface.h"
  53. //mk
  54. #include "llviewercontrol.h"
  55. #include "llviewerobjectlist.h"
  56. #include "llviewerwindow.h"
  57. #include "llvoavatar.h"
  58. #include "llvoicechannel.h"
  59. #include "llworld.h"
  60. using namespace LLOldEvents;
  61. // Seconds of not being on voice channel before removed from list of active
  62. // speakers
  63. constexpr F32 SPEAKER_TIMEOUT = 10.f;
  64. // Seconds of mouse inactivity before it's ok to sort regardless of
  65. // mouse-in-view.
  66. constexpr F32 RESORT_TIMEOUT = 5.f;
  67. const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);
  68. const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f);
  69. LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name,
  70. ESpeakerType type, ESpeakerStatus status)
  71. : mStatus(status),
  72. mLastSpokeTime(0.f),
  73. mSpeechVolume(0.f),
  74. mHasSpoken(false),
  75. mDotColor(LLColor4::white),
  76. mID(id),
  77. mTyping(false),
  78. mSortIndex(0),
  79. mType(type),
  80. mIsModerator(false),
  81. mModeratorMutedVoice(false),
  82. mModeratorMutedText(false),
  83. mNeedsResort(true)
  84. {
  85. if (name.empty() && type == SPEAKER_AGENT)
  86. {
  87. lookupName();
  88. }
  89. else
  90. {
  91. mDisplayName = mLegacyName = name;
  92. }
  93. gVoiceClient.setUserVolume(id, LLMuteList::getSavedResidentVolume(id));
  94. mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
  95. }
  96. void LLSpeaker::lookupName()
  97. {
  98. LLAvatarNameCache::get(mID,
  99. boost::bind(&LLSpeaker::onAvatarNameLookup, _1, _2,
  100. new LLHandle<LLSpeaker>(getHandle())));
  101. }
  102. //static
  103. void LLSpeaker::onAvatarNameLookup(const LLUUID& id,
  104. const LLAvatarName& avatar_name,
  105. void* user_data)
  106. {
  107. LLSpeaker* speaker_ptr = ((LLHandle<LLSpeaker>*)user_data)->get();
  108. if (speaker_ptr)
  109. {
  110. // Must keep "Resident" last names, thus the "true"
  111. speaker_ptr->mLegacyName = avatar_name.getLegacyName(true);
  112. if (!LLAvatarName::sLegacyNamesForSpeakers &&
  113. LLAvatarNameCache::useDisplayNames())
  114. {
  115. // Always show "Display Name [Legacy Name]" for security reasons
  116. speaker_ptr->setDisplayName(avatar_name.getNames());
  117. }
  118. else
  119. {
  120. // "Resident" last names stripped when appropriate
  121. speaker_ptr->setDisplayName(avatar_name.getLegacyName());
  122. }
  123. }
  124. delete (LLHandle<LLSpeaker>*)user_data;
  125. }
  126. LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source)
  127. : LLEvent(source, "Speaker text moderation event")
  128. {
  129. }
  130. LLSD LLSpeakerTextModerationEvent::getValue()
  131. {
  132. return std::string("text");
  133. }
  134. LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source)
  135. : LLEvent(source, "Speaker voice moderation event")
  136. {
  137. }
  138. LLSD LLSpeakerVoiceModerationEvent::getValue()
  139. {
  140. return std::string("voice");
  141. }
  142. LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source,
  143. const LLUUID& speaker_id)
  144. : LLEvent(source, "Speaker added/removed from speaker mgr"),
  145. mSpeakerID(speaker_id)
  146. {
  147. }
  148. LLSD LLSpeakerListChangeEvent::getValue()
  149. {
  150. return mSpeakerID;
  151. }
  152. // Helper sort class
  153. struct LLSortRecentSpeakers
  154. {
  155. bool operator()(const LLPointer<LLSpeaker> lhs,
  156. const LLPointer<LLSpeaker> rhs) const;
  157. };
  158. bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs,
  159. const LLPointer<LLSpeaker> rhs) const
  160. {
  161. // Sort first on status
  162. if (lhs->mStatus != rhs->mStatus)
  163. {
  164. return lhs->mStatus < rhs->mStatus;
  165. }
  166. // And then on last speaking time
  167. if (lhs->mLastSpokeTime != rhs->mLastSpokeTime)
  168. {
  169. return lhs->mLastSpokeTime > rhs->mLastSpokeTime;
  170. }
  171. // And finally (only if those are both equal), on name.
  172. return lhs->mDisplayName.compare(rhs->mDisplayName) < 0;
  173. }
  174. //
  175. // LLFloaterActiveSpeakers
  176. //
  177. LLFloaterActiveSpeakers::LLFloaterActiveSpeakers(const LLSD& seed)
  178. : mPanel(NULL)
  179. {
  180. mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel,
  181. NULL);
  182. // Do not automatically open singleton floaters (as result of getInstance())
  183. bool no_open = false;
  184. LLUICtrlFactory::getInstance()->buildFloater(this,
  185. "floater_active_speakers.xml",
  186. &getFactoryMap(), no_open);
  187. #if 0
  188. // RN: for now, we poll voice client every frame to get voice amplitude
  189. // feedback
  190. gVoiceClient.addObserver(this);
  191. #endif
  192. mPanel->refreshSpeakers(true);
  193. }
  194. void LLFloaterActiveSpeakers::onOpen()
  195. {
  196. gSavedSettings.setBool("ShowActiveSpeakers", true);
  197. }
  198. void LLFloaterActiveSpeakers::onClose(bool app_quitting)
  199. {
  200. if (!app_quitting)
  201. {
  202. gSavedSettings.setBool("ShowActiveSpeakers", false);
  203. }
  204. setVisible(false);
  205. }
  206. void LLFloaterActiveSpeakers::draw()
  207. {
  208. // Update state every frame to get live amplitude feedback
  209. mPanel->refreshSpeakers();
  210. LLFloater::draw();
  211. }
  212. bool LLFloaterActiveSpeakers::postBuild()
  213. {
  214. mPanel = getChild<LLPanelActiveSpeakers>("active_speakers_panel");
  215. return true;
  216. }
  217. //static
  218. void* LLFloaterActiveSpeakers::createSpeakersPanel(void* data)
  219. {
  220. // Do not show text only speakers
  221. return new LLPanelActiveSpeakers(LLActiveSpeakerMgr::getInstance(), false);
  222. }
  223. //
  224. // LLPanelActiveSpeakers::SpeakerMuteListener
  225. //
  226. bool LLPanelActiveSpeakers::SpeakerMuteListener::handleEvent(LLPointer<LLEvent> event,
  227. const LLSD& userdata)
  228. {
  229. LLPointer<LLSpeaker> speakerp = (LLSpeaker*)event->getSource();
  230. if (speakerp.isNull()) return false;
  231. // Update UI on confirmation of moderator mutes
  232. if (mPanel->mModeratorAllowVoiceCtrl)
  233. {
  234. if (event->getValue().asString() == "voice")
  235. {
  236. mPanel->mModeratorAllowVoiceCtrl->setValue(!speakerp->mModeratorMutedVoice);
  237. }
  238. }
  239. if (mPanel->mModeratorAllowTextCtrl)
  240. {
  241. if (event->getValue().asString() == "text")
  242. {
  243. mPanel->mModeratorAllowTextCtrl->setValue(!speakerp->mModeratorMutedText);
  244. }
  245. }
  246. return true;
  247. }
  248. //
  249. // LLPanelActiveSpeakers::SpeakerAddListener
  250. //
  251. bool LLPanelActiveSpeakers::SpeakerAddListener::handleEvent(LLPointer<LLEvent> event,
  252. const LLSD& userdata)
  253. {
  254. mPanel->addSpeaker(event->getValue().asUUID());
  255. return true;
  256. }
  257. //
  258. // LLPanelActiveSpeakers::SpeakerRemoveListener
  259. //
  260. bool LLPanelActiveSpeakers::SpeakerRemoveListener::handleEvent(LLPointer<LLEvent> event,
  261. const LLSD& userdata)
  262. {
  263. mPanel->removeSpeaker(event->getValue().asUUID());
  264. return true;
  265. }
  266. //
  267. // LLPanelActiveSpeakers::SpeakerClearListener
  268. //
  269. bool LLPanelActiveSpeakers::SpeakerClearListener::handleEvent(LLPointer<LLEvent> event,
  270. const LLSD& userdata)
  271. {
  272. mPanel->mSpeakerList->clearRows();
  273. return true;
  274. }
  275. //
  276. // LLPanelActiveSpeakers
  277. //
  278. LLPanelActiveSpeakers::LLPanelActiveSpeakers(LLSpeakerMgr* data_source,
  279. bool show_text_chatters)
  280. : mSpeakerList(NULL),
  281. mModerationPanel(NULL),
  282. mModerationControls(NULL),
  283. mSpeakerVolumeSlider(NULL),
  284. mMuteVoiceCtrl(NULL),
  285. mMuteTextCtrl(NULL),
  286. mModeratorAllowVoiceCtrl(NULL),
  287. mModeratorAllowTextCtrl(NULL),
  288. mModerationModeCtrl(NULL),
  289. mModeratorControlsText(NULL),
  290. mNameText(NULL),
  291. mProfileBtn(NULL),
  292. mShowTextChatters(show_text_chatters),
  293. mSpeakerMgr(data_source)
  294. {
  295. setMouseOpaque(false);
  296. mSpeakerMuteListener = new SpeakerMuteListener(this);
  297. mSpeakerAddListener = new SpeakerAddListener(this);
  298. mSpeakerRemoveListener = new SpeakerRemoveListener(this);
  299. mSpeakerClearListener = new SpeakerClearListener(this);
  300. mSpeakerMgr->addListener(mSpeakerAddListener, "add");
  301. mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove");
  302. mSpeakerMgr->addListener(mSpeakerClearListener, "clear");
  303. }
  304. bool LLPanelActiveSpeakers::postBuild()
  305. {
  306. std::string sort_column = gSavedSettings.getString("FloaterActiveSpeakersSortColumn");
  307. bool sort_ascending = gSavedSettings.getBool("FloaterActiveSpeakersSortAscending");
  308. mSpeakerList = getChild<LLScrollListCtrl>("speakers_list");
  309. mSpeakerList->sortByColumn(sort_column, sort_ascending);
  310. mSpeakerList->setDoubleClickCallback(onDoubleClickSpeaker);
  311. mSpeakerList->setCommitOnSelectionChange(true);
  312. mSpeakerList->setCommitCallback(onSelectSpeaker);
  313. mSpeakerList->setSortChangedCallback(onSortChanged);
  314. mSpeakerList->setCallbackUserData(this);
  315. mMuteTextCtrl = getChild<LLUICtrl>("mute_text_btn", true, false);
  316. if (mMuteTextCtrl)
  317. {
  318. childSetCommitCallback("mute_text_btn", onClickMuteTextCommit, this);
  319. }
  320. mMuteVoiceCtrl = getChild<LLUICtrl>("mute_check", true, false);
  321. if (mMuteVoiceCtrl)
  322. {
  323. // For the mute check box, in floater_chat_history.xml
  324. childSetCommitCallback("mute_check", onClickMuteVoiceCommit, this);
  325. }
  326. LLButton* buttonp = getChild<LLButton>("mute_btn", true, false);
  327. if (buttonp)
  328. {
  329. // For the mute buttons, everywhere else
  330. buttonp->setClickedCallback(onClickMuteVoice, this);
  331. }
  332. mDevicesBtn = getChild<LLButton>("devices_btn", true, false);
  333. if (mDevicesBtn)
  334. {
  335. mDevicesBtn->setClickedCallback(onClickDeviceSettings, this);
  336. }
  337. mSpeakerVolumeSlider = getChild<LLSliderCtrl>("speaker_volume", true,
  338. false);
  339. if (mSpeakerVolumeSlider)
  340. {
  341. mSpeakerVolumeSlider->setCommitCallback(onVolumeChange);
  342. mSpeakerVolumeSlider->setCallbackUserData(this);
  343. }
  344. mNameText = getChild<LLTextBox>("resident_name", true, false);
  345. mProfileBtn = getChild<LLButton>("profile_btn", true, false);
  346. if (mProfileBtn)
  347. {
  348. childSetAction("profile_btn", onClickProfile, this);
  349. }
  350. mModeratorAllowVoiceCtrl = getChild<LLUICtrl>("moderator_allow_voice",
  351. true, false);
  352. if (mModeratorAllowVoiceCtrl)
  353. {
  354. mModeratorAllowVoiceCtrl->setCommitCallback(onModeratorMuteVoice);
  355. mModeratorAllowVoiceCtrl->setCallbackUserData(this);
  356. mModeratorAllowTextCtrl = getChild<LLUICtrl>("moderator_allow_text",
  357. true, false);
  358. if (mModeratorAllowTextCtrl)
  359. {
  360. mModeratorAllowTextCtrl->setCommitCallback(onModeratorMuteText);
  361. mModeratorAllowTextCtrl->setCallbackUserData(this);
  362. }
  363. mModerationModeCtrl = getChild<LLUICtrl>("moderation_mode",
  364. true, false);
  365. if (mModerationModeCtrl)
  366. {
  367. mModerationModeCtrl->setCommitCallback(onChangeModerationMode);
  368. mModerationModeCtrl->setCallbackUserData(this);
  369. }
  370. mModeratorControlsText = getChild<LLTextBox>("moderator_controls_label",
  371. true, false);
  372. mModerationPanel = getChild<LLView>("moderation_mode_panel",
  373. true, false);
  374. mModerationControls = getChild<LLView>("moderator_controls",
  375. true, false);
  376. }
  377. // Update speaker UI
  378. handleSpeakerSelect();
  379. return true;
  380. }
  381. void LLPanelActiveSpeakers::addSpeaker(const LLUUID& speaker_id, bool force)
  382. {
  383. if (speaker_id.isNull() || mSpeakerList->getItemIndex(speaker_id) >= 0 ||
  384. !gCacheNamep)
  385. {
  386. // Already have this speaker
  387. return;
  388. }
  389. LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id);
  390. if (force && speakerp.isNull())
  391. {
  392. llinfos << "Force-adding absent speaker: " << speaker_id << llendl;
  393. speakerp = mSpeakerMgr->setSpeaker(speaker_id);
  394. // The "add" event that results from the above call will automatically
  395. // re-call this method.
  396. return;
  397. }
  398. if (speakerp.notNull())
  399. {
  400. // Since we are forced to sort by text, encode sort order as string
  401. std::string speaking_order_sort_string = llformat("%010d",
  402. speakerp->mSortIndex);
  403. LLSD row;
  404. row["id"] = speaker_id;
  405. LLSD& columns = row["columns"];
  406. columns[0]["column"] = "icon_speaking_status";
  407. columns[0]["type"] = "icon";
  408. columns[0]["value"] = "icn_active-speakers-dot-lvl0.tga";
  409. std::string speaker_name;
  410. if (speakerp->mDisplayName.empty())
  411. {
  412. speaker_name = gCacheNamep->getDefaultName();
  413. }
  414. else
  415. {
  416. speaker_name = speakerp->mDisplayName;
  417. }
  418. columns[1]["column"] = "speaker_name";
  419. columns[1]["type"] = "text";
  420. columns[1]["value"] = speaker_name;
  421. columns[2]["column"] = "speaking_status";
  422. columns[2]["type"] = "text";
  423. // Print speaking ordinal in a text-sorting friendly manner
  424. columns[2]["value"] = speaking_order_sort_string;
  425. mSpeakerList->addElement(row);
  426. }
  427. }
  428. void LLPanelActiveSpeakers::removeSpeaker(const LLUUID& speaker_id)
  429. {
  430. mSpeakerList->deleteSingleItem(mSpeakerList->getItemIndex(speaker_id));
  431. }
  432. void LLPanelActiveSpeakers::handleSpeakerSelect()
  433. {
  434. LLUUID speaker_id = mSpeakerList->getValue().asUUID();
  435. LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id);
  436. if (speakerp.notNull())
  437. {
  438. // Since setting these values is delayed by a round trip to the Vivox
  439. // servers update them only when selecting a new speaker or
  440. // asynchronously when an update arrives
  441. if (mModeratorAllowVoiceCtrl)
  442. {
  443. mModeratorAllowVoiceCtrl->setValue(speakerp ?
  444. !speakerp->mModeratorMutedVoice :
  445. true);
  446. }
  447. if (mModeratorAllowTextCtrl)
  448. {
  449. mModeratorAllowTextCtrl->setValue(speakerp ?
  450. !speakerp->mModeratorMutedText :
  451. true);
  452. }
  453. mSpeakerMuteListener->clearDispatchers();
  454. speakerp->addListener(mSpeakerMuteListener);
  455. }
  456. }
  457. void LLPanelActiveSpeakers::refreshSpeakers(bool force)
  458. {
  459. if (mDevicesBtn)
  460. {
  461. mDevicesBtn->setEnabled(!gVoiceClient.tuningModeActive());
  462. }
  463. static const LLUIImagePtr icon_image_0 =
  464. LLUI::getUIImage("icn_active-speakers-dot-lvl0.tga");
  465. static const LLUIImagePtr icon_image_1 =
  466. LLUI::getUIImage("icn_active-speakers-dot-lvl1.tga");
  467. static const LLUIImagePtr icon_image_2 =
  468. LLUI::getUIImage("icn_active-speakers-dot-lvl2.tga");
  469. static const LLUIImagePtr mute_icon_image =
  470. LLUI::getUIImage("mute_icon.tga");
  471. if (!gCacheNamep)
  472. {
  473. return;
  474. }
  475. //MK
  476. if (gRLenabled && (gRLInterface.mContainsShownames ||
  477. gRLInterface.mContainsShowNearby))
  478. {
  479. mSpeakerList->clearRows();
  480. return;
  481. }
  482. //mk
  483. // Store off current selection and scroll state to preserve across list
  484. // rebuilds
  485. LLUUID selected_id = mSpeakerList->getSelectedValue().asUUID();
  486. S32 scroll_pos = mSpeakerList->getScrollPos();
  487. // Decide whether it is ok to resort the list then update the speaker
  488. // manager appropriately. Rapid resorting by activity makes it hard to
  489. // interact with speakers in the list so we freeze the sorting while the
  490. // user appears to be interacting with the control. We assume this is the
  491. // case whenever the mouse pointer is within the active speaker panel and
  492. // has not been motionless for more than a few seconds. see DEV-6655 -MG
  493. LLRect screen_rect;
  494. localRectToScreen(getLocalRect(), &screen_rect);
  495. bool mouse_in_view = screen_rect.pointInRect(gViewerWindowp->getCurrentMouseX(),
  496. gViewerWindowp->getCurrentMouseY());
  497. F32 mouse_last_movement = gMouseIdleTimer.getElapsedTimeF32();
  498. bool sort_ok = force || !mouse_in_view ||
  499. mouse_last_movement >= RESORT_TIMEOUT;
  500. mSpeakerMgr->update(sort_ok);
  501. std::vector<LLScrollListItem*> items = mSpeakerList->getAllData();
  502. LLSpeakerMgr::speaker_list_t speaker_list;
  503. mSpeakerMgr->getSpeakerList(&speaker_list, mShowTextChatters);
  504. for (std::vector<LLScrollListItem*>::iterator item_it = items.begin(),
  505. end = items.end();
  506. item_it != end; ++item_it)
  507. {
  508. LLScrollListItem* itemp = *item_it;
  509. LLUUID speaker_id = itemp->getUUID();
  510. LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id);
  511. if (speakerp.isNull())
  512. {
  513. continue;
  514. }
  515. // Since we are forced to sort by text, encode sort order as string
  516. std::string speaking_order_sort_string = llformat("%010d",
  517. speakerp->mSortIndex);
  518. LLScrollListIcon* icon_cell =
  519. dynamic_cast<LLScrollListIcon*>(itemp->getColumn(0));
  520. if (icon_cell)
  521. {
  522. LLUIImagePtr icon_image_id;
  523. S32 icon_image_idx = llmin(2,
  524. llfloor(3.f * speakerp->mSpeechVolume /
  525. OVERDRIVEN_POWER_LEVEL));
  526. switch (icon_image_idx)
  527. {
  528. case 0:
  529. icon_image_id = icon_image_0;
  530. break;
  531. case 1:
  532. icon_image_id = icon_image_1;
  533. break;
  534. case 2:
  535. icon_image_id = icon_image_2;
  536. }
  537. LLColor4 icon_color;
  538. if (speakerp->mStatus == LLSpeaker::STATUS_MUTED)
  539. {
  540. icon_cell->setImage(mute_icon_image);
  541. if (speakerp->mModeratorMutedVoice)
  542. {
  543. icon_color.set(0.5f, 0.5f, 0.5f, 1.f);
  544. }
  545. else
  546. {
  547. icon_color.set(1.f, 71.f / 255.f, 71.f / 255.f, 1.f);
  548. }
  549. }
  550. else
  551. {
  552. icon_cell->setImage(icon_image_id);
  553. icon_color = speakerp->mDotColor;
  554. // If voice is disabled for this speaker
  555. if (speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE)
  556. {
  557. // Non voice speakers have hidden icons, render as
  558. // transparent
  559. icon_color.set(0.f, 0.f, 0.f, 0.f);
  560. }
  561. }
  562. icon_cell->setColor(icon_color);
  563. // If voice is disabled for this speaker
  564. if (speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE &&
  565. speakerp->mStatus != LLSpeaker::STATUS_MUTED)
  566. {
  567. // Non voice speakers have hidden icons, render as transparent
  568. icon_cell->setColor(LLColor4::transparent);
  569. }
  570. }
  571. // Update name column
  572. LLScrollListCell* name_cell = itemp->getColumn(1);
  573. if (name_cell)
  574. {
  575. // *FIXME: remove hard coding of font colors
  576. if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL)
  577. {
  578. // Draw inactive speakers in gray
  579. name_cell->setColor(LLColor4::grey4);
  580. }
  581. else
  582. {
  583. name_cell->setColor(LLColor4::black);
  584. }
  585. std::string speaker_name;
  586. if (speakerp->mDisplayName.empty())
  587. {
  588. speaker_name = gCacheNamep->getDefaultName();
  589. }
  590. else
  591. {
  592. speaker_name = speakerp->mDisplayName;
  593. }
  594. if (speakerp->mIsModerator)
  595. {
  596. speaker_name += " " + getString("moderator_label");
  597. }
  598. name_cell->setValue(speaker_name);
  599. LLScrollListText* text_cell =
  600. dynamic_cast<LLScrollListText*>(name_cell);
  601. if (text_cell)
  602. {
  603. text_cell->setFontStyle(speakerp->mIsModerator ?
  604. LLFontGL::BOLD : LLFontGL::NORMAL);
  605. }
  606. }
  607. // Update speaking order column
  608. LLScrollListCell* speaking_status_cell = itemp->getColumn(2);
  609. if (speaking_status_cell)
  610. {
  611. // Print speaking ordinal in a text-sorting friendly manner
  612. speaking_status_cell->setValue(speaking_order_sort_string);
  613. }
  614. }
  615. // We potentially modified the sort order by touching the list items
  616. mSpeakerList->setSorted(false);
  617. LLPointer<LLSpeaker> selected_speakerp = mSpeakerMgr->findSpeaker(selected_id);
  618. // Update UI for selected participant
  619. bool valid_speaker = selected_id.notNull() && selected_id != gAgentID &&
  620. selected_speakerp.notNull();
  621. bool speaker_on_voice = LLVoiceClient::voiceEnabled() &&
  622. gVoiceClient.getVoiceEnabled(selected_id);
  623. if (mMuteVoiceCtrl)
  624. {
  625. mMuteVoiceCtrl->setValue(LLMuteList::isMuted(selected_id,
  626. LLMute::flagVoiceChat));
  627. mMuteVoiceCtrl->setEnabled(speaker_on_voice && valid_speaker &&
  628. (selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT ||
  629. selected_speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL));
  630. }
  631. if (mMuteTextCtrl)
  632. {
  633. mMuteTextCtrl->setValue(LLMuteList::isMuted(selected_id,
  634. LLMute::flagTextChat));
  635. mMuteTextCtrl->setEnabled(valid_speaker &&
  636. selected_speakerp->mType != LLSpeaker::SPEAKER_EXTERNAL &&
  637. !LLMuteList::isLinden(selected_speakerp->mLegacyName));
  638. }
  639. if (mSpeakerVolumeSlider)
  640. {
  641. mSpeakerVolumeSlider->setValue(gVoiceClient.getUserVolume(selected_id));
  642. mSpeakerVolumeSlider->setEnabled(speaker_on_voice && valid_speaker &&
  643. (selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT ||
  644. selected_speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL));
  645. }
  646. if (mModeratorAllowVoiceCtrl)
  647. {
  648. mModeratorAllowVoiceCtrl->setEnabled(selected_id.notNull() &&
  649. mSpeakerMgr->isVoiceActive() &&
  650. gVoiceClient.getVoiceEnabled(selected_id));
  651. }
  652. if (mModeratorAllowTextCtrl)
  653. {
  654. mModeratorAllowTextCtrl->setEnabled(selected_id.notNull());
  655. }
  656. if (mModeratorControlsText)
  657. {
  658. mModeratorControlsText->setEnabled(selected_id.notNull());
  659. }
  660. if (mProfileBtn)
  661. {
  662. mProfileBtn->setEnabled(selected_id.notNull() &&
  663. selected_speakerp.notNull() &&
  664. selected_speakerp->mType != LLSpeaker::SPEAKER_EXTERNAL);
  665. }
  666. // Show selected user name in large font
  667. if (mNameText)
  668. {
  669. if (selected_speakerp)
  670. {
  671. mNameText->setValue(selected_speakerp->mDisplayName);
  672. }
  673. else
  674. {
  675. mNameText->setValue(LLStringUtil::null);
  676. }
  677. }
  678. if (mModeratorAllowVoiceCtrl)
  679. {
  680. // Update moderator capabilities
  681. LLPointer<LLSpeaker> self_speakerp = mSpeakerMgr->findSpeaker(gAgentID);
  682. if (self_speakerp.notNull())
  683. {
  684. bool moderator = self_speakerp->mIsModerator;
  685. if (mModerationPanel)
  686. {
  687. mModerationPanel->setVisible(moderator &&
  688. mSpeakerMgr->isVoiceActive());
  689. }
  690. if (mModerationControls)
  691. {
  692. mModerationControls->setVisible(moderator);
  693. }
  694. }
  695. }
  696. // Keep scroll value stable
  697. mSpeakerList->setScrollPos(scroll_pos);
  698. }
  699. void LLPanelActiveSpeakers::setSpeaker(const LLUUID& id,
  700. const std::string& name,
  701. LLSpeaker::ESpeakerStatus status,
  702. LLSpeaker::ESpeakerType type,
  703. const LLUUID& owner_id)
  704. {
  705. mSpeakerMgr->setSpeaker(id, name, status, type, owner_id);
  706. }
  707. void LLPanelActiveSpeakers::setVoiceModerationCtrlMode(const bool& moderated_voice)
  708. {
  709. if (mModerationModeCtrl)
  710. {
  711. std::string value = moderated_voice ? "moderated" : "unmoderated";
  712. mModerationModeCtrl->setValue(value);
  713. }
  714. }
  715. //static
  716. void LLPanelActiveSpeakers::onClickMuteTextCommit(LLUICtrl* ctrl,
  717. void* user_data)
  718. {
  719. LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
  720. if (!panelp) return;
  721. LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
  722. bool is_muted = LLMuteList::isMuted(speaker_id, LLMute::flagTextChat);
  723. //fill in name using voice client's copy of name cache
  724. LLPointer<LLSpeaker> speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id);
  725. if (speakerp.isNull())
  726. {
  727. return;
  728. }
  729. std::string name = speakerp->mLegacyName;
  730. LLMute mute(speaker_id, name,
  731. speakerp->mType == LLSpeaker::SPEAKER_AGENT ? LLMute::AGENT
  732. : LLMute::OBJECT);
  733. if (!is_muted)
  734. {
  735. LLMuteList::add(mute, LLMute::flagTextChat);
  736. }
  737. else
  738. {
  739. LLMuteList::remove(mute, LLMute::flagTextChat);
  740. }
  741. }
  742. //static
  743. void LLPanelActiveSpeakers::onClickMuteVoice(void* user_data)
  744. {
  745. onClickMuteVoiceCommit(NULL, user_data);
  746. }
  747. //static
  748. void LLPanelActiveSpeakers::onClickDeviceSettings(void*)
  749. {
  750. LLFloaterVoiceDeviceSettings::showInstance();
  751. }
  752. //static
  753. void LLPanelActiveSpeakers::onClickMuteVoiceCommit(LLUICtrl* ctrl, void* user_data)
  754. {
  755. LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
  756. if (!panelp) return;
  757. LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
  758. bool is_muted = LLMuteList::isMuted(speaker_id, LLMute::flagVoiceChat);
  759. LLPointer<LLSpeaker> speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id);
  760. if (speakerp.isNull())
  761. {
  762. return;
  763. }
  764. std::string name = speakerp->mLegacyName;
  765. // Muting voice means we're dealing with an agent
  766. LLMute mute(speaker_id, name, LLMute::AGENT);
  767. if (!is_muted)
  768. {
  769. LLMuteList::add(mute, LLMute::flagVoiceChat);
  770. }
  771. else
  772. {
  773. LLMuteList::remove(mute, LLMute::flagVoiceChat);
  774. }
  775. }
  776. //static
  777. void LLPanelActiveSpeakers::onVolumeChange(LLUICtrl* source, void* user_data)
  778. {
  779. LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
  780. if (panelp && panelp->mSpeakerVolumeSlider)
  781. {
  782. LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
  783. F32 new_volume = panelp->mSpeakerVolumeSlider->getValue().asReal();
  784. gVoiceClient.setUserVolume(speaker_id, new_volume);
  785. // Store this volume setting for future sessions
  786. LLMuteList::setSavedResidentVolume(speaker_id, new_volume);
  787. }
  788. }
  789. //static
  790. void LLPanelActiveSpeakers::onClickProfile(void* user_data)
  791. {
  792. LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
  793. if (!panelp) return;
  794. LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
  795. LLPointer<LLSpeaker> speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id);
  796. if (speakerp.isNull()) return;
  797. if (speakerp->mType == LLSpeaker::SPEAKER_AGENT)
  798. {
  799. LLFloaterAvatarInfo::showFromDirectory(speaker_id);
  800. }
  801. else if (speakerp->mType == LLSpeaker::SPEAKER_OBJECT)
  802. {
  803. LLViewerObject* object = gObjectList.findObject(speaker_id);
  804. if (!object)
  805. {
  806. // Others' HUDs are not in our objects list: use the HUD owner
  807. // to find out their actual position...
  808. object = gObjectList.findObject(speakerp->mOwnerID);
  809. }
  810. if (object
  811. //MK
  812. && !(gRLenabled && gRLInterface.mContainsShowloc))
  813. //mk
  814. {
  815. LLVector3 pos = object->getPositionRegion();
  816. S32 x = ll_round((F32)fmod((F64)pos.mV[VX],
  817. (F64)REGION_WIDTH_METERS));
  818. S32 y = ll_round((F32)fmod((F64)pos.mV[VY],
  819. (F64)REGION_WIDTH_METERS));
  820. S32 z = ll_round((F32)pos.mV[VZ]);
  821. std::ostringstream location;
  822. location << object->getRegion()->getName() << "/" << x << "/"
  823. << y << "/" << z;
  824. LLObjectIMInfo::show(speaker_id, speakerp->mDisplayName,
  825. location.str(), speakerp->mOwnerID, false);
  826. }
  827. }
  828. }
  829. //static
  830. void LLPanelActiveSpeakers::onDoubleClickSpeaker(void* user_data)
  831. {
  832. LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
  833. if (!panelp) return;
  834. LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
  835. LLPointer<LLSpeaker> speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id);
  836. if (gIMMgrp && speaker_id != gAgentID && speakerp.notNull())
  837. {
  838. gIMMgrp->addSession(speakerp->mLegacyName, IM_NOTHING_SPECIAL, speaker_id);
  839. }
  840. }
  841. //static
  842. void LLPanelActiveSpeakers::onSelectSpeaker(LLUICtrl* source, void* user_data)
  843. {
  844. LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
  845. if (panelp)
  846. {
  847. panelp->handleSpeakerSelect();
  848. }
  849. }
  850. //static
  851. void LLPanelActiveSpeakers::onSortChanged(void* user_data)
  852. {
  853. LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
  854. if (panelp)
  855. {
  856. gSavedSettings.setString("FloaterActiveSpeakersSortColumn",
  857. panelp->mSpeakerList->getSortColumnName());
  858. gSavedSettings.setBool("FloaterActiveSpeakersSortAscending",
  859. panelp->mSpeakerList->getSortAscending());
  860. }
  861. }
  862. //static
  863. void LLPanelActiveSpeakers::moderatorActionFailedCallback(const LLSD& result,
  864. LLUUID session_id)
  865. {
  866. if (gIMMgrp) return; // Viewer is closing down !
  867. LLFloaterIMSession* floaterp =
  868. LLFloaterIMSession::findInstance(session_id);
  869. if (!floaterp)
  870. {
  871. llinfos << "Received a reply for closed session Id: " << session_id
  872. << ". Ignored." << llendl;
  873. return;
  874. }
  875. LLCore::HttpStatus status =
  876. LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result);
  877. if (status == gStatusForbidden)
  878. {
  879. // 403 == you are not a mod: should be disabled then.
  880. floaterp->showSessionEventError("mute", "not_a_moderator");
  881. }
  882. else
  883. {
  884. floaterp->showSessionEventError("mute", "generic");
  885. }
  886. }
  887. //static
  888. void LLPanelActiveSpeakers::onModeratorMuteVoice(LLUICtrl* ctrl,
  889. void* user_data)
  890. {
  891. LLPanelActiveSpeakers* self = (LLPanelActiveSpeakers*)user_data;
  892. if (!self || !self->mSpeakerList || !ctrl) return;
  893. const LLUUID& session_id = self->mSpeakerMgr->getSessionID();
  894. LLAgent::httpCallback_t
  895. fail = boost::bind(&LLPanelActiveSpeakers::moderatorActionFailedCallback,
  896. _1, session_id);
  897. LLSD data;
  898. data["method"] = "mute update";
  899. data["session-id"] = session_id;
  900. data["params"] = LLSD::emptyMap();
  901. data["params"]["agent_id"] = self->mSpeakerList->getValue();
  902. data["params"]["mute_info"] = LLSD::emptyMap();
  903. // Ctrl value represents ability to type, so invert
  904. data["params"]["mute_info"]["voice"] = !ctrl->getValue();
  905. if (!gAgent.requestPostCapability("ChatSessionRequest", data, NULL, fail))
  906. {
  907. llwarns << "Cannot get the ChatSessionRequest capability ! Aborted."
  908. << llendl;
  909. }
  910. }
  911. //static
  912. void LLPanelActiveSpeakers::onModeratorMuteText(LLUICtrl* ctrl,
  913. void* user_data)
  914. {
  915. LLPanelActiveSpeakers* self = (LLPanelActiveSpeakers*)user_data;
  916. if (!self || !self->mSpeakerList || !ctrl) return;
  917. const LLUUID& session_id = self->mSpeakerMgr->getSessionID();
  918. LLAgent::httpCallback_t
  919. fail = boost::bind(&LLPanelActiveSpeakers::moderatorActionFailedCallback,
  920. _1, session_id);
  921. LLSD data;
  922. data["method"] = "mute update";
  923. data["session-id"] = session_id;
  924. data["params"] = LLSD::emptyMap();
  925. data["params"]["agent_id"] = self->mSpeakerList->getValue();
  926. data["params"]["mute_info"] = LLSD::emptyMap();
  927. // Ctrl value represents ability to type, so invert
  928. data["params"]["mute_info"]["text"] = !ctrl->getValue();
  929. if (!gAgent.requestPostCapability("ChatSessionRequest", data, NULL, fail))
  930. {
  931. llwarns << "Cannot get the ChatSessionRequest capability ! Aborted."
  932. << llendl;
  933. }
  934. }
  935. //static
  936. void LLPanelActiveSpeakers::onChangeModerationMode(LLUICtrl* ctrl,
  937. void* user_data)
  938. {
  939. LLPanelActiveSpeakers* self = (LLPanelActiveSpeakers*)user_data;
  940. if (!self || !ctrl) return;
  941. const std::string& url = gAgent.getRegionCapability("ChatSessionRequest");
  942. if (url.empty())
  943. {
  944. llwarns << "Cannot get the ChatSessionRequest capability ! Aborted."
  945. << llendl;
  946. return;
  947. }
  948. LLSD data;
  949. data["method"] = "session update";
  950. data["session-id"] = self->mSpeakerMgr->getSessionID();
  951. data["params"] = LLSD::emptyMap();
  952. data["params"]["update_info"] = LLSD::emptyMap();
  953. data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap();
  954. if (ctrl->getValue().asString() == "unmoderated")
  955. {
  956. data["params"]["update_info"]["moderated_mode"]["voice"] = false;
  957. }
  958. else if (ctrl->getValue().asString() == "moderated")
  959. {
  960. data["params"]["update_info"]["moderated_mode"]["voice"] = true;
  961. }
  962. LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data,
  963. "Moderation mode changed",
  964. "Failed to change moderation mode");
  965. }
  966. //
  967. // LLSpeakerMgr
  968. //
  969. LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp)
  970. : mVoiceChannel(channelp)
  971. {
  972. }
  973. LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id,
  974. const std::string& name,
  975. LLSpeaker::ESpeakerStatus status,
  976. LLSpeaker::ESpeakerType type,
  977. const LLUUID& owner_id)
  978. {
  979. if (id.isNull()) return NULL;
  980. LLPointer<LLSpeaker> speakerp = findSpeaker(id);
  981. if (speakerp.isNull())
  982. {
  983. speakerp = new LLSpeaker(id, name, type, status);
  984. speakerp->mOwnerID = owner_id;
  985. mSpeakers[id] = speakerp;
  986. mSpeakersSorted.emplace_back(speakerp);
  987. fireEvent(new LLSpeakerListChangeEvent(this, id), "add");
  988. }
  989. else
  990. {
  991. // Keep highest priority status (lowest value) instead of overriding
  992. // current value
  993. speakerp->setStatus(llmin(speakerp->mStatus, status));
  994. speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
  995. // RN: due to a weird behavior where IMs from attached objects come
  996. // from the wearer's agent_id we need to override speakers that we
  997. // think are objects when we find out they are really residents
  998. if (type == LLSpeaker::SPEAKER_AGENT)
  999. {
  1000. speakerp->mType = LLSpeaker::SPEAKER_AGENT;
  1001. speakerp->lookupName();
  1002. }
  1003. }
  1004. return speakerp;
  1005. }
  1006. void LLSpeakerMgr::update(bool resort_ok)
  1007. {
  1008. static LLCachedControl<LLColor4> speaking(gSavedSettings,
  1009. "SpeakingColor");
  1010. static LLCachedControl<LLColor4> overdriven(gSavedSettings,
  1011. "OverdrivenColor");
  1012. LLColor4 speaking_color = speaking;
  1013. LLColor4 overdriven_color = overdriven;
  1014. bool dirty = false;
  1015. // Only allow list changes when user is not interacting with it
  1016. if (resort_ok)
  1017. {
  1018. updateSpeakerList();
  1019. dirty = true;
  1020. }
  1021. // Update status of all current speakers
  1022. bool voice_channel_active = (mVoiceChannel && mVoiceChannel->isActive()) ||
  1023. (!mVoiceChannel &&
  1024. gVoiceClient.inProximalChannel());
  1025. for (speaker_map_t::iterator it = mSpeakers.begin(), end = mSpeakers.end();
  1026. it != end; ++it)
  1027. {
  1028. LLUUID speaker_id = it->first;
  1029. LLSpeaker* speakerp = it->second;
  1030. if (voice_channel_active && gVoiceClient.getVoiceEnabled(speaker_id))
  1031. {
  1032. speakerp->mSpeechVolume = gVoiceClient.getCurrentPower(speaker_id);
  1033. bool moderator_muted_voice;
  1034. moderator_muted_voice = gVoiceClient.getIsModeratorMuted(speaker_id);
  1035. if (moderator_muted_voice != speakerp->mModeratorMutedVoice)
  1036. {
  1037. speakerp->mModeratorMutedVoice = moderator_muted_voice;
  1038. speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp));
  1039. }
  1040. if (gVoiceClient.getOnMuteList(speaker_id) ||
  1041. speakerp->mModeratorMutedVoice)
  1042. {
  1043. speakerp->setStatus(LLSpeaker::STATUS_MUTED);
  1044. }
  1045. else if (gVoiceClient.getIsSpeaking(speaker_id))
  1046. {
  1047. // Reset inactivity expiration
  1048. if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING)
  1049. {
  1050. speakerp->setSpokenTime(mSpeechTimer.getElapsedTimeF32());
  1051. }
  1052. speakerp->setStatus(LLSpeaker::STATUS_SPEAKING);
  1053. // Interpolate between active color and full speaking color
  1054. // based on power of speech output
  1055. speakerp->mDotColor = speaking_color;
  1056. if (speakerp->mSpeechVolume > OVERDRIVEN_POWER_LEVEL)
  1057. {
  1058. speakerp->mDotColor = overdriven_color;
  1059. }
  1060. }
  1061. else
  1062. {
  1063. speakerp->mSpeechVolume = 0.f;
  1064. speakerp->mDotColor = ACTIVE_COLOR;
  1065. if (speakerp->mHasSpoken)
  1066. {
  1067. // Has spoken once, not currently speaking
  1068. speakerp->setStatus(LLSpeaker::STATUS_HAS_SPOKEN);
  1069. }
  1070. else
  1071. {
  1072. // Default state for being in voice channel
  1073. speakerp->setStatus(LLSpeaker::STATUS_VOICE_ACTIVE);
  1074. }
  1075. }
  1076. if (speakerp->mNeedsResort)
  1077. {
  1078. speakerp->mNeedsResort = false;
  1079. dirty = true;
  1080. }
  1081. }
  1082. // Speaker no longer registered in voice channel, demote to text only
  1083. else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL)
  1084. {
  1085. if (speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL)
  1086. {
  1087. // External speakers should be timed out when they leave the
  1088. // voice channel (since they only exist via SLVoice)
  1089. speakerp->setStatus(LLSpeaker::STATUS_NOT_IN_CHANNEL);
  1090. }
  1091. else
  1092. {
  1093. speakerp->setStatus(LLSpeaker::STATUS_TEXT_ONLY);
  1094. speakerp->mSpeechVolume = 0.f;
  1095. speakerp->mDotColor = ACTIVE_COLOR;
  1096. }
  1097. }
  1098. }
  1099. if (!dirty)
  1100. {
  1101. return;
  1102. }
  1103. // Sort by status then time last spoken
  1104. std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(),
  1105. LLSortRecentSpeakers());
  1106. // For recent speakers who are not currently speaking, show "recent" color
  1107. // dot for most recent fading to "active" color
  1108. S32 recent_speaker_count = 0;
  1109. S32 sort_index = 0;
  1110. for (speaker_list_t::iterator it = mSpeakersSorted.begin();
  1111. it != mSpeakersSorted.end(); )
  1112. {
  1113. LLPointer<LLSpeaker> speakerp = *it;
  1114. // Color code recent speakers who are not currently speaking
  1115. if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN)
  1116. {
  1117. speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR,
  1118. clamp_rescale((F32)recent_speaker_count,
  1119. -2.f, 3.f, 0.f, 1.f));
  1120. ++recent_speaker_count;
  1121. }
  1122. // Stuff sort ordinal into speaker so the ui can sort by this value
  1123. speakerp->mSortIndex = sort_index++;
  1124. // Remove speakers that have been gone too long
  1125. if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL &&
  1126. speakerp->mActivityTimer.hasExpired())
  1127. {
  1128. fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID),
  1129. "remove");
  1130. mSpeakers.erase(speakerp->mID);
  1131. it = mSpeakersSorted.erase(it);
  1132. }
  1133. else
  1134. {
  1135. ++it;
  1136. }
  1137. }
  1138. }
  1139. void LLSpeakerMgr::updateSpeakerList()
  1140. {
  1141. // Are we bound to the currently active voice channel ?
  1142. if ((mVoiceChannel && !mVoiceChannel->isActive()) ||
  1143. (!mVoiceChannel && !gVoiceClient.inProximalChannel()))
  1144. {
  1145. return;
  1146. }
  1147. LLVoiceClient::participants_vec_t participants;
  1148. if (!gVoiceClient.getParticipants(participants))
  1149. {
  1150. return;
  1151. }
  1152. // Add new participants to our list of known speakers
  1153. for (size_t i = 0, count = participants.size(); i < count; ++i)
  1154. {
  1155. LLVoiceClient::ParticipantData& data = participants[i];
  1156. setSpeaker(data.mId, data.mName, LLSpeaker::STATUS_VOICE_ACTIVE,
  1157. (data.mIsAvatar ? LLSpeaker::SPEAKER_AGENT
  1158. : LLSpeaker::SPEAKER_EXTERNAL));
  1159. }
  1160. }
  1161. const LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id)
  1162. {
  1163. speaker_map_t::iterator it = mSpeakers.find(speaker_id);
  1164. if (it == mSpeakers.end())
  1165. {
  1166. return NULL;
  1167. }
  1168. return it->second;
  1169. }
  1170. void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list,
  1171. bool include_text)
  1172. {
  1173. speaker_list->clear();
  1174. for (speaker_map_t::iterator it = mSpeakers.begin(), end = mSpeakers.end();
  1175. it != end; ++it)
  1176. {
  1177. LLPointer<LLSpeaker> speakerp = it->second;
  1178. // What about text only muted or inactive ?
  1179. if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY)
  1180. {
  1181. speaker_list->push_back(speakerp);
  1182. }
  1183. }
  1184. }
  1185. const LLUUID LLSpeakerMgr::getSessionID()
  1186. {
  1187. return mVoiceChannel->getSessionID();
  1188. }
  1189. void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, bool typing)
  1190. {
  1191. LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
  1192. if (speakerp.notNull())
  1193. {
  1194. speakerp->mTyping = typing;
  1195. }
  1196. }
  1197. // Speaker has chatted via either text or voice
  1198. void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)
  1199. {
  1200. LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
  1201. if (speakerp.notNull())
  1202. {
  1203. speakerp->setSpokenTime(mSpeechTimer.getElapsedTimeF32());
  1204. }
  1205. }
  1206. bool LLSpeakerMgr::isVoiceActive()
  1207. {
  1208. // mVoiceChannel = NULL means current voice channel, whatever it is
  1209. return LLVoiceClient::voiceEnabled() && mVoiceChannel &&
  1210. mVoiceChannel->isActive();
  1211. }
  1212. //
  1213. // LLIMSpeakerMgr
  1214. //
  1215. LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel)
  1216. : LLSpeakerMgr(channel)
  1217. {
  1218. }
  1219. void LLIMSpeakerMgr::updateSpeakerList()
  1220. {
  1221. // Do not do normal updates which are pulled from voice channel: rely on
  1222. // user list reported by sim.
  1223. // We need to do this to allow PSTN callers into group chats to show in the
  1224. // list.
  1225. LLSpeakerMgr::updateSpeakerList();
  1226. return;
  1227. }
  1228. void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers)
  1229. {
  1230. if (!speakers.isMap()) return;
  1231. if (speakers.has("agent_info") && speakers["agent_info"].isMap())
  1232. {
  1233. for (LLSD::map_const_iterator it = speakers["agent_info"].beginMap(),
  1234. end = speakers["agent_info"].endMap();
  1235. it != end; ++it)
  1236. {
  1237. const LLUUID agent_id(it->first);
  1238. LLPointer<LLSpeaker> speakerp = setSpeaker(agent_id);
  1239. if (it->second.isMap())
  1240. {
  1241. speakerp->mIsModerator = it->second["is_moderator"];
  1242. speakerp->mModeratorMutedText = it->second["mutes"]["text"];
  1243. }
  1244. }
  1245. }
  1246. else if (speakers.has("agents") && speakers["agents"].isArray())
  1247. {
  1248. // Older, more deprecated way. Needed for older server versions
  1249. for (LLSD::array_const_iterator it = speakers["agents"].beginArray(),
  1250. end = speakers["agents"].endArray();
  1251. it != end; ++it)
  1252. {
  1253. const LLUUID agent_id = it->asUUID();
  1254. setSpeaker(agent_id);
  1255. }
  1256. }
  1257. }
  1258. void LLIMSpeakerMgr::updateSpeakers(const LLSD& update)
  1259. {
  1260. if (!update.isMap()) return;
  1261. if (update.has("agent_updates") && update["agent_updates"].isMap())
  1262. {
  1263. LLPointer<LLSpeaker> speakerp;
  1264. for (LLSD::map_const_iterator it = update["agent_updates"].beginMap(),
  1265. end = update["agent_updates"].endMap();
  1266. it != end; ++it)
  1267. {
  1268. LLUUID agent_id(it->first);
  1269. speakerp = findSpeaker(agent_id);
  1270. LLSD agent_data = it->second;
  1271. if (agent_data.isMap() && agent_data.has("transition"))
  1272. {
  1273. const std::string& trans = agent_data["transition"].asString();
  1274. if (trans == "LEAVE")
  1275. {
  1276. if (speakerp.notNull())
  1277. {
  1278. speakerp->setStatus(LLSpeaker::STATUS_NOT_IN_CHANNEL);
  1279. speakerp->mDotColor = INACTIVE_COLOR;
  1280. speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
  1281. }
  1282. }
  1283. else if (trans == "ENTER")
  1284. {
  1285. // Add or update speaker
  1286. speakerp = setSpeaker(agent_id);
  1287. }
  1288. else
  1289. {
  1290. llwarns << "bad membership list update "
  1291. << ll_print_sd(agent_data["transition"]) << llendl;
  1292. }
  1293. }
  1294. if (speakerp.isNull()) continue;
  1295. // Should have a valid speaker from this point on
  1296. if (agent_data.isMap() && agent_data.has("info"))
  1297. {
  1298. const LLSD& agent_info = agent_data["info"];
  1299. if (agent_info.has("is_moderator"))
  1300. {
  1301. speakerp->mIsModerator = agent_info["is_moderator"];
  1302. }
  1303. if (agent_info.has("mutes"))
  1304. {
  1305. speakerp->mModeratorMutedText = agent_info["mutes"]["text"];
  1306. }
  1307. }
  1308. }
  1309. }
  1310. else if (update.has("updates") && update["updates"].isMap())
  1311. {
  1312. for (LLSD::map_const_iterator it = update["updates"].beginMap(),
  1313. end = update["updates"].endMap();
  1314. it != end; ++it)
  1315. {
  1316. const LLUUID agent_id(it->first);
  1317. LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
  1318. std::string agent_transition = it->second.asString();
  1319. if (agent_transition == "LEAVE" && speakerp.notNull())
  1320. {
  1321. speakerp->setStatus(LLSpeaker::STATUS_NOT_IN_CHANNEL);
  1322. speakerp->mDotColor = INACTIVE_COLOR;
  1323. speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
  1324. }
  1325. else if (agent_transition == "ENTER")
  1326. {
  1327. // Add or update speaker
  1328. speakerp = setSpeaker(agent_id);
  1329. }
  1330. else
  1331. {
  1332. llwarns << "bad membership list update " << agent_transition
  1333. << llendl;
  1334. }
  1335. }
  1336. }
  1337. }
  1338. //
  1339. // LLActiveSpeakerMgr
  1340. //
  1341. LLActiveSpeakerMgr::LLActiveSpeakerMgr()
  1342. : LLSpeakerMgr(NULL)
  1343. {
  1344. }
  1345. void LLActiveSpeakerMgr::updateSpeakerList()
  1346. {
  1347. // Point to whatever the current voice channel is
  1348. mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
  1349. // Always populate from active voice channel
  1350. if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel)
  1351. {
  1352. fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear");
  1353. mSpeakers.clear();
  1354. mSpeakersSorted.clear();
  1355. mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
  1356. }
  1357. LLSpeakerMgr::updateSpeakerList();
  1358. // Clean up text only speakers
  1359. for (speaker_map_t::iterator it = mSpeakers.begin(), end = mSpeakers.end();
  1360. it != end; ++it)
  1361. {
  1362. LLSpeaker* speakerp = it->second;
  1363. if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
  1364. {
  1365. // Automatically flag text only speakers for removal
  1366. speakerp->setStatus(LLSpeaker::STATUS_NOT_IN_CHANNEL);
  1367. }
  1368. }
  1369. }
  1370. //
  1371. // LLLocalSpeakerMgr
  1372. //
  1373. LLLocalSpeakerMgr::LLLocalSpeakerMgr()
  1374. : LLSpeakerMgr(LLVoiceChannelProximal::getInstance())
  1375. {
  1376. }
  1377. void LLLocalSpeakerMgr::updateSpeakerList()
  1378. {
  1379. // Pull speakers from voice channel
  1380. LLSpeakerMgr::updateSpeakerList();
  1381. LLViewerRegion* regionp = gAgent.getRegion();
  1382. if (gDisconnected || !regionp)
  1383. {
  1384. return;
  1385. }
  1386. // Pick up non-voice speakers in chat range
  1387. uuid_vec_t avatar_ids;
  1388. std::vector<LLVector3d> positions;
  1389. F32 radius = regionp->getChatRange();
  1390. gWorld.getAvatars(avatar_ids, &positions, NULL,
  1391. gAgent.getPositionGlobal(), radius);
  1392. for (U32 i = 0, count = avatar_ids.size(); i < count; ++i)
  1393. {
  1394. setSpeaker(avatar_ids[i]);
  1395. }
  1396. // Check if text only speakers have moved out of chat range
  1397. for (speaker_map_t::iterator it = mSpeakers.begin(), end = mSpeakers.end();
  1398. it != end; ++it)
  1399. {
  1400. LLUUID speaker_id = it->first;
  1401. LLSpeaker* speakerp = it->second;
  1402. if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
  1403. {
  1404. LLVOAvatar* avatarp = gObjectList.findAvatar(speaker_id);
  1405. if (!avatarp ||
  1406. dist_vec(avatarp->getPositionAgent(),
  1407. gAgent.getPositionAgent()) > radius)
  1408. {
  1409. speakerp->setStatus(LLSpeaker::STATUS_NOT_IN_CHANNEL);
  1410. speakerp->mDotColor = INACTIVE_COLOR;
  1411. speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
  1412. }
  1413. }
  1414. }
  1415. }