hbfloaterradar.cpp 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138
  1. /**
  2. * @file hbfloaterradar.cpp
  3. * @brief Radar floater class implementation
  4. *
  5. * @authors Original code from Dale Glass, amended by jcool410 for Emerald,
  6. * fully rewritten and heavily modified by Henri Beauchamp (the laggy spying
  7. * tool becomes a true, low lag radar), with avatar marking and announcements
  8. * additions, among others.
  9. *
  10. * $LicenseInfo:firstyear=2007&license=viewergpl$
  11. *
  12. * Copyright (c) 2008-2024, Henri Beauchamp.
  13. *
  14. * Second Life Viewer Source Code
  15. * The source code in this file ("Source Code") is provided by Linden Lab
  16. * to you under the terms of the GNU General Public License, version 2.0
  17. * ("GPL"), unless you have obtained a separate licensing agreement
  18. * ("Other License"), formally executed by you and Linden Lab. Terms of
  19. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  20. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  21. *
  22. * There are special exceptions to the terms and conditions of the GPL as
  23. * it is applied to this Source Code. View the full text of the exception
  24. * in the file doc/FLOSS-exception.txt in this software distribution, or
  25. * online at
  26. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  27. *
  28. * By copying, modifying or distributing this software, you acknowledge
  29. * that you have read and understood your obligations described above,
  30. * and agree to abide by those obligations.
  31. *
  32. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  33. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  34. * COMPLETENESS OR PERFORMANCE.
  35. * $/LicenseInfo$
  36. */
  37. #include "llviewerprecompiledheaders.h"
  38. #include <utility>
  39. #include "hbfloaterradar.h"
  40. #include "llbutton.h"
  41. #include "llcachename.h"
  42. #include "llcallbacklist.h"
  43. #include "llcheckboxctrl.h"
  44. #include "lldir.h"
  45. #include "llfasttimer.h"
  46. #include "llradiogroup.h"
  47. #include "llregionflags.h"
  48. #include "llscrolllistctrl.h"
  49. #include "llsdutil.h"
  50. #include "lluictrlfactory.h"
  51. #include "llmessage.h"
  52. #include "llagent.h"
  53. #include "llappviewer.h" // For gFrameCount
  54. #include "llavataractions.h"
  55. #include "llchat.h"
  56. #include "llfloateravatarinfo.h"
  57. #include "llfloaterchat.h"
  58. #include "llfloaterinspect.h"
  59. #include "llfloatermute.h"
  60. #include "llfloaterreporter.h"
  61. //MK
  62. #include "mkrlinterface.h"
  63. //mk
  64. #include "lltracker.h"
  65. #include "hbviewerautomation.h"
  66. #include "llviewercontrol.h"
  67. #include "llviewerobjectlist.h"
  68. #include "llviewerregion.h"
  69. #include "llviewerwindow.h"
  70. #include "llvoavatar.h"
  71. #include "llworld.h"
  72. #define COMMENT_PREFIX "\342\200\243 "
  73. // Static member variables
  74. LLFrameTimer HBFloaterRadar::sUpdateTimer;
  75. std::string HBFloaterRadar::sCardinals;
  76. std::string HBFloaterRadar::sTotalAvatarsStr;
  77. std::string HBFloaterRadar::sNoAvatarStr;
  78. std::string HBFloaterRadar::sLastKnownPosStr;
  79. std::string HBFloaterRadar::sHasEnteredStr;
  80. std::string HBFloaterRadar::sHasLeftStr;
  81. std::string HBFloaterRadar::sTheSimStr;
  82. std::string HBFloaterRadar::sDrawDistanceStr;
  83. std::string HBFloaterRadar::sShoutRangeStr;
  84. std::string HBFloaterRadar::sChatRangeStr;
  85. LLColor4 HBFloaterRadar::sMarkColor;
  86. LLColor4 HBFloaterRadar::sNameColor;
  87. LLColor4 HBFloaterRadar::sFriendNameColor;
  88. LLColor4 HBFloaterRadar::sMutedNameColor;
  89. LLColor4 HBFloaterRadar::sDerenderedNameColor;
  90. LLColor4 HBFloaterRadar::sFarDistanceColor;
  91. LLColor4 HBFloaterRadar::sShoutDistanceColor;
  92. LLColor4 HBFloaterRadar::sChatDistanceColor;
  93. U32 HBFloaterRadar::sUpdatesPerSecond;
  94. bool HBFloaterRadar::sRememberMarked;
  95. // Helper function
  96. static void announce(std::string msg)
  97. {
  98. static LLCachedControl<S32> chan(gSavedSettings, "RadarChatKeysChannel");
  99. LL_DEBUGS("Radar") << "Radar broadcasting avatar key: " << msg
  100. << " - on channel: " << (S32)chan << LL_ENDL;
  101. LLMessageSystem* msgsys = gMessageSystemp;
  102. msgsys->newMessage(_PREHASH_ScriptDialogReply);
  103. msgsys->nextBlock(_PREHASH_AgentData);
  104. msgsys->addUUID(_PREHASH_AgentID, gAgentID);
  105. msgsys->addUUID(_PREHASH_SessionID, gAgentSessionID);
  106. msgsys->nextBlock(_PREHASH_Data);
  107. msgsys->addUUID(_PREHASH_ObjectID, gAgentID);
  108. msgsys->addS32(_PREHASH_ChatChannel, (S32)chan);
  109. msgsys->addS32(_PREHASH_ButtonIndex, 1);
  110. msgsys->addString(_PREHASH_ButtonLabel, msg);
  111. gAgent.sendReliableMessage();
  112. }
  113. ///////////////////////////////////////////////////////////////////////////////
  114. // HBRadarListEntry class
  115. ///////////////////////////////////////////////////////////////////////////////
  116. HBRadarListEntry::HBRadarListEntry(LLVOAvatar* avatarp, const LLUUID& avid,
  117. const std::string& name,
  118. const std::string& listed_name,
  119. const LLVector3d& position, bool marked)
  120. : mID(avid),
  121. mLegacyName(name),
  122. mListedName(listed_name),
  123. mPosition(position),
  124. mMarked(marked),
  125. mCustomMark(false),
  126. mMarkChar("X"),
  127. mMarkColor(HBFloaterRadar::sMarkColor),
  128. mFocused(false),
  129. mMuted(LLMuteList::isMuted(avid)),
  130. mDerendered(gObjectList.mBlackListedObjects.count(avid)),
  131. mFriend(LLAvatarTracker::isAgentFriend(avid)),
  132. mFrame(gFrameCount),
  133. mInSimFrame(U32_MAX),
  134. mInDrawFrame(U32_MAX),
  135. mInChatFrame(U32_MAX),
  136. mInShoutFrame(U32_MAX)
  137. {
  138. if (avatarp)
  139. {
  140. mColor = avatarp->getRadarColor();
  141. }
  142. if (gAutomationp)
  143. {
  144. gAutomationp->onRadar(mID, mLegacyName, -1, mMarked);
  145. }
  146. }
  147. HBRadarListEntry::~HBRadarListEntry()
  148. {
  149. if (gAutomationp)
  150. {
  151. gAutomationp->onRadar(mID, mLegacyName, -2, mMarked);
  152. }
  153. }
  154. bool HBRadarListEntry::setMarkChar(const std::string& chr)
  155. {
  156. size_t len = chr.size();
  157. if (len == 0 || len > 3) // Accept UTF-8 characters
  158. {
  159. mMarkChar = "X";
  160. mCustomMark = false;
  161. return false;
  162. }
  163. mMarkChar = chr;
  164. mCustomMark = true;
  165. return true;
  166. }
  167. void HBRadarListEntry::setPosition(const LLVector3d& position, bool this_sim,
  168. bool drawn, bool chatrange, bool shoutrange)
  169. {
  170. if (drawn)
  171. {
  172. mDrawPosition = position;
  173. }
  174. else if (mInDrawFrame == U32_MAX)
  175. {
  176. mDrawPosition.setZero();
  177. }
  178. mPosition = position;
  179. mFrame = gFrameCount;
  180. if (this_sim)
  181. {
  182. if (mInSimFrame == U32_MAX)
  183. {
  184. reportAvatarStatus(ALERT_TYPE_SIM, true);
  185. }
  186. mInSimFrame = mFrame;
  187. }
  188. if (drawn)
  189. {
  190. if (mInDrawFrame == U32_MAX)
  191. {
  192. reportAvatarStatus(ALERT_TYPE_DRAW, true);
  193. }
  194. mInDrawFrame = mFrame;
  195. }
  196. if (chatrange)
  197. {
  198. if (mInChatFrame == U32_MAX)
  199. {
  200. if (reportAvatarStatus(ALERT_TYPE_CHATRANGE, true))
  201. {
  202. // Note: if the avatar entered the chat range, then it also
  203. // entered the shout range, so do not announce the latter if
  204. // the former has already been announced.
  205. if (shoutrange)
  206. {
  207. mInShoutFrame = mFrame;
  208. }
  209. }
  210. }
  211. mInChatFrame = mFrame;
  212. }
  213. if (shoutrange)
  214. {
  215. if (mInShoutFrame == U32_MAX)
  216. {
  217. reportAvatarStatus(ALERT_TYPE_SHOUTRANGE, true);
  218. }
  219. mInShoutFrame = mFrame;
  220. }
  221. mUpdateTimer.start();
  222. }
  223. bool HBRadarListEntry::getAlive()
  224. {
  225. U32 current = gFrameCount;
  226. if (mInSimFrame != U32_MAX && current - mInSimFrame >= 2)
  227. {
  228. mInSimFrame = U32_MAX;
  229. reportAvatarStatus(ALERT_TYPE_SIM, false);
  230. }
  231. if (mInDrawFrame != U32_MAX && current - mInDrawFrame >= 2)
  232. {
  233. mInDrawFrame = U32_MAX;
  234. reportAvatarStatus(ALERT_TYPE_DRAW, false);
  235. }
  236. if (mInShoutFrame != U32_MAX && current - mInShoutFrame >= 2)
  237. {
  238. mInShoutFrame = U32_MAX;
  239. if (reportAvatarStatus(ALERT_TYPE_SHOUTRANGE, false))
  240. {
  241. // Note: if the avatar left the shout range, then it also left the
  242. // chat range, so do not announce the latter if the former has
  243. // already been announced.
  244. mInChatFrame = U32_MAX;
  245. }
  246. }
  247. if (mInChatFrame != U32_MAX && current - mInChatFrame >= 2)
  248. {
  249. mInChatFrame = U32_MAX;
  250. reportAvatarStatus(ALERT_TYPE_CHATRANGE, false);
  251. }
  252. return current - mFrame <= 2;
  253. }
  254. bool HBRadarListEntry::reportAvatarStatus(ERadarAlertType type, bool entering)
  255. {
  256. static LLCachedControl<bool> do_alert(gSavedSettings, "RadarChatAlerts");
  257. static LLCachedControl<bool> sim_range(gSavedSettings, "RadarAlertSim");
  258. static LLCachedControl<bool> draw_range(gSavedSettings, "RadarAlertDraw");
  259. static LLCachedControl<bool> shout_range(gSavedSettings,
  260. "RadarAlertShoutRange");
  261. static LLCachedControl<bool> chat_range(gSavedSettings,
  262. "RadarAlertChatRange");
  263. static LLCachedControl<bool> send_key(gSavedSettings, "RadarChatKeys");
  264. bool announced = false;
  265. if (do_alert)
  266. {
  267. LLChat chat;
  268. // *TODO: translate
  269. std::string message = mListedName + " ";
  270. if (entering)
  271. {
  272. message += HBFloaterRadar::sHasEnteredStr + " ";
  273. }
  274. else
  275. {
  276. message += HBFloaterRadar::sHasLeftStr + " ";
  277. }
  278. switch (type)
  279. {
  280. case ALERT_TYPE_SIM:
  281. if (sim_range)
  282. {
  283. chat.mText = message + HBFloaterRadar::sTheSimStr;
  284. }
  285. break;
  286. case ALERT_TYPE_DRAW:
  287. if (draw_range)
  288. {
  289. chat.mText = message + HBFloaterRadar::sDrawDistanceStr;
  290. }
  291. break;
  292. case ALERT_TYPE_SHOUTRANGE:
  293. if (shout_range)
  294. {
  295. chat.mText = message + HBFloaterRadar::sShoutRangeStr;
  296. }
  297. break;
  298. case ALERT_TYPE_CHATRANGE:
  299. if (chat_range)
  300. {
  301. chat.mText = message + HBFloaterRadar::sChatRangeStr;
  302. }
  303. }
  304. if (!chat.mText.empty())
  305. {
  306. chat.mSourceType = CHAT_SOURCE_SYSTEM;
  307. LLFloaterChat::addChat(chat);
  308. announced = true;
  309. }
  310. }
  311. if (send_key && entering && type == ALERT_TYPE_SIM)
  312. {
  313. announce(mID.asString());
  314. }
  315. if (entering && gAutomationp)
  316. {
  317. gAutomationp->onRadar(mID, mLegacyName, type, mMarked);
  318. }
  319. return announced;
  320. }
  321. ///////////////////////////////////////////////////////////////////////////////
  322. // HBFloaterRadar class proper
  323. ///////////////////////////////////////////////////////////////////////////////
  324. HBFloaterRadar::HBFloaterRadar(const LLSD&)
  325. : mTracking(false)
  326. {
  327. LLUICtrlFactory::getInstance()->buildFloater(this, "floater_radar.xml");
  328. LLMuteList::addObserver(this);
  329. gAvatarTracker.addObserver(this);
  330. }
  331. //virtual
  332. HBFloaterRadar::~HBFloaterRadar()
  333. {
  334. LLMuteList::removeObserver(this);
  335. gAvatarTracker.removeObserver(this);
  336. gIdleCallbacks.deleteFunction(HBFloaterRadar::callbackIdle);
  337. }
  338. //virtual
  339. bool HBFloaterRadar::postBuild()
  340. {
  341. mTabContainer = getChild<LLTabContainer>("tab_container");
  342. LLPanel* tab = mTabContainer->getChild<LLPanel>("actions_tab");
  343. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  344. mTabContainer->setTabUserData(tab, this);
  345. tab = mTabContainer->getChild<LLPanel>("alerts_tab");
  346. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  347. mTabContainer->setTabUserData(tab, this);
  348. tab = mTabContainer->getChild<LLPanel>("moderation_tab");
  349. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  350. mTabContainer->setTabUserData(tab, this);
  351. tab = mTabContainer->getChild<LLPanel>("options_tab");
  352. mTabContainer->setTabChangeCallback(tab, onTabChanged);
  353. mTabContainer->setTabUserData(tab, this);
  354. // Avatar tab buttons:
  355. mProfileButton = getChild<LLButton>("profile_btn");
  356. mProfileButton->setClickedCallback(onClickProfile, this);
  357. mTrackButton = getChild<LLButton>("track_btn");
  358. mTrackButton->setClickedCallback(onClickTrack, this);
  359. mIMButton = getChild<LLButton>("im_btn");
  360. mIMButton->setClickedCallback(onClickIM, this);
  361. mTPOfferButton = getChild<LLButton>("offer_btn");
  362. mTPOfferButton->setClickedCallback(onClickTeleportOffer, this);
  363. mRequestTPButton = getChild<LLButton>("request_tp_btn");
  364. mRequestTPButton->setClickedCallback(onClickTeleportRequest, this);
  365. mTeleportToButton = getChild<LLButton>("teleport_btn");
  366. mTeleportToButton->setClickedCallback(onClickTeleportTo, this);
  367. mMarkButton = getChild<LLButton>("mark_btn");
  368. mMarkButton->setClickedCallback(onClickMark, this);
  369. mPrevMarkedButton = getChild<LLButton>("prev_marked_btn");
  370. mPrevMarkedButton->setClickedCallback(onClickPrevMarked, this);
  371. mNextMarkedButton = getChild<LLButton>("next_marked_btn");
  372. mNextMarkedButton->setClickedCallback(onClickNextMarked, this);
  373. mFocusButton = getChild<LLButton>("focus_btn");
  374. mFocusButton->setClickedCallback(onClickFocus, this);
  375. mPrevInListButton = getChild<LLButton>("prev_in_list_btn");
  376. mPrevInListButton->setClickedCallback(onClickPrevInList, this);
  377. mNextInListButton = getChild<LLButton>("next_in_list_btn");
  378. mNextInListButton->setClickedCallback(onClickNextInList, this);
  379. // Alerts tab check boxes:
  380. mRadarAlertsCheck = getChild<LLCheckBoxCtrl>("radar_alerts");
  381. mRadarAlertsCheck->setCommitCallback(onCheckRadarAlerts);
  382. mRadarAlertsCheck->setCallbackUserData(this);
  383. mSimAlertsCheck = getChild<LLCheckBoxCtrl>("alerts_sim");
  384. mDrawAlertsCheck = getChild<LLCheckBoxCtrl>("alerts_draw");
  385. mShoutAlertsCheck = getChild<LLCheckBoxCtrl>("alerts_shout");
  386. mChatAlertsCheck = getChild<LLCheckBoxCtrl>("alerts_chat");
  387. // Sync the check boxes enabled state already
  388. onCheckRadarAlerts(mRadarAlertsCheck, this);
  389. // Moderation tab search editor:
  390. LLSearchEditor* editp = getChild<LLSearchEditor>("filter_search");
  391. editp->setSearchCallback(onSearchEdit, this);
  392. // Moderation tab buttons:
  393. mMuteButton = getChild<LLButton>("mute_btn");
  394. mMuteButton->setClickedCallback(onClickMute, this);
  395. mFreezeButton = getChild<LLButton>("freeze_btn");
  396. mFreezeButton->setClickedCallback(onClickFreeze, this);
  397. mARButton = getChild<LLButton>("ar_btn");
  398. mARButton->setClickedCallback(onClickAR, this);
  399. mEjectButton = getChild<LLButton>("eject_btn");
  400. mEjectButton->setClickedCallback(onClickEject, this);
  401. mEstateEjectButton = getChild<LLButton>("estate_eject_btn");
  402. mEstateEjectButton->setClickedCallback(onClickEjectFromEstate, this);
  403. mGetKeyButton = getChild<LLButton>("get_key_btn");
  404. mGetKeyButton->setClickedCallback(onClickGetKey, this);
  405. mDerenderButton = getChild<LLButton>("derender_btn");
  406. mDerenderButton->setClickedCallback(onClickDerender, this);
  407. mRerenderButton = getChild<LLButton>("rerender_btn");
  408. mRerenderButton->setClickedCallback(onClickDerender, this);
  409. // Options tab button:
  410. childSetAction("send_keys_btn", onClickSendKeys, this);
  411. mClearSavedMarkedButton = getChild<LLButton>("clear_marked_btn");
  412. mClearSavedMarkedButton->setClickedCallback(onClickClearSavedMarked, this);
  413. mUseLegacyNamesCheck = getChild<LLCheckBoxCtrl>("radar_use_legacy_names");
  414. mUseLegacyNamesCheck->setCommitCallback(onCheckUseLegacyNames);
  415. mUseLegacyNamesCheck->setCallbackUserData(this);
  416. // Scroll list
  417. mAvatarList = getChild<LLScrollListCtrl>("avatar_list");
  418. mAvatarList->sortByColumn("distance", true);
  419. mAvatarList->setCommitOnSelectionChange(true);
  420. mAvatarList->setCommitCallback(onSelectName);
  421. mAvatarList->setDoubleClickCallback(onDoubleClick);
  422. mAvatarList->setCallbackUserData(this);
  423. // Make sure the cached values will be properly updated on setting changes.
  424. connectRefreshCachedSettingsSafe("RadarMarkColor");
  425. connectRefreshCachedSettingsSafe("RadarNameColor");
  426. connectRefreshCachedSettingsSafe("RadarFriendNameColor");
  427. connectRefreshCachedSettingsSafe("RadarMutedNameColor");
  428. connectRefreshCachedSettingsSafe("RadarDerenderedNameColor");
  429. connectRefreshCachedSettingsSafe("RadarFarDistanceColor");
  430. connectRefreshCachedSettingsSafe("RadarShoutDistanceColor");
  431. connectRefreshCachedSettingsSafe("RadarChatDistanceColor");
  432. connectRefreshCachedSettingsSafe("RadarUpdatesPerSecond");
  433. connectRefreshCachedSettingsSafe("RadarRememberMarked");
  434. // Update cached setting values now.
  435. refreshCachedSettings();
  436. // Cache these UI strings once, i.e. for the session duration: this has
  437. // to be done here (and not in refreshCachedSettings() which is a static
  438. // method), so that we can extract the strings from 'this' floater XUI.
  439. if (sCardinals.empty())
  440. {
  441. // Cardinal points: a 4 characters string for North, South, West and
  442. // East, in this order. E.g. "NSWE". Note: only ASCII (one byte)
  443. // characters are accepted !
  444. sCardinals = getString("cardinals");
  445. if (sCardinals.size() != 4)
  446. {
  447. llwarns << "Invalid cardinals string in floater XUI definition."
  448. << llendl;
  449. sCardinals = "NSWE";
  450. }
  451. // Strings used for the number of avatars in the list
  452. sTotalAvatarsStr = COMMENT_PREFIX + getString("total_avatars");
  453. sNoAvatarStr = COMMENT_PREFIX + getString("no_avatar");
  454. // Used for the tracker arrow text
  455. sLastKnownPosStr = "\n" + getString("last_known_pos");
  456. // Strings used for announcements
  457. sHasEnteredStr = getString("has_entered");
  458. sHasLeftStr = getString("has_left");
  459. sTheSimStr = getString("the_sim");
  460. sDrawDistanceStr = getString("draw_distance");
  461. sShoutRangeStr = getString("shout_range");
  462. sChatRangeStr = getString("chat_range");
  463. }
  464. // Load the marked avatars list
  465. loadMarkedFromFile();
  466. updateAvatarList();
  467. gIdleCallbacks.addFunction(HBFloaterRadar::callbackIdle);
  468. mTabContainer->selectTab(gSavedSettings.getS32("LastRadarTab"));
  469. return true;
  470. }
  471. //static
  472. void HBFloaterRadar::refreshCachedSettings()
  473. {
  474. // Note: do not bother using LLCachedControls here: this method is rarely
  475. // ever called.
  476. sMarkColor = gColors.getColor("RadarMarkColor");
  477. sNameColor = gColors.getColor("RadarNameColor");
  478. sFriendNameColor = gColors.getColor("RadarFriendNameColor");
  479. sMutedNameColor = gColors.getColor("RadarMutedNameColor");
  480. sDerenderedNameColor = gColors.getColor("RadarDerenderedNameColor");
  481. sFarDistanceColor = gColors.getColor("RadarFarDistanceColor");
  482. sShoutDistanceColor = gColors.getColor("RadarShoutDistanceColor");
  483. sChatDistanceColor = gColors.getColor("RadarChatDistanceColor");
  484. sUpdatesPerSecond = gSavedSettings.getU32("RadarUpdatesPerSecond");
  485. sRememberMarked = gSavedSettings.getBool("RadarRememberMarked");
  486. }
  487. //static
  488. void HBFloaterRadar::connectRefreshCachedSettingsSafe(const char* name)
  489. {
  490. LLControlVariable* controlp = gColors.getControl(name);
  491. if (!controlp)
  492. {
  493. controlp = gSavedSettings.getControl(name);
  494. }
  495. if (!controlp)
  496. {
  497. llwarns << "Setting name not found: " << name << llendl;
  498. return;
  499. }
  500. controlp->getSignal()->connect(boost::bind(&HBFloaterRadar::refreshCachedSettings));
  501. }
  502. //virtual
  503. void HBFloaterRadar::onOpen()
  504. {
  505. bool visible = true;
  506. //MK
  507. if (gRLenabled &&
  508. (gRLInterface.mContainsShownames || gRLInterface.mContainsShowNearby ||
  509. gRLInterface.mContainsShownametags))
  510. {
  511. // The floater will be automatically destroyed on the next idle
  512. // callback. Just make it invisible till then (better than destroying
  513. // the floater during an onOpen() event...).
  514. visible = false;
  515. }
  516. //mk
  517. gSavedSettings.setBool("ShowRadar", visible);
  518. setVisible(visible);
  519. }
  520. //virtual
  521. void HBFloaterRadar::onClose(bool app_quitting)
  522. {
  523. setVisible(false);
  524. if (!app_quitting)
  525. {
  526. gSavedSettings.setBool("ShowRadar", false);
  527. }
  528. if (!gSavedSettings.getBool("RadarKeepOpen") || app_quitting)
  529. {
  530. destroy();
  531. }
  532. }
  533. // Mute list observer
  534. //virtual
  535. void HBFloaterRadar::onChange()
  536. {
  537. for (avatar_list_t::iterator iter = mAvatars.begin(), end = mAvatars.end();
  538. iter != end; ++iter)
  539. {
  540. iter->second.mMuted = LLMuteList::isMuted(iter->first);
  541. }
  542. }
  543. // Friends list observer
  544. //virtual
  545. void HBFloaterRadar::changed(U32 mask)
  546. {
  547. if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE))
  548. {
  549. for (avatar_list_t::iterator iter = mAvatars.begin(),
  550. end = mAvatars.end();
  551. iter != end; ++iter)
  552. {
  553. iter->second.mFriend = LLAvatarTracker::isAgentFriend(iter->first);
  554. }
  555. }
  556. }
  557. void HBFloaterRadar::updateAvatarList()
  558. {
  559. LLViewerRegion* aregionp = gAgent.getRegion();
  560. if (!aregionp) return;
  561. U32 use_listed_names = LLAvatarNameCache::useDisplayNames();
  562. static LLCachedControl<bool> legacy(gSavedSettings, "RadarUseLegacyNames");
  563. bool use_legacy_names = use_listed_names == 0 || legacy;
  564. mUseLegacyNamesCheck->setEnabled(use_listed_names != 0);
  565. mUseLegacyNamesCheck->set(use_legacy_names);
  566. LLVector3d mypos = gAgent.getPositionGlobal();
  567. F32 chat_range = (F32)aregionp->getChatRange();
  568. F32 shout_range = (F32)aregionp->getShoutRange();
  569. uuid_vec_t avatar_ids;
  570. std::vector<LLVector3d> positions;
  571. gWorld.getAvatars(avatar_ids, &positions, NULL, mypos, 65536.f);
  572. LLVector3d position;
  573. std::string name, listed_name;
  574. for (S32 i = 0, count = avatar_ids.size(); i < count; ++i)
  575. {
  576. const LLUUID& avid = avatar_ids[i];
  577. if (avid.isNull())
  578. {
  579. continue;
  580. }
  581. LLVOAvatar* avatarp = gObjectList.findAvatar(avid);
  582. if (avatarp)
  583. {
  584. // Get avatar data
  585. position =
  586. gAgent.getPosGlobalFromAgent(avatarp->getCharacterPosition());
  587. name = listed_name = avatarp->getFullname();
  588. if (name.empty() &&
  589. (!gCacheNamep || !gCacheNamep->getFullName(avid, name)))
  590. {
  591. continue; // prevent (Loading...)
  592. }
  593. if (!use_legacy_names)
  594. {
  595. LLAvatarName avatar_name;
  596. if (LLAvatarNameCache::get(avid, &avatar_name))
  597. {
  598. if (use_listed_names == 2)
  599. {
  600. listed_name = avatar_name.mDisplayName;
  601. }
  602. else
  603. {
  604. listed_name = avatar_name.getNames();
  605. }
  606. }
  607. }
  608. avatar_list_t::iterator it = mAvatars.find(avid);
  609. if (it == mAvatars.end())
  610. {
  611. // Avatar not there yet, add it
  612. bool marked = sRememberMarked && mMarkedAvatars.count(avid);
  613. // C++11 is not the most limpid language... This whole
  614. // gymnastic with piecewise_construct and forward_as_tuple is
  615. // here to ensure HBRadarListEntry is constructed emplace, and
  616. // not just the UUID key of the map.
  617. mAvatars.emplace(std::piecewise_construct,
  618. std::forward_as_tuple(avid),
  619. std::forward_as_tuple(avatarp, avid ,name,
  620. listed_name, position,
  621. marked));
  622. }
  623. else
  624. {
  625. HBRadarListEntry& entry = it->second;
  626. // Avatar already in list, update position
  627. F32 dist = (F32)(position - mypos).length();
  628. entry.setPosition(position, avatarp->getRegion() == aregionp,
  629. true, dist < chat_range, dist < shout_range);
  630. // Update avatar display name.
  631. entry.setListedName(listed_name);
  632. }
  633. }
  634. else
  635. {
  636. if (i >= (S32)positions.size())
  637. {
  638. continue;
  639. }
  640. position = positions[i];
  641. if (!gCacheNamep || !gCacheNamep->getFullName(avid, name))
  642. {
  643. continue; // Prevents (Loading...)
  644. }
  645. listed_name = name;
  646. if (!use_legacy_names)
  647. {
  648. LLAvatarName avatar_name;
  649. if (LLAvatarNameCache::get(avid, &avatar_name))
  650. {
  651. if (use_listed_names == 2)
  652. {
  653. listed_name = avatar_name.mDisplayName;
  654. }
  655. else
  656. {
  657. listed_name = avatar_name.getNames();
  658. }
  659. }
  660. }
  661. avatar_list_t::iterator it = mAvatars.find(avid);
  662. if (it == mAvatars.end())
  663. {
  664. // Avatar not there yet, add it
  665. bool marked = sRememberMarked && mMarkedAvatars.count(avid);
  666. // C++11 is not the most limpid language... This whole
  667. // gymnastic with piecewise_construct and forward_as_tuple is
  668. // here to ensure HBRadarListEntry is constructed emplace, and
  669. // not just the UUID key of the map.
  670. mAvatars.emplace(std::piecewise_construct,
  671. std::forward_as_tuple(avid),
  672. std::forward_as_tuple(avatarp, avid ,name,
  673. listed_name, position,
  674. marked));
  675. }
  676. else
  677. {
  678. HBRadarListEntry& entry = it->second;
  679. // Avatar already in list, update position
  680. F32 dist = (F32)(position - mypos).length();
  681. entry.setPosition(position,
  682. aregionp->pointInRegionGlobal(position),
  683. false, dist < chat_range,
  684. dist < shout_range);
  685. // Update avatar display name.
  686. entry.setListedName(listed_name);
  687. }
  688. }
  689. }
  690. expireAvatarList();
  691. refreshAvatarList();
  692. refreshTracker();
  693. }
  694. void HBFloaterRadar::expireAvatarList()
  695. {
  696. for (avatar_list_t::iterator iter = mAvatars.begin(), end = mAvatars.end();
  697. iter != end; )
  698. {
  699. avatar_list_t::iterator curiter = iter++;
  700. HBRadarListEntry& entry = curiter->second;
  701. if (!entry.getAlive() && entry.isDead())
  702. {
  703. LL_DEBUGS("Radar") << "Radar: expiring avatar "
  704. << entry.getListedName() << LL_ENDL;
  705. if (curiter->first == mTrackedAvatar)
  706. {
  707. stopTracker();
  708. }
  709. mAvatars.erase(curiter);
  710. }
  711. }
  712. }
  713. // Redraws the avatar list
  714. void HBFloaterRadar::refreshAvatarList()
  715. {
  716. // Do not update the list when the floater is hidden
  717. if (!getVisible() || isMinimized()) return;
  718. // We rebuild the list fully each time it is refreshed. The assumption is
  719. // that it is faster than to refresh each entry and sort again the list.
  720. uuid_vec_t selected = mAvatarList->getSelectedIDs();
  721. S32 scrollpos = mAvatarList->getScrollPos();
  722. mAvatarList->deleteAllItems();
  723. LLViewerRegion* aregionp = gAgent.getRegion();
  724. if (!aregionp) return;
  725. F32 chat_range = (F32)aregionp->getChatRange();
  726. F32 shout_range = (F32)aregionp->getShoutRange();
  727. S32 sim_width = aregionp->getWidth();
  728. bool marked_avatars = false;
  729. bool filter = !mFilterString.empty();
  730. LLVector3d mypos = gAgent.getPositionGlobal();
  731. LLVector3d posagent, position, delta;
  732. posagent.set(gAgent.getPositionAgent());
  733. LLVector3d simpos = mypos - posagent;
  734. char temp[32];
  735. S32 in_sim = 0;
  736. for (avatar_list_t::iterator iter = mAvatars.begin(), end = mAvatars.end();
  737. iter != end; ++iter)
  738. {
  739. HBRadarListEntry& entry = iter->second;
  740. // Skip if avatar has not been around
  741. if (entry.isDead())
  742. {
  743. continue;
  744. }
  745. const std::string& listed_name = entry.getListedName();
  746. if (filter)
  747. {
  748. std::string lc_name = listed_name;
  749. LLStringUtil::toLower(lc_name);
  750. if (lc_name.find(mFilterString) == std::string::npos)
  751. {
  752. // Avatar name does not match the filter, skip this entry.
  753. continue;
  754. }
  755. }
  756. position = entry.getPosition();
  757. delta = position - mypos;
  758. F32 distance = (F32)delta.length();
  759. static LLCachedControl<U32> unknwown_av_alt(gSavedSettings,
  760. "UnknownAvatarAltitude");
  761. bool unknown_altitude = false;
  762. if (position.mdV[VZ] == (F32)unknwown_av_alt)
  763. {
  764. unknown_altitude = true;
  765. distance = 9000.f;
  766. }
  767. delta.mdV[2] = 0.f;
  768. F32 side_distance = (F32)delta.length();
  769. // *HACK: Workaround for an apparent bug: sometimes avatar entries get
  770. // stuck, and are registered by the client as perpetually moving in the
  771. // same direction. This makes sure they get removed from the visible
  772. // list eventually
  773. if (side_distance > 2048.f)
  774. {
  775. continue;
  776. }
  777. const LLUUID& avid = iter->first;
  778. LLSD element;
  779. element["id"] = avid;
  780. LLSD& mark_column = element["columns"][LIST_MARK];
  781. mark_column["column"] = "marked";
  782. mark_column["type"] = "text";
  783. if (entry.isMarked() || entry.isCustomMark())
  784. {
  785. mark_column["value"] = entry.getMarkChar();
  786. mark_column["color"] = entry.getMarkColor().getValue();
  787. mark_column["font-style"] = "BOLD";
  788. if (entry.isMarked())
  789. {
  790. marked_avatars = true;
  791. }
  792. }
  793. else
  794. {
  795. mark_column["value"] = "";
  796. }
  797. LLSD& name_column = element["columns"][LIST_AVATAR_NAME];
  798. name_column["column"] = "avatar_name";
  799. name_column["type"] = "text";
  800. name_column["value"] = listed_name.c_str();
  801. if (entry.getEntryAgeSeconds() > 1.f)
  802. {
  803. name_column["font-style"] = "ITALIC";
  804. }
  805. else if (entry.isFocused())
  806. {
  807. name_column["font-style"] = "BOLD";
  808. }
  809. if (entry.isDerendered())
  810. {
  811. name_column["color"] = sDerenderedNameColor.getValue();
  812. }
  813. else if (entry.isMuted())
  814. {
  815. name_column["color"] = sMutedNameColor.getValue();
  816. }
  817. else if (entry.isFriend())
  818. {
  819. name_column["color"] = sFriendNameColor.getValue();
  820. }
  821. else
  822. {
  823. name_column["color"] = entry.getColor().getValue();
  824. }
  825. LLColor4* color = &sNameColor;
  826. LLSD& dist_column = element["columns"][LIST_DISTANCE];
  827. dist_column["column"] = "distance";
  828. dist_column["type"] = "text";
  829. if (unknown_altitude)
  830. {
  831. strcpy(temp, "?");
  832. if (entry.isDrawn())
  833. {
  834. color = &sFarDistanceColor;
  835. }
  836. }
  837. else if (distance < shout_range)
  838. {
  839. snprintf(temp, sizeof(temp), "%.1f", distance);
  840. if (distance > chat_range)
  841. {
  842. color = &sShoutDistanceColor;
  843. }
  844. else
  845. {
  846. color = &sChatDistanceColor;
  847. }
  848. }
  849. else
  850. {
  851. if (entry.isDrawn())
  852. {
  853. color = &sFarDistanceColor;
  854. }
  855. snprintf(temp, sizeof(temp), "%d", (S32)distance);
  856. }
  857. dist_column["value"] = temp;
  858. dist_column["color"] = color->getValue();
  859. position = position - simpos;
  860. S32 x = (S32)position.mdV[VX];
  861. S32 y = (S32)position.mdV[VY];
  862. if (x >= 0 && x <= sim_width && y >= 0 && y <= sim_width)
  863. {
  864. snprintf(temp, sizeof(temp), "%d, %d", x, y);
  865. ++in_sim;
  866. }
  867. else
  868. {
  869. temp[0] = '\0';
  870. if (y < 0)
  871. {
  872. strncat(temp, &sCardinals[1], 1); // South
  873. }
  874. else if (y > sim_width)
  875. {
  876. strncat(temp, &sCardinals[0], 1); // North
  877. }
  878. if (x < 0)
  879. {
  880. strncat(temp, &sCardinals[2], 1); // West
  881. }
  882. else if (x > sim_width)
  883. {
  884. strncat(temp, &sCardinals[3], 1); // East
  885. }
  886. }
  887. LLSD& pos_column = element["columns"][LIST_POSITION];
  888. pos_column["column"] = "position";
  889. pos_column["type"] = "text";
  890. pos_column["value"] = temp;
  891. LLSD& alt_column = element["columns"][LIST_ALTITUDE];
  892. alt_column["column"] = "altitude";
  893. alt_column["type"] = "text";
  894. if (unknown_altitude)
  895. {
  896. strcpy(temp, "?");
  897. }
  898. else
  899. {
  900. snprintf(temp, sizeof(temp), "%d", (S32)position.mdV[VZ]);
  901. }
  902. alt_column["value"] = temp;
  903. // Add to list
  904. LLScrollListItem* itemp = mAvatarList->addElement(element, ADD_BOTTOM);
  905. if (itemp)
  906. {
  907. itemp->setToolTip(entry.getToolTip());
  908. }
  909. }
  910. // Sort
  911. mAvatarList->sortItems();
  912. // Add the number of avatars as a comment at the bottom of the list
  913. S32 count = mAvatarList->getItemCount();
  914. std::string comment;
  915. if (count > 0)
  916. {
  917. comment = llformat(sTotalAvatarsStr.c_str(), count, in_sim);
  918. }
  919. else
  920. {
  921. comment = sNoAvatarStr;
  922. }
  923. mAvatarList->addCommentText(comment);
  924. // Finish
  925. mAvatarList->selectMultiple(selected);
  926. mAvatarList->setScrollPos(scrollpos);
  927. // Refresh the buttons
  928. mPrevMarkedButton->setEnabled(marked_avatars);
  929. mNextMarkedButton->setEnabled(marked_avatars);
  930. onSelectName(NULL, this); // NULL is used to flag this false commit event.
  931. }
  932. bool HBFloaterRadar::loadMarkedFromFile()
  933. {
  934. mMarkedAvatars.clear();
  935. if (!sRememberMarked)
  936. {
  937. return true;
  938. }
  939. std::string file = gDirUtil.getFullPath(LL_PATH_PER_ACCOUNT,
  940. "marked_avatars.lst");
  941. if (file.empty())
  942. {
  943. llwarns << "Marked avatars filename is empty !" << llendl;
  944. return false;
  945. }
  946. LLFILE* fp = LLFile::open(file, "rb");
  947. if (!fp)
  948. {
  949. llwarns << "Could not open marked avatars file " << file << llendl;
  950. return false;
  951. }
  952. char buffer[64], id_buffer[64];
  953. id_buffer[63] = '\0';
  954. while (!feof(fp) && fgets(buffer, 64, fp))
  955. {
  956. id_buffer[0] = '\0';
  957. if (sscanf(buffer, "%37s\n", id_buffer) == 1)
  958. {
  959. LLUUID id = LLUUID(id_buffer);
  960. if (id.notNull())
  961. {
  962. LL_DEBUGS("Radar") << "Adding UUID: " << id << LL_ENDL;
  963. mMarkedAvatars.emplace(id);
  964. }
  965. }
  966. else
  967. {
  968. llwarns_sparse << "Malformed marked avatars file !" << llendl;
  969. }
  970. }
  971. LLFile::close(fp);
  972. return true;
  973. }
  974. bool HBFloaterRadar::saveMarkedToFile(bool force)
  975. {
  976. if (!force && !sRememberMarked)
  977. {
  978. return true;
  979. }
  980. std::string file = gDirUtil.getFullPath(LL_PATH_PER_ACCOUNT,
  981. "marked_avatars.lst");
  982. if (file.empty())
  983. {
  984. llwarns << "Marked avatars filename is empty !" << llendl;
  985. return false;
  986. }
  987. LLFILE* fp = LLFile::open(file, "wb");
  988. if (!fp)
  989. {
  990. llwarns << "Could not open marked avatars file " << file << llendl;
  991. return false;
  992. }
  993. if (mMarkedAvatars.empty())
  994. {
  995. // Write a null UUID to ensure the old file is overwritten
  996. fprintf(fp, "%s\n", LLUUID::null.asString().c_str());
  997. }
  998. else
  999. {
  1000. for (uuid_list_t::iterator it = mMarkedAvatars.begin(),
  1001. end = mMarkedAvatars.end();
  1002. it != end; ++it)
  1003. {
  1004. const LLUUID& id = *it;
  1005. LL_DEBUGS("Radar") << "Saving UUID: " << id << LL_ENDL;
  1006. fprintf(fp, "%s\n", id.asString().c_str());
  1007. }
  1008. }
  1009. LLFile::close(fp);
  1010. return true;
  1011. }
  1012. bool HBFloaterRadar::startTracker(const LLUUID& avid)
  1013. {
  1014. avatar_list_t::iterator it = mAvatars.find(avid);
  1015. if (it == mAvatars.end())
  1016. {
  1017. return false;
  1018. }
  1019. mTracking = true;
  1020. mTrackedAvatar = avid;
  1021. HBRadarListEntry& entry = it->second;
  1022. std::string name = entry.getListedName();
  1023. if (!sUpdatesPerSecond)
  1024. {
  1025. name += sLastKnownPosStr;
  1026. }
  1027. // Note: gTracker.trackAvatar() only works for friends allowing you to see
  1028. // them on map, so we must use out own tracking code, with a position
  1029. // tracker beacon instead.
  1030. gTracker.trackLocation(entry.getPosition(), name, "");
  1031. if (gAutomationp)
  1032. {
  1033. gAutomationp->onRadarTrack(avid, entry.getLegacyName(), true);
  1034. }
  1035. return true;
  1036. }
  1037. void HBFloaterRadar::stopTracker()
  1038. {
  1039. if (mTracking && gAutomationp)
  1040. {
  1041. std::string name;
  1042. avatar_list_t::iterator it = mAvatars.find(mTrackedAvatar);
  1043. if (it != mAvatars.end())
  1044. {
  1045. HBRadarListEntry& entry = it->second;
  1046. name = entry.getLegacyName();
  1047. }
  1048. gAutomationp->onRadarTrack(mTrackedAvatar, name, false);
  1049. }
  1050. gTracker.stopTracking();
  1051. mTracking = false;
  1052. }
  1053. void HBFloaterRadar::refreshTracker()
  1054. {
  1055. if (!mTracking) return;
  1056. if (gTracker.isTracking())
  1057. {
  1058. avatar_list_t::iterator it = mAvatars.find(mTrackedAvatar);
  1059. if (it == mAvatars.end())
  1060. {
  1061. stopTracker();
  1062. return;
  1063. }
  1064. HBRadarListEntry& entry = it->second;
  1065. LLVector3d pos;
  1066. if (sUpdatesPerSecond)
  1067. {
  1068. pos = entry.getPosition();
  1069. }
  1070. else
  1071. {
  1072. LLVOAvatar* avatarp = gObjectList.findAvatar(mTrackedAvatar);
  1073. if (!avatarp)
  1074. {
  1075. stopTracker();
  1076. return;
  1077. }
  1078. pos = gAgent.getPosGlobalFromAgent(avatarp->getCharacterPosition());
  1079. }
  1080. F32 dist = (pos - gTracker.getTrackedPositionGlobal()).length();
  1081. if (dist > 1.f)
  1082. {
  1083. std::string name = entry.getListedName();
  1084. gTracker.trackLocation(pos, name, "");
  1085. }
  1086. }
  1087. else
  1088. {
  1089. stopTracker();
  1090. }
  1091. }
  1092. HBRadarListEntry* HBFloaterRadar::getAvatarEntry(const LLUUID& avid)
  1093. {
  1094. if (avid.isNull())
  1095. {
  1096. return NULL;
  1097. }
  1098. avatar_list_t::iterator iter = mAvatars.find(avid);
  1099. return iter != mAvatars.end() ? &iter->second : NULL;
  1100. }
  1101. void HBFloaterRadar::removeFocusFromAll()
  1102. {
  1103. for (avatar_list_t::iterator iter = mAvatars.begin(), end = mAvatars.end();
  1104. iter != end; ++iter)
  1105. {
  1106. HBRadarListEntry* entry = &iter->second;
  1107. entry->setFocus(false);
  1108. }
  1109. }
  1110. void HBFloaterRadar::focusOnCurrent()
  1111. {
  1112. if (mAvatars.empty())
  1113. {
  1114. return;
  1115. }
  1116. for (avatar_list_t::iterator iter = mAvatars.begin(), end = mAvatars.end();
  1117. iter != end; iter++)
  1118. {
  1119. if (iter->first == mFocusedAvatar)
  1120. {
  1121. HBRadarListEntry& entry = iter->second;
  1122. if (!entry.isDead())
  1123. {
  1124. removeFocusFromAll();
  1125. entry.setFocus(true);
  1126. gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
  1127. }
  1128. break;
  1129. }
  1130. }
  1131. }
  1132. void HBFloaterRadar::focusOnPrev(bool marked_only)
  1133. {
  1134. if (mAvatars.empty())
  1135. {
  1136. return;
  1137. }
  1138. HBRadarListEntry* prev = NULL;
  1139. for (avatar_list_t::iterator iter = mAvatars.begin(), end = mAvatars.end();
  1140. iter != end; ++iter)
  1141. {
  1142. HBRadarListEntry* entry = &iter->second;
  1143. if (entry->isDead())
  1144. {
  1145. continue;
  1146. }
  1147. if (prev && iter->first == mFocusedAvatar)
  1148. {
  1149. break;
  1150. }
  1151. if ((!marked_only && entry->isDrawn()) || entry->isMarked())
  1152. {
  1153. prev = entry;
  1154. }
  1155. }
  1156. if (prev)
  1157. {
  1158. removeFocusFromAll();
  1159. prev->setFocus(true);
  1160. mFocusedAvatar = prev->getID();
  1161. mAvatarList->selectByID(mFocusedAvatar);
  1162. gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
  1163. }
  1164. }
  1165. void HBFloaterRadar::focusOnNext(bool marked_only)
  1166. {
  1167. if (mAvatars.empty())
  1168. {
  1169. return;
  1170. }
  1171. HBRadarListEntry* next = NULL;
  1172. bool found = false;
  1173. for (avatar_list_t::iterator iter = mAvatars.begin(), end = mAvatars.end();
  1174. iter != end; ++iter)
  1175. {
  1176. HBRadarListEntry* entry = &iter->second;
  1177. if (entry->isDead())
  1178. {
  1179. continue;
  1180. }
  1181. if (!next && ((!marked_only && entry->isDrawn()) || entry->isMarked()))
  1182. {
  1183. next = entry;
  1184. }
  1185. if (found && ((!marked_only && entry->isDrawn()) || entry->isMarked()))
  1186. {
  1187. next = entry;
  1188. break;
  1189. }
  1190. if (iter->first == mFocusedAvatar)
  1191. {
  1192. found = true;
  1193. }
  1194. }
  1195. if (next)
  1196. {
  1197. removeFocusFromAll();
  1198. next->setFocus(true);
  1199. mFocusedAvatar = next->getID();
  1200. mAvatarList->selectByID(mFocusedAvatar);
  1201. gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
  1202. }
  1203. }
  1204. void HBFloaterRadar::doCommand(void (*func)(const LLUUID& avid))
  1205. {
  1206. uuid_vec_t ids = mAvatarList->getSelectedIDs();
  1207. for (U32 i = 0, count = ids.size(); i < count; ++i)
  1208. {
  1209. const LLUUID& avid = ids[i];
  1210. HBRadarListEntry* entry = getAvatarEntry(avid);
  1211. if (entry)
  1212. {
  1213. llinfos << "Executing command on " << entry->getListedName()
  1214. << llendl;
  1215. func(avid);
  1216. }
  1217. }
  1218. }
  1219. std::string HBFloaterRadar::getSelectedNames(const std::string& separator)
  1220. {
  1221. std::string ret;
  1222. uuid_vec_t ids = mAvatarList->getSelectedIDs();
  1223. for (U32 i = 0, count = ids.size(); i < count; ++i)
  1224. {
  1225. const LLUUID& avid = ids[i];
  1226. HBRadarListEntry* entry = getAvatarEntry(avid);
  1227. if (entry)
  1228. {
  1229. if (!ret.empty())
  1230. {
  1231. ret += separator;
  1232. }
  1233. ret += entry->getLegacyName();
  1234. }
  1235. }
  1236. return ret;
  1237. }
  1238. std::string HBFloaterRadar::getSelectedName()
  1239. {
  1240. LLUUID id = getSelectedID();
  1241. HBRadarListEntry* entry = getAvatarEntry(id);
  1242. return entry ? entry->getLegacyName() : "";
  1243. }
  1244. LLUUID HBFloaterRadar::getSelectedID()
  1245. {
  1246. LLScrollListItem* item = mAvatarList->getFirstSelected();
  1247. return item ? item->getUUID() : LLUUID::null;
  1248. }
  1249. //static
  1250. bool HBFloaterRadar::setAvatarNameColor(const LLUUID& id, const LLColor4& col)
  1251. {
  1252. HBFloaterRadar* self = findInstance();
  1253. if (!self)
  1254. {
  1255. return true; // When no radar instance exists, report a success.
  1256. }
  1257. // First, make sure the list is up to date.
  1258. self->updateAvatarList();
  1259. HBRadarListEntry* entry = self->getAvatarEntry(id);
  1260. if (!entry)
  1261. {
  1262. return false; // Avatar not found in Radar list.
  1263. }
  1264. entry->setColor(col);
  1265. return true;
  1266. }
  1267. //static
  1268. void HBFloaterRadar::setRenderStatusDirty(const LLUUID& avid)
  1269. {
  1270. HBFloaterRadar* self = findInstance();
  1271. if (!self)
  1272. {
  1273. return; // Nothing to do.
  1274. }
  1275. bool maybe_derendered = !gObjectList.mBlackListedObjects.empty();
  1276. if (avid.notNull())
  1277. {
  1278. HBRadarListEntry* entry = self->getAvatarEntry(avid);
  1279. if (entry)
  1280. {
  1281. entry->mDerendered =
  1282. maybe_derendered &&
  1283. gObjectList.mBlackListedObjects.count(avid);
  1284. }
  1285. return;
  1286. }
  1287. for (avatar_list_t::iterator it = self->mAvatars.begin(),
  1288. end = self->mAvatars.end();
  1289. it != end; ++it)
  1290. {
  1291. it->second.mDerendered =
  1292. maybe_derendered &&
  1293. gObjectList.mBlackListedObjects.count(it->first);
  1294. }
  1295. }
  1296. //static
  1297. void HBFloaterRadar::onTabChanged(void* userdata, bool from_click)
  1298. {
  1299. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1300. if (self && self->mTabContainer)
  1301. {
  1302. gSavedSettings.setS32("LastRadarTab",
  1303. self->mTabContainer->getCurrentPanelIndex());
  1304. }
  1305. }
  1306. //static
  1307. void HBFloaterRadar::onClickIM(void* userdata)
  1308. {
  1309. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1310. if (self)
  1311. {
  1312. uuid_vec_t ids = self->mAvatarList->getSelectedIDs();
  1313. LLAvatarActions::startIM(ids);
  1314. }
  1315. }
  1316. //static
  1317. void HBFloaterRadar::onClickTeleportOffer(void* userdata)
  1318. {
  1319. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1320. if (self)
  1321. {
  1322. uuid_vec_t ids = self->mAvatarList->getSelectedIDs();
  1323. if (!ids.empty())
  1324. {
  1325. LLAvatarActions::offerTeleport(ids);
  1326. }
  1327. }
  1328. }
  1329. //static
  1330. void HBFloaterRadar::onClickTeleportRequest(void* userdata)
  1331. {
  1332. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1333. if (self)
  1334. {
  1335. uuid_vec_t ids = self->mAvatarList->getSelectedIDs();
  1336. if (!ids.empty())
  1337. {
  1338. LLAvatarActions::teleportRequest(ids[0]);
  1339. }
  1340. }
  1341. }
  1342. //static
  1343. void HBFloaterRadar::onClickTrack(void* userdata)
  1344. {
  1345. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1346. if (!self) return;
  1347. LLScrollListItem* item = self->mAvatarList->getFirstSelected();
  1348. if (!item) return;
  1349. LLUUID avid = item->getUUID();
  1350. if (self->mTracking && self->mTrackedAvatar == avid)
  1351. {
  1352. self->stopTracker();
  1353. }
  1354. else
  1355. {
  1356. self->startTracker(avid);
  1357. }
  1358. }
  1359. //static
  1360. void HBFloaterRadar::onClickMark(void* userdata)
  1361. {
  1362. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1363. if (!self) return;
  1364. uuid_vec_t ids = self->mAvatarList->getSelectedIDs();
  1365. for (U32 i = 0, count = ids.size(); i < count; ++i)
  1366. {
  1367. const LLUUID& avid = ids[i];
  1368. HBRadarListEntry* entry = self->getAvatarEntry(avid);
  1369. if (entry)
  1370. {
  1371. bool marked = entry->toggleMark();
  1372. if (marked)
  1373. {
  1374. self->mMarkedAvatars.emplace(avid);
  1375. }
  1376. else
  1377. {
  1378. self->mMarkedAvatars.erase(avid);
  1379. }
  1380. if (gAutomationp)
  1381. {
  1382. gAutomationp->onRadarMark(avid, entry->getLegacyName(),
  1383. marked);
  1384. }
  1385. }
  1386. }
  1387. self->saveMarkedToFile();
  1388. }
  1389. //static
  1390. void HBFloaterRadar::onClickFocus(void* userdata)
  1391. {
  1392. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1393. if (!self) return;
  1394. LLScrollListItem* item = self->mAvatarList->getFirstSelected();
  1395. if (item)
  1396. {
  1397. self->mFocusedAvatar = item->getUUID();
  1398. self->focusOnCurrent();
  1399. }
  1400. }
  1401. //static
  1402. void HBFloaterRadar::onClickPrevInList(void* userdata)
  1403. {
  1404. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1405. if (self)
  1406. {
  1407. self->focusOnPrev(false);
  1408. }
  1409. }
  1410. //static
  1411. void HBFloaterRadar::onClickNextInList(void* userdata)
  1412. {
  1413. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1414. if (self)
  1415. {
  1416. self->focusOnNext(false);
  1417. }
  1418. }
  1419. //static
  1420. void HBFloaterRadar::onClickPrevMarked(void* userdata)
  1421. {
  1422. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1423. if (self)
  1424. {
  1425. self->focusOnPrev(true);
  1426. }
  1427. }
  1428. //static
  1429. void HBFloaterRadar::onClickNextMarked(void* userdata)
  1430. {
  1431. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1432. if (self)
  1433. {
  1434. self->focusOnNext(true);
  1435. }
  1436. }
  1437. //static
  1438. void HBFloaterRadar::onClickClearSavedMarked(void* userdata)
  1439. {
  1440. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1441. if (self)
  1442. {
  1443. self->mMarkedAvatars.clear();
  1444. if (sRememberMarked && self->mAvatars.size() > 0)
  1445. {
  1446. for (avatar_list_t::iterator iter = self->mAvatars.begin(),
  1447. end = self->mAvatars.end();
  1448. iter != end; ++iter)
  1449. {
  1450. if (iter->second.isMarked())
  1451. {
  1452. self->mMarkedAvatars.emplace(iter->first);
  1453. }
  1454. }
  1455. }
  1456. self->saveMarkedToFile(true);
  1457. }
  1458. }
  1459. //static
  1460. void HBFloaterRadar::onCheckRadarAlerts(LLUICtrl* ctrl, void* userdata)
  1461. {
  1462. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1463. LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl;
  1464. if (self && check)
  1465. {
  1466. bool enabled = check->get();
  1467. self->mSimAlertsCheck->setEnabled(enabled);
  1468. self->mDrawAlertsCheck->setEnabled(enabled);
  1469. self->mShoutAlertsCheck->setEnabled(enabled);
  1470. self->mChatAlertsCheck->setEnabled(enabled);
  1471. }
  1472. }
  1473. //static
  1474. void HBFloaterRadar::onCheckUseLegacyNames(LLUICtrl* ctrl, void*)
  1475. {
  1476. LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl;
  1477. if (check)
  1478. {
  1479. gSavedSettings.setBool("RadarUseLegacyNames", check->get());
  1480. }
  1481. }
  1482. //static
  1483. void HBFloaterRadar::onClickGetKey(void* userdata)
  1484. {
  1485. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1486. if (!self) return;
  1487. LLScrollListItem* item = self->mAvatarList->getFirstSelected();
  1488. if (!item) return;
  1489. LLUUID avid = item->getUUID();
  1490. if (gWindowp)
  1491. {
  1492. gWindowp->copyTextToClipboard(utf8str_to_wstring(avid.asString()));
  1493. }
  1494. }
  1495. //static
  1496. void HBFloaterRadar::onClickSendKeys(void* userdata)
  1497. {
  1498. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1499. if (!self || self->mAvatars.empty())
  1500. {
  1501. return;
  1502. }
  1503. for (avatar_list_t::iterator iter = self->mAvatars.begin(),
  1504. end = self->mAvatars.end();
  1505. iter != end; ++iter)
  1506. {
  1507. const HBRadarListEntry& entry = iter->second;
  1508. if (!entry.isDead() && entry.isInSim())
  1509. {
  1510. announce(iter->first.asString());
  1511. }
  1512. }
  1513. }
  1514. static void send_estate_message(const char* request, const LLUUID& target)
  1515. {
  1516. if (!gAgent.getRegion()) return;
  1517. LLUUID invoice;
  1518. // This seems to provide an ID so that the sim can say which request it's
  1519. // replying to.
  1520. invoice.generate();
  1521. llinfos << "Sending estate request '" << request << "'" << llendl;
  1522. LLMessageSystem* msg = gMessageSystemp;
  1523. msg->newMessage(_PREHASH_EstateOwnerMessage);
  1524. msg->nextBlockFast(_PREHASH_AgentData);
  1525. msg->addUUIDFast(_PREHASH_AgentID, gAgentID);
  1526. msg->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
  1527. msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); //not used
  1528. msg->nextBlock(_PREHASH_MethodData);
  1529. msg->addString(_PREHASH_Method, request);
  1530. msg->addUUID(_PREHASH_Invoice, invoice);
  1531. // Agent id
  1532. msg->nextBlock(_PREHASH_ParamList);
  1533. msg->addString(_PREHASH_Parameter, gAgentID.asString().c_str());
  1534. // Target
  1535. msg->nextBlock(_PREHASH_ParamList);
  1536. msg->addString(_PREHASH_Parameter, target.asString().c_str());
  1537. msg->sendReliable(gAgent.getRegionHost());
  1538. }
  1539. static void cmd_freeze(const LLUUID& avid)
  1540. {
  1541. LLAvatarActions::sendFreeze(avid, true);
  1542. }
  1543. static void cmd_unfreeze(const LLUUID& avid)
  1544. {
  1545. LLAvatarActions::sendFreeze(avid, false);
  1546. }
  1547. static void cmd_eject(const LLUUID& avid)
  1548. {
  1549. LLAvatarActions::sendEject(avid, false);
  1550. }
  1551. static void cmd_ban(const LLUUID& avid)
  1552. {
  1553. LLAvatarActions::sendEject(avid, true);
  1554. }
  1555. static void cmd_profile(const LLUUID& avid)
  1556. {
  1557. LLFloaterAvatarInfo::showFromDirectory(avid);
  1558. }
  1559. static void cmd_estate_eject(const LLUUID& avid)
  1560. {
  1561. send_estate_message("teleporthomeuser", avid);
  1562. }
  1563. //static
  1564. void HBFloaterRadar::callbackFreeze(const LLSD& notification,
  1565. const LLSD& response)
  1566. {
  1567. HBFloaterRadar* self = findInstance();
  1568. if (!self) return;
  1569. S32 option = LLNotification::getSelectedOption(notification, response);
  1570. if (option == 0)
  1571. {
  1572. self->doCommand(cmd_freeze);
  1573. }
  1574. else if (option == 1)
  1575. {
  1576. self->doCommand(cmd_unfreeze);
  1577. }
  1578. }
  1579. //static
  1580. void HBFloaterRadar::callbackEject(const LLSD& notification,
  1581. const LLSD& response)
  1582. {
  1583. HBFloaterRadar* self = findInstance();
  1584. if (!self) return;
  1585. S32 option = LLNotification::getSelectedOption(notification, response);
  1586. if (option == 0)
  1587. {
  1588. self->doCommand(cmd_eject);
  1589. }
  1590. else if (option == 1)
  1591. {
  1592. self->doCommand(cmd_ban);
  1593. }
  1594. }
  1595. //static
  1596. void HBFloaterRadar::callbackEjectFromEstate(const LLSD& notification,
  1597. const LLSD& response)
  1598. {
  1599. HBFloaterRadar* self = findInstance();
  1600. if (!self) return;
  1601. if (LLNotification::getSelectedOption(notification, response) == 0)
  1602. {
  1603. self->doCommand(cmd_estate_eject);
  1604. }
  1605. }
  1606. //static
  1607. void HBFloaterRadar::callbackIdle(void* userdata)
  1608. {
  1609. static U32 last_update_frame = 0;
  1610. LL_FAST_TIMER(FTM_IDLE_CB_RADAR);
  1611. HBFloaterRadar* self = findInstance();
  1612. if (!self) return;
  1613. //MK
  1614. if (gRLenabled &&
  1615. (gRLInterface.mContainsShownames || gRLInterface.mContainsShowNearby ||
  1616. gRLInterface.mContainsShownametags))
  1617. {
  1618. self->destroy();
  1619. gSavedSettings.setBool("ShowRadar", false);
  1620. return;
  1621. }
  1622. //mk
  1623. // In case of slow rendering do not cause more lag...
  1624. if (gFrameCount - last_update_frame > 4)
  1625. {
  1626. if (sUpdatesPerSecond)
  1627. {
  1628. if (sUpdateTimer.getElapsedTimeF32() >=
  1629. 1.f / (F32)sUpdatesPerSecond)
  1630. {
  1631. self->updateAvatarList();
  1632. sUpdateTimer.reset();
  1633. last_update_frame = gFrameCount;
  1634. }
  1635. }
  1636. else
  1637. {
  1638. self->refreshTracker();
  1639. }
  1640. }
  1641. }
  1642. //static
  1643. void HBFloaterRadar::onClickFreeze(void* userdata)
  1644. {
  1645. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1646. if (!self) return;
  1647. LLSD args;
  1648. args["AVATAR_NAME"] = self->getSelectedNames();
  1649. gNotifications.add("FreezeAvatarFullname", args, LLSD(), callbackFreeze);
  1650. }
  1651. //static
  1652. void HBFloaterRadar::onClickEject(void* userdata)
  1653. {
  1654. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1655. if (!self) return;
  1656. LLSD args;
  1657. args["AVATAR_NAME"] = self->getSelectedNames();
  1658. gNotifications.add("EjectAvatarFullname", args, LLSD(), callbackEject);
  1659. }
  1660. //static
  1661. void HBFloaterRadar::onSearchEdit(const std::string& search_string,
  1662. void* userdata)
  1663. {
  1664. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1665. if (self)
  1666. {
  1667. self->mFilterString = search_string;
  1668. LLStringUtil::trim(self->mFilterString);
  1669. LLStringUtil::toLower(self->mFilterString);
  1670. self->refreshAvatarList();
  1671. }
  1672. }
  1673. //static
  1674. void HBFloaterRadar::onClickMute(void* userdata)
  1675. {
  1676. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1677. if (!self) return;
  1678. uuid_vec_t ids = self->mAvatarList->getSelectedIDs();
  1679. for (U32 i = 0, count = ids.size(); i < count; ++i)
  1680. {
  1681. const LLUUID& avid = ids[i];
  1682. avatar_list_t::iterator it = self->mAvatars.find(avid);
  1683. if (it == self->mAvatars.end())
  1684. {
  1685. llwarns << "Could not find " << avid
  1686. << " in the Radar list; (un)mute action aborted."
  1687. << llendl;
  1688. continue;
  1689. }
  1690. const std::string& name = it->second.getLegacyName();
  1691. if (LLMuteList::isMuted(avid))
  1692. {
  1693. LLMute mute(avid, name, LLMute::AGENT);
  1694. LLMuteList::remove(mute);
  1695. }
  1696. else
  1697. {
  1698. LLMute mute(avid, name, LLMute::AGENT);
  1699. if (LLMuteList::add(mute))
  1700. {
  1701. LLFloaterMute::selectMute(mute.mID);
  1702. }
  1703. }
  1704. }
  1705. }
  1706. //static
  1707. void HBFloaterRadar::onClickDerender(void* userdata)
  1708. {
  1709. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1710. if (!self) return;
  1711. uuid_vec_t ids = self->mAvatarList->getSelectedIDs();
  1712. for (U32 i = 0, count = ids.size(); i < count; ++i)
  1713. {
  1714. const LLUUID& avid = ids[i];
  1715. bool derendered = gObjectList.mBlackListedObjects.count(avid);
  1716. if (derendered)
  1717. {
  1718. // Remove from the black list
  1719. gObjectList.mBlackListedObjects.erase(avid);
  1720. }
  1721. else
  1722. {
  1723. // Add to the black list
  1724. gObjectList.mBlackListedObjects.emplace(avid);
  1725. // Derender by killing the object.
  1726. LLViewerObject* vobj = gObjectList.findObject(avid);
  1727. if (vobj)
  1728. {
  1729. gObjectList.killObject(vobj);
  1730. }
  1731. }
  1732. // Update any cached derendered status
  1733. HBRadarListEntry* entry = self->getAvatarEntry(avid);
  1734. if (entry)
  1735. {
  1736. entry->mDerendered = !derendered; // Status just got toggled
  1737. }
  1738. }
  1739. }
  1740. //static
  1741. void HBFloaterRadar::onClickEjectFromEstate(void* userdata)
  1742. {
  1743. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1744. if (!self) return;
  1745. LLSD args;
  1746. LLSD payload;
  1747. args["EVIL_USER"] = self->getSelectedNames();
  1748. gNotifications.add("EstateKickUser", args, payload,
  1749. callbackEjectFromEstate);
  1750. }
  1751. //static
  1752. void HBFloaterRadar::onClickAR(void* userdata)
  1753. {
  1754. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1755. if (!self) return;
  1756. LLScrollListItem* item = self->mAvatarList->getFirstSelected();
  1757. if (item)
  1758. {
  1759. LLUUID avid = item->getUUID();
  1760. if (self->getAvatarEntry(avid))
  1761. {
  1762. LLFloaterReporter::showFromAvatar(avid);
  1763. }
  1764. }
  1765. }
  1766. //static
  1767. void HBFloaterRadar::onClickProfile(void* userdata)
  1768. {
  1769. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1770. if (self)
  1771. {
  1772. self->doCommand(cmd_profile);
  1773. }
  1774. }
  1775. //static
  1776. void HBFloaterRadar::onClickTeleportTo(void* userdata)
  1777. {
  1778. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1779. if (!self) return;
  1780. LLScrollListItem* item = self->mAvatarList->getFirstSelected();
  1781. if (item)
  1782. {
  1783. LLUUID avid = item->getUUID();
  1784. HBRadarListEntry* entry = self->getAvatarEntry(avid);
  1785. if (entry)
  1786. {
  1787. gAgent.teleportViaLocation(entry->getPosition());
  1788. }
  1789. }
  1790. }
  1791. //static
  1792. void HBFloaterRadar::onDoubleClick(void* userdata)
  1793. {
  1794. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1795. if (self)
  1796. {
  1797. LLScrollListItem* item = self->mAvatarList->getFirstSelected();
  1798. if (item)
  1799. {
  1800. const LLUUID& avid = item->getUUID();
  1801. if (gObjectList.findAvatar(avid))
  1802. {
  1803. HBFloaterInspectAvatar::show(avid);
  1804. }
  1805. }
  1806. }
  1807. }
  1808. //static
  1809. void HBFloaterRadar::onSelectName(LLUICtrl* ctrlp, void* userdata)
  1810. {
  1811. HBFloaterRadar* self = (HBFloaterRadar*)userdata;
  1812. if (!self) return;
  1813. uuid_vec_t ids = self->mAvatarList->getSelectedIDs();
  1814. U32 count = ids.size();
  1815. // Check whether selected avatars are in the same state or not regarding
  1816. // marking, muting and rendering, so that the corresponding button is only
  1817. // enabled when the same action can be performed on them.
  1818. bool marked = false;
  1819. bool unmarked = false;
  1820. bool muted = false;
  1821. bool unmuted = false;
  1822. bool derendered = false;
  1823. bool rendered = false;
  1824. for (U32 i = 0; i < count; ++i)
  1825. {
  1826. const LLUUID& avid = ids[i];
  1827. HBRadarListEntry* entry = self->getAvatarEntry(avid);
  1828. if (!entry)
  1829. {
  1830. continue;
  1831. }
  1832. if (entry->isMarked())
  1833. {
  1834. marked = true;
  1835. }
  1836. else
  1837. {
  1838. unmarked = true;
  1839. }
  1840. if (entry->isMuted())
  1841. {
  1842. muted = true;
  1843. }
  1844. else
  1845. {
  1846. unmuted = true;
  1847. }
  1848. if (entry->isDerendered())
  1849. {
  1850. derendered = true;
  1851. }
  1852. else
  1853. {
  1854. rendered = true;
  1855. }
  1856. }
  1857. self->mMarkButton->setEnabled(marked != unmarked);
  1858. self->mMuteButton->setEnabled(muted != unmuted);
  1859. self->mDerenderButton->setEnabled(!derendered && rendered);
  1860. self->mRerenderButton->setEnabled(derendered && !rendered);
  1861. // Buttons that must be enabled when one or more avatars are selected
  1862. bool enabled = count > 0;
  1863. self->mProfileButton->setEnabled(enabled);
  1864. self->mIMButton->setEnabled(enabled);
  1865. self->mTPOfferButton->setEnabled(enabled);
  1866. self->mFreezeButton->setEnabled(enabled);
  1867. self->mEjectButton->setEnabled(enabled);
  1868. self->mEstateEjectButton->setEnabled(enabled);
  1869. // Buttons that must be enabled when only one avatar is selected
  1870. enabled = count == 1;
  1871. self->mTrackButton->setEnabled(enabled);
  1872. self->mRequestTPButton->setEnabled(enabled);
  1873. self->mTeleportToButton->setEnabled(enabled);
  1874. self->mRequestTPButton->setEnabled(enabled);
  1875. self->mARButton->setEnabled(enabled);
  1876. self->mGetKeyButton->setEnabled(enabled);
  1877. // Buttons that must be enabled when the selected avatar is drawn
  1878. if (enabled)
  1879. {
  1880. enabled = false;
  1881. LLScrollListItem* item = self->mAvatarList->getFirstSelected();
  1882. if (item)
  1883. {
  1884. LLUUID avid = item->getUUID();
  1885. HBRadarListEntry* entry = self->getAvatarEntry(avid);
  1886. if (entry)
  1887. {
  1888. enabled = entry->isDrawn();
  1889. }
  1890. }
  1891. self->mFocusButton->setEnabled(enabled);
  1892. self->mPrevInListButton->setEnabled(enabled);
  1893. self->mNextInListButton->setEnabled(enabled);
  1894. }
  1895. // Note: ctrlp is NULL when this method gets called after a list refresh
  1896. // and we do not want this false commit event transmitted to the Lua
  1897. // callback. Likewise, a commit happens when the list is emptied, and we
  1898. // do not want this event to be transmitted, thus the test for empty ids.
  1899. // Finally, we only transmit the selection when it changed.
  1900. if (gAutomationp && ctrlp && !ids.empty() && self->mLastSelection != ids)
  1901. {
  1902. gAutomationp->onRadarSelection(ids);
  1903. self->mLastSelection.swap(ids);
  1904. }
  1905. }