aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llfloateravatarlist.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/llfloateravatarlist.cpp')
-rw-r--r--linden/indra/newview/llfloateravatarlist.cpp1471
1 files changed, 1471 insertions, 0 deletions
diff --git a/linden/indra/newview/llfloateravatarlist.cpp b/linden/indra/newview/llfloateravatarlist.cpp
new file mode 100644
index 0000000..2346e94
--- /dev/null
+++ b/linden/indra/newview/llfloateravatarlist.cpp
@@ -0,0 +1,1471 @@
1/**
2 * @file llfloateravatarlist.cpp
3 * @brief Avatar list/radar floater
4 *
5 * @author Dale Glass <dale@daleglass.net>, (C) 2007
6 */
7
8/**
9 * Rewritten by jcool410
10 * Removed usage of globals
11 * Removed TrustNET
12 * Added utilization of "minimap" data
13 * Heavily modified by Henri Beauchamp (the laggy spying tool becomes a true,
14 * low lag radar)
15 */
16
17#include "llviewerprecompiledheaders.h"
18
19#include "llavatarconstants.h"
20#include "llfloateravatarlist.h"
21
22#include "llcachename.h"
23#include "lluictrlfactory.h"
24#include "llviewerwindow.h"
25#include "llscrolllistctrl.h"
26#include "llradiogroup.h"
27#include "llviewercontrol.h"
28
29#include "llvoavatar.h"
30#include "llimview.h"
31#include "llfloateravatarinfo.h"
32#include "llregionflags.h"
33#include "llfloaterreporter.h"
34#include "llagent.h"
35#include "llviewerregion.h"
36#include "lltracker.h"
37#include "llviewerstats.h"
38#include "llerror.h"
39#include "llchat.h"
40#include "llfloaterchat.h"
41#include "llviewermessage.h"
42#include "llweb.h"
43#include "llviewerobjectlist.h"
44#include "llmutelist.h"
45#include "llcallbacklist.h"
46#include "llviewermenu.h"
47
48#include <time.h>
49#include <string.h>
50
51#include <map>
52
53
54#include "llworld.h"
55
56#include "llsdutil.h"
57
58/**
59 * @brief How long to keep people who are gone in the list and in memory.
60 */
61const F32 DEAD_KEEP_TIME = 10.0f;
62
63extern U32 gFrameCount;
64
65typedef enum e_radar_alert_type
66{
67 ALERT_TYPE_SIM = 0,
68 ALERT_TYPE_DRAW = 1,
69 ALERT_TYPE_SHOUTRANGE = 2,
70 ALERT_TYPE_CHATRANGE = 3
71} ERadarAlertType;
72
73void announce(std::string msg)
74{
75 //llinfos << "Radar broadcasting key: " << key.asString() << " - on channel " << gSavedSettings.getS32("RadarChatKeysChannel") << llendl;
76 gMessageSystem->newMessage("ScriptDialogReply");
77 gMessageSystem->nextBlock("AgentData");
78 gMessageSystem->addUUID("AgentID", gAgent.getID());
79 gMessageSystem->addUUID("SessionID", gAgent.getSessionID());
80 gMessageSystem->nextBlock("Data");
81 gMessageSystem->addUUID("ObjectID", gAgent.getID());
82 gMessageSystem->addS32("ChatChannel", gSavedSettings.getS32("RadarChatKeysChannel"));
83 gMessageSystem->addS32("ButtonIndex", 1);
84 gMessageSystem->addString("ButtonLabel", msg);
85 gAgent.sendReliableMessage();
86}
87
88void chat_avatar_status(std::string name, LLUUID key, ERadarAlertType type, bool entering)
89{
90 if (gSavedSettings.getBOOL("RadarChatAlerts"))
91 {
92 LLChat chat;
93 switch(type)
94 {
95 case ALERT_TYPE_SIM:
96 if (gSavedSettings.getBOOL("MiniMapNotifySimRange"))
97 {
98 chat.mText = name+" has "+(entering ? "entered" : "left")+" the sim.";
99 }
100 break;
101 case ALERT_TYPE_DRAW:
102 if (gSavedSettings.getBOOL("RadarAlertDraw"))
103 {
104 chat.mText = name+" has "+(entering ? "entered" : "left")+" draw distance.";
105 }
106 break;
107 case ALERT_TYPE_SHOUTRANGE:
108 if (gSavedSettings.getBOOL("RadarAlertShoutRange"))
109 {
110 chat.mText = name+" has "+(entering ? "entered" : "left")+" shout range.";
111 }
112 break;
113 case ALERT_TYPE_CHATRANGE:
114 if (gSavedSettings.getBOOL("MiniMapNotifyChatRange"))
115 {
116 chat.mText = name+" has "+(entering ? "entered" : "left")+" chat range.";
117 }
118 break;
119 }
120 if (chat.mText != "")
121 {
122 chat.mSourceType = CHAT_SOURCE_SYSTEM;
123 LLFloaterChat::addChat(chat);
124 }
125 }
126 if (type == ALERT_TYPE_SIM && entering && gSavedSettings.getBOOL("RadarChatKeys"))
127 {
128 announce(key.asString());
129 }
130}
131
132LLAvatarListEntry::LLAvatarListEntry(const LLUUID& id, const std::string &name, const LLVector3d &position) :
133 mID(id), mName(name), mDisplayName(name), mPosition(position), mDrawPosition(), mMarked(FALSE),
134 mFocused(FALSE), mUpdateTimer(), mFrame(gFrameCount), mInSimFrame(U32_MAX), mInDrawFrame(U32_MAX),
135 mInChatFrame(U32_MAX), mInShoutFrame(U32_MAX)
136{
137}
138
139void LLAvatarListEntry::setPosition(LLVector3d position, bool this_sim, bool drawn, bool chatrange, bool shoutrange)
140{
141 if (drawn)
142 {
143 mDrawPosition = position;
144 }
145 else if (mInDrawFrame == U32_MAX)
146 {
147 mDrawPosition.setZero();
148 }
149
150 mPosition = position;
151
152 mFrame = gFrameCount;
153 if (this_sim)
154 {
155 if (mInSimFrame == U32_MAX)
156 {
157 chat_avatar_status(mName, mID, ALERT_TYPE_SIM, true);
158 }
159 mInSimFrame = mFrame;
160 }
161 if (drawn)
162 {
163 if (mInDrawFrame == U32_MAX)
164 {
165 chat_avatar_status(mName, mID, ALERT_TYPE_DRAW, true);
166 }
167 mInDrawFrame = mFrame;
168 }
169 if (shoutrange)
170 {
171 if (mInShoutFrame == U32_MAX)
172 {
173 chat_avatar_status(mName, mID, ALERT_TYPE_SHOUTRANGE, true);
174 }
175 mInShoutFrame = mFrame;
176 }
177 if (chatrange)
178 {
179 if (mInChatFrame == U32_MAX)
180 {
181 chat_avatar_status(mName, mID, ALERT_TYPE_CHATRANGE, true);
182 }
183 mInChatFrame = mFrame;
184 }
185
186 mUpdateTimer.start();
187}
188
189bool LLAvatarListEntry::getAlive()
190{
191 U32 current = gFrameCount;
192 if (mInSimFrame != U32_MAX && (current - mInSimFrame) >= 2)
193 {
194 mInSimFrame = U32_MAX;
195 chat_avatar_status(mName, mID, ALERT_TYPE_SIM, false);
196 }
197 if (mInDrawFrame != U32_MAX && (current - mInDrawFrame) >= 2)
198 {
199 mInDrawFrame = U32_MAX;
200 chat_avatar_status(mName, mID, ALERT_TYPE_DRAW, false);
201 }
202 if (mInShoutFrame != U32_MAX && (current - mInShoutFrame) >= 2)
203 {
204 mInShoutFrame = U32_MAX;
205 chat_avatar_status(mName, mID, ALERT_TYPE_SHOUTRANGE, false);
206 }
207 if (mInChatFrame != U32_MAX && (current - mInChatFrame) >= 2)
208 {
209 mInChatFrame = U32_MAX;
210 chat_avatar_status(mName, mID, ALERT_TYPE_CHATRANGE, false);
211 }
212 return ((current - mFrame) <= 2);
213}
214
215F32 LLAvatarListEntry::getEntryAgeSeconds()
216{
217 return mUpdateTimer.getElapsedTimeF32();
218}
219
220BOOL LLAvatarListEntry::isDead()
221{
222 return getEntryAgeSeconds() > DEAD_KEEP_TIME;
223}
224
225LLFloaterAvatarList* LLFloaterAvatarList::sInstance = NULL;
226
227LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar"))
228{
229 llassert_always(sInstance == NULL);
230 sInstance = this;
231 mUpdateRate = gSavedSettings.getU32("RadarUpdateRate") * 3 + 3;
232}
233
234LLFloaterAvatarList::~LLFloaterAvatarList()
235{
236 gIdleCallbacks.deleteFunction(LLFloaterAvatarList::callbackIdle);
237 sInstance = NULL;
238}
239
240//static
241void LLFloaterAvatarList::toggle(void*)
242{
243#ifdef LL_RRINTERFACE_H //MK
244 if (gRRenabled && gAgent.mRRInterface.mContainsShownames)
245 {
246 if (sInstance && sInstance->getVisible())
247 {
248 sInstance->close(false);
249 }
250 }
251#endif //mk
252 if (sInstance)
253 {
254 if (sInstance->getVisible())
255 {
256 sInstance->close(false);
257 }
258 else
259 {
260 sInstance->open();
261 }
262 }
263 else
264 {
265 showInstance();
266 }
267}
268
269//static
270void LLFloaterAvatarList::showInstance()
271{
272#ifdef LL_RRINTERFACE_H //MK
273 if (gRRenabled && gAgent.mRRInterface.mContainsShownames)
274 {
275 return;
276 }
277#endif //mk
278 if (sInstance)
279 {
280 if (!sInstance->getVisible())
281 {
282 sInstance->open();
283 }
284 }
285 else
286 {
287 sInstance = new LLFloaterAvatarList();
288 LLUICtrlFactory::getInstance()->buildFloater(sInstance, "floater_radar.xml");
289 }
290}
291
292void LLFloaterAvatarList::draw()
293{
294 LLFloater::draw();
295}
296
297void LLFloaterAvatarList::onOpen()
298{
299 gSavedSettings.setBOOL("ShowRadar", TRUE);
300 sInstance->setVisible(TRUE);
301}
302
303void LLFloaterAvatarList::onClose(bool app_quitting)
304{
305 sInstance->setVisible(FALSE);
306 if (!app_quitting)
307 {
308 gSavedSettings.setBOOL("ShowRadar", FALSE);
309 }
310 if (!gSavedSettings.getBOOL("RadarKeepOpen") || app_quitting)
311 {
312 destroy();
313 }
314}
315
316BOOL LLFloaterAvatarList::postBuild()
317{
318 // Default values
319 mTracking = FALSE;
320 mUpdate = TRUE;
321
322 // Hide them until some other way is found
323 // Users may not expect to find a Ban feature on the Eject button
324 childSetVisible("estate_ban_btn", false);
325
326 // Set callbacks
327 childSetAction("profile_btn", onClickProfile, this);
328 childSetAction("im_btn", onClickIM, this);
329 childSetAction("offer_btn", onClickTeleportOffer, this);
330 childSetAction("track_btn", onClickTrack, this);
331 childSetAction("mark_btn", onClickMark, this);
332 childSetAction("focus_btn", onClickFocus, this);
333 childSetAction("prev_in_list_btn", onClickPrevInList, this);
334 childSetAction("next_in_list_btn", onClickNextInList, this);
335 childSetAction("prev_marked_btn", onClickPrevMarked, this);
336 childSetAction("next_marked_btn", onClickNextMarked, this);
337
338 childSetAction("get_key_btn", onClickGetKey, this);
339
340 childSetAction("freeze_btn", onClickFreeze, this);
341 childSetAction("eject_btn", onClickEject, this);
342 childSetAction("mute_btn", onClickMute, this);
343 childSetAction("ar_btn", onClickAR, this);
344 childSetAction("teleport_btn", onClickTeleport, this);
345 childSetAction("estate_eject_btn", onClickEjectFromEstate, this);
346
347 childSetAction("send_keys_btn", onClickSendKeys, this);
348
349 getChild<LLRadioGroup>("update_rate")->setSelectedIndex(gSavedSettings.getU32("RadarUpdateRate"));
350 childSetCommitCallback("update_rate", onCommitUpdateRate, this);
351
352 // Get a pointer to the scroll list from the interface
353 mAvatarList = getChild<LLScrollListCtrl>("avatar_list");
354 mAvatarList->sortByColumn("distance", TRUE);
355 mAvatarList->setCommitOnSelectionChange(TRUE);
356 childSetCommitCallback("avatar_list", onSelectName, this);
357 refreshAvatarList();
358
359 gIdleCallbacks.addFunction(LLFloaterAvatarList::callbackIdle);
360
361 return TRUE;
362}
363
364void LLFloaterAvatarList::updateAvatarList()
365{
366 if (sInstance != this) return;
367
368#ifdef LL_RRINTERFACE_H //MK
369 if (gRRenabled && gAgent.mRRInterface.mContainsShownames)
370 {
371 close();
372 }
373#endif //mk
374
375 //llinfos << "radar refresh: updating map" << llendl;
376
377 // Check whether updates are enabled
378 LLCheckboxCtrl* check = getChild<LLCheckboxCtrl>("update_enabled_cb");
379 if (check && !check->getValue())
380 {
381 mUpdate = FALSE;
382 refreshTracker();
383 return;
384 }
385 else
386 {
387 mUpdate = TRUE;
388 }
389
390 LLVector3d mypos = gAgent.getPositionGlobal();
391
392 {
393 std::vector<LLUUID> avatar_ids;
394 std::vector<LLUUID> sorted_avatar_ids;
395 std::vector<LLVector3d> positions;
396
397 LLWorld::instance().getAvatars(&avatar_ids, &positions, mypos, F32_MAX);
398
399 sorted_avatar_ids = avatar_ids;
400 std::sort(sorted_avatar_ids.begin(), sorted_avatar_ids.end());
401
402 for (std::vector<LLCharacter*>::const_iterator iter = LLCharacter::sInstances.begin(); iter != LLCharacter::sInstances.end(); ++iter)
403 {
404 LLUUID avid = (*iter)->getID();
405
406 if (!std::binary_search(sorted_avatar_ids.begin(), sorted_avatar_ids.end(), avid))
407 {
408 avatar_ids.push_back(avid);
409 }
410 }
411
412 size_t i;
413 size_t count = avatar_ids.size();
414
415 for (i = 0; i < count; ++i)
416 {
417 std::string name, first, last;
418 const LLUUID &avid = avatar_ids[i];
419
420 LLVector3d position;
421 LLViewerObject *obj = gObjectList.findObject(avid);
422
423 if (obj)
424 {
425 LLVOAvatar* avatarp = dynamic_cast<LLVOAvatar*>(obj);
426
427 if (avatarp == NULL)
428 {
429 continue;
430 }
431
432 // Skip if avatar is dead(what's that?)
433 // or if the avatar is ourselves.
434 if (avatarp->isDead() || avatarp->isSelf())
435 {
436 continue;
437 }
438
439 // Get avatar data
440 position = gAgent.getPosGlobalFromAgent(avatarp->getCharacterPosition());
441 name = avatarp->getFullname();
442
443 // Apparently, sometimes the name comes out empty, with a " " name. This is because
444 // getFullname concatenates first and last name with a " " in the middle.
445 // This code will avoid adding a nameless entry to the list until it acquires a name.
446
447 //duped for lower section
448 if (name.empty() || (name.compare(" ") == 0))// || (name.compare(gCacheName->getDefaultName()) == 0))
449 {
450 if (gCacheName->getName(avid, first, last))
451 {
452 name = first + " " + last;
453 }
454 else
455 {
456 continue;
457 }
458 }
459
460#ifdef LL_DISPLAY_NAMES
461 std::string display_name = name;
462 if (LLAvatarName::sOmitResidentAsLastName)
463 {
464 LLStringUtil::replaceString(display_name, " Resident", "");
465 }
466 if (LLAvatarNameCache::useDisplayNames())
467 {
468 LLAvatarName avatar_name;
469 if (LLAvatarNameCache::get(avid, &avatar_name))
470 {
471 if (LLAvatarNameCache::useDisplayNames() == 2)
472 {
473 display_name = avatar_name.mDisplayName;
474 }
475 else
476 {
477 display_name = avatar_name.getNames();
478 }
479 }
480 }
481#endif
482
483 if (avid.isNull())
484 {
485 //llinfos << "Key empty for avatar " << name << llendl;
486 continue;
487 }
488
489 if (mAvatars.count(avid) > 0)
490 {
491 // Avatar already in list, update position
492 F32 dist = (F32)(position - mypos).magVec();
493 mAvatars[avid].setPosition(position, (avatarp->getRegion() == gAgent.getRegion()), true, dist < 20.0, dist < 100.0);
494 }
495 else
496 {
497 // Avatar not there yet, add it
498 LLAvatarListEntry entry(avid, name, position);
499 mAvatars[avid] = entry;
500 }
501#ifdef LL_DISPLAY_NAMES
502 // update avatar display name.
503 mAvatars[avid].setDisplayName(display_name);
504#endif
505 }
506 else
507 {
508 if (i < positions.size())
509 {
510 position = positions[i];
511 }
512 else
513 {
514 continue;
515 }
516
517 if (gCacheName->getName(avid, first, last))
518 {
519 name = first + " " + last;
520 }
521 else
522 {
523 //name = gCacheName->getDefaultName();
524 continue; //prevent (Loading...)
525 }
526
527#ifdef LL_DISPLAY_NAMES
528 std::string display_name = name;
529 if (LLAvatarName::sOmitResidentAsLastName)
530 {
531 LLStringUtil::replaceString(display_name, " Resident", "");
532 }
533 if (LLAvatarNameCache::useDisplayNames())
534 {
535 LLAvatarName avatar_name;
536 if (LLAvatarNameCache::get(avid, &avatar_name))
537 {
538 if (LLAvatarNameCache::useDisplayNames() == 2)
539 {
540 display_name = avatar_name.mDisplayName;
541 }
542 else
543 {
544 display_name = avatar_name.getNames();
545 }
546 }
547 }
548#endif
549
550 if (mAvatars.count(avid) > 0)
551 {
552 // Avatar already in list, update position
553 F32 dist = (F32)(position - mypos).magVec();
554 mAvatars[avid].setPosition(position, gAgent.getRegion()->pointInRegionGlobal(position), false, dist < 20.0, dist < 100.0);
555 }
556 else
557 {
558 // Avatar not there yet, add it
559 LLAvatarListEntry entry(avid, name, position);
560 mAvatars[avid] = entry;
561 }
562#ifdef LL_DISPLAY_NAMES
563 // update avatar display name.
564 mAvatars[avid].setDisplayName(display_name);
565#endif
566 }
567 }
568 }
569
570// llinfos << "radar refresh: done" << llendl;
571
572 expireAvatarList();
573 refreshAvatarList();
574 refreshTracker();
575}
576
577void LLFloaterAvatarList::expireAvatarList()
578{
579// llinfos << "radar: expiring" << llendl;
580 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
581 std::queue<LLUUID> delete_queue;
582
583 for (iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
584 {
585 LLAvatarListEntry *entry = &iter->second;
586
587 if (!entry->getAlive() && entry->isDead())
588 {
589 //llinfos << "radar: expiring avatar " << entry->getDisplayName() << llendl;
590 LLUUID av_id = entry->getID();
591 delete_queue.push(av_id);
592 if (av_id == mTrackedAvatar)
593 {
594 stopTracker();
595 }
596 }
597 }
598
599 while (!delete_queue.empty())
600 {
601 mAvatars.erase(delete_queue.front());
602 delete_queue.pop();
603 }
604}
605
606/**
607 * Redraws the avatar list
608 * Only does anything if the avatar list is visible.
609 * @author Dale Glass
610 */
611void LLFloaterAvatarList::refreshAvatarList()
612{
613 // Don't update list when interface is hidden
614 if (!sInstance->getVisible()) return;
615
616 // We rebuild the list fully each time it's refreshed
617 // The assumption is that it's faster to refill it and sort than
618 // to rebuild the whole list.
619 LLDynamicArray<LLUUID> selected = mAvatarList->getSelectedIDs();
620 S32 scrollpos = mAvatarList->getScrollPos();
621
622 mAvatarList->deleteAllItems();
623
624 LLVector3d mypos = gAgent.getPositionGlobal();
625 LLVector3d posagent;
626 posagent.setVec(gAgent.getPositionAgent());
627 LLVector3d simpos = mypos - posagent;
628
629 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
630 for (iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
631 {
632 LLSD element;
633 LLUUID av_id;
634
635 LLAvatarListEntry *entry = &iter->second;
636
637 // Skip if avatar hasn't been around
638 if (entry->isDead())
639 {
640 continue;
641 }
642
643 av_id = entry->getID();
644
645 LLVector3d position = entry->getPosition();
646 BOOL UnknownAltitude = false;
647
648 LLVector3d delta = position - mypos;
649 F32 distance = (F32)delta.magVec();
650 if (position.mdV[VZ] == 0.0)
651 {
652 UnknownAltitude = true;
653 distance = 9000.0;
654 }
655 delta.mdV[2] = 0.0f;
656 F32 side_distance = (F32)delta.magVec();
657
658 // HACK: Workaround for an apparent bug:
659 // sometimes avatar entries get stuck, and are registered
660 // by the client as perpetually moving in the same direction.
661 // this makes sure they get removed from the visible list eventually
662
663 //jcool410 -- this fucks up seeing dueds thru minimap data > 1024m away, so, lets just say > 2048m to the side is bad
664 //aka 8 sims
665 if (side_distance > 2048.0f)
666 {
667 continue;
668 }
669
670 if (av_id.isNull())
671 {
672 //llwarns << "Avatar with null key somehow got into the list!" << llendl;
673 continue;
674 }
675
676 element["id"] = av_id;
677
678 element["columns"][LIST_MARK]["column"] = "marked";
679 element["columns"][LIST_MARK]["type"] = "text";
680 if (entry->isMarked())
681 {
682 element["columns"][LIST_MARK]["value"] = "X";
683 element["columns"][LIST_MARK]["color"] = LLColor4::blue.getValue();
684 element["columns"][LIST_MARK]["font-style"] = "BOLD";
685 }
686 else
687 {
688 element["columns"][LIST_MARK]["value"] = "";
689 }
690
691 element["columns"][LIST_AVATAR_NAME]["column"] = "avatar_name";
692 element["columns"][LIST_AVATAR_NAME]["type"] = "text";
693 element["columns"][LIST_AVATAR_NAME]["value"] = entry->getDisplayName().c_str();
694 if (entry->getEntryAgeSeconds() > 1.0f)
695 {
696 element["columns"][LIST_AVATAR_NAME]["font-style"] = "ITALIC";
697 }
698 else if (entry->isFocused())
699 {
700 element["columns"][LIST_AVATAR_NAME]["font-style"] = "BOLD";
701 }
702 if (LLMuteList::getInstance()->isMuted(av_id))
703 {
704 element["columns"][LIST_AVATAR_NAME]["color"] = LLColor4::red2.getValue();
705 }
706 else if (is_agent_friend(av_id))
707 {
708 element["columns"][LIST_AVATAR_NAME]["color"] = LLColor4::green3.getValue();
709 }
710
711 char temp[32];
712 LLColor4 color = LLColor4::black;
713 element["columns"][LIST_DISTANCE]["column"] = "distance";
714 element["columns"][LIST_DISTANCE]["type"] = "text";
715 if (UnknownAltitude)
716 {
717 strcpy(temp, "?");
718 if (entry->isDrawn())
719 {
720 color = LLColor4::green2;
721 }
722 }
723 else
724 {
725 if (distance < 100.0)
726 {
727 snprintf(temp, sizeof(temp), "%.1f", distance);
728 if (distance > 20.0f)
729 {
730 color = LLColor4::yellow1;
731 }
732 else
733 {
734 color = LLColor4::red;
735 }
736 }
737 else
738 {
739 if (entry->isDrawn())
740 {
741 color = LLColor4::green2;
742 }
743 snprintf(temp, sizeof(temp), "%d", (S32)distance);
744 }
745 }
746 element["columns"][LIST_DISTANCE]["value"] = temp;
747 element["columns"][LIST_DISTANCE]["color"] = color.getValue();
748
749 position = position - simpos;
750
751 S32 x = (S32)position.mdV[VX];
752 S32 y = (S32)position.mdV[VY];
753 if (x >= 0 && x <= 256 && y >= 0 && y <= 256)
754 {
755 snprintf(temp, sizeof(temp), "%d, %d", x, y);
756 }
757 else
758 {
759 temp[0] = '\0';
760 if (y < 0)
761 {
762 strcat(temp, "S");
763 }
764 else if (y > 256)
765 {
766 strcat(temp, "N");
767 }
768 if (x < 0)
769 {
770 strcat(temp, "W");
771 }
772 else if (x > 256)
773 {
774 strcat(temp, "E");
775 }
776 }
777 element["columns"][LIST_POSITION]["column"] = "position";
778 element["columns"][LIST_POSITION]["type"] = "text";
779 element["columns"][LIST_POSITION]["value"] = temp;
780
781 element["columns"][LIST_ALTITUDE]["column"] = "altitude";
782 element["columns"][LIST_ALTITUDE]["type"] = "text";
783 if (UnknownAltitude)
784 {
785 strcpy(temp, "?");
786 }
787 else
788 {
789 snprintf(temp, sizeof(temp), "%d", (S32)position.mdV[VZ]);
790 }
791 element["columns"][LIST_ALTITUDE]["value"] = temp;
792
793 // Add to list
794 mAvatarList->addElement(element, ADD_BOTTOM);
795 }
796
797 // finish
798 mAvatarList->sortItems();
799 mAvatarList->selectMultiple(selected);
800 mAvatarList->setScrollPos(scrollpos);
801
802// llinfos << "radar refresh: done" << llendl;
803
804}
805
806// static
807void LLFloaterAvatarList::onClickIM(void* userdata)
808{
809 //llinfos << "LLFloaterFriends::onClickIM()" << llendl;
810 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
811
812 LLDynamicArray<LLUUID> ids = self->mAvatarList->getSelectedIDs();
813 if (ids.size() > 0)
814 {
815 if (ids.size() == 1)
816 {
817 // Single avatar
818 LLUUID agent_id = ids[0];
819
820 char buffer[MAX_STRING];
821 snprintf(buffer, MAX_STRING, "%s", self->mAvatars[agent_id].getName().c_str());
822 gIMMgr->setFloaterOpen(TRUE);
823 gIMMgr->addSession(buffer, IM_NOTHING_SPECIAL, agent_id);
824 }
825 else
826 {
827 // Group IM
828 LLUUID session_id;
829 session_id.generate();
830 gIMMgr->setFloaterOpen(TRUE);
831 gIMMgr->addSession("Avatars Conference", IM_SESSION_CONFERENCE_START, ids[0], ids);
832 }
833 }
834}
835
836void LLFloaterAvatarList::onClickTeleportOffer(void *userdata)
837{
838 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
839
840 LLDynamicArray<LLUUID> ids = self->mAvatarList->getSelectedIDs();
841 if (ids.size() > 0)
842 {
843 handle_lure(ids);
844 }
845}
846
847void LLFloaterAvatarList::onClickTrack(void *userdata)
848{
849 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
850
851 LLScrollListItem *item = self->mAvatarList->getFirstSelected();
852 if (!item) return;
853
854 LLUUID agent_id = item->getUUID();
855
856 if (self->mTracking && self->mTrackedAvatar == agent_id) {
857 self->stopTracker();
858 }
859 else
860 {
861 self->mTracking = TRUE;
862 self->mTrackedAvatar = agent_id;
863// trackAvatar only works for friends allowing you to see them on map...
864// LLTracker::trackAvatar(agent_id, self->mAvatars[agent_id].getDisplayName());
865 std::string name = self->mAvatars[agent_id].getDisplayName();
866 if (!self->mUpdate)
867 {
868 name += "\n(last known position)";
869 }
870 LLTracker::trackLocation(self->mAvatars[agent_id].getPosition(), name, "");
871 }
872}
873
874void LLFloaterAvatarList::stopTracker()
875{
876 LLTracker::stopTracking(NULL);
877 mTracking = FALSE;
878}
879
880void LLFloaterAvatarList::refreshTracker()
881{
882 if (!mTracking) return;
883
884 if (LLTracker::isTracking(NULL))
885 {
886 LLVector3d pos;
887 if (mUpdate)
888 {
889 pos = mAvatars[mTrackedAvatar].getPosition();
890 }
891 else
892 {
893 LLViewerObject *obj = gObjectList.findObject(mTrackedAvatar);
894 if (!obj)
895 {
896 stopTracker();
897 return;
898 }
899 LLVOAvatar* avatarp = dynamic_cast<LLVOAvatar*>(obj);
900 if (!avatarp)
901 {
902 stopTracker();
903 return;
904 }
905 pos = gAgent.getPosGlobalFromAgent(avatarp->getCharacterPosition());
906 }
907 if (pos != LLTracker::getTrackedPositionGlobal())
908 {
909 std::string name = mAvatars[mTrackedAvatar].getDisplayName();
910 LLTracker::trackLocation(pos, name, "");
911 }
912 }
913 else
914 {
915 stopTracker();
916 }
917}
918
919LLAvatarListEntry * LLFloaterAvatarList::getAvatarEntry(LLUUID avatar)
920{
921 if (avatar.isNull())
922 {
923 return NULL;
924 }
925
926 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
927
928 iter = mAvatars.find(avatar);
929 if (iter == mAvatars.end())
930 {
931 return NULL;
932 }
933
934 return &iter->second;
935}
936
937//static
938void LLFloaterAvatarList::onClickMark(void *userdata)
939{
940 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
941 LLDynamicArray<LLUUID> ids = self->mAvatarList->getSelectedIDs();
942
943 for (LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
944 {
945 LLUUID avid = *itr;
946 LLAvatarListEntry *entry = self->getAvatarEntry(avid);
947 if (entry != NULL)
948 {
949 entry->toggleMark();
950 }
951 }
952}
953
954void LLFloaterAvatarList::onClickFocus(void *userdata)
955{
956 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
957
958 LLScrollListItem *item = self->mAvatarList->getFirstSelected();
959 if (item)
960 {
961 self->mFocusedAvatar = item->getUUID();
962 self->focusOnCurrent();
963 }
964}
965
966void LLFloaterAvatarList::removeFocusFromAll()
967{
968 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
969
970 for (iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
971 {
972 LLAvatarListEntry *entry = &iter->second;
973 entry->setFocus(FALSE);
974 }
975}
976
977void LLFloaterAvatarList::focusOnCurrent()
978{
979 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
980 LLAvatarListEntry *entry;
981
982 if (mAvatars.size() == 0)
983 {
984 return;
985 }
986
987 for (iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
988 {
989 entry = &iter->second;
990
991 if (entry->isDead())
992 continue;
993
994 if (entry->getID() == mFocusedAvatar)
995 {
996 removeFocusFromAll();
997 entry->setFocus(TRUE);
998 gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
999 return;
1000 }
1001 }
1002}
1003
1004void LLFloaterAvatarList::focusOnPrev(BOOL marked_only)
1005{
1006 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
1007 LLAvatarListEntry *prev = NULL;
1008 LLAvatarListEntry *entry;
1009
1010 if (mAvatars.size() == 0)
1011 {
1012 return;
1013 }
1014
1015 for (iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
1016 {
1017 entry = &iter->second;
1018
1019 if (entry->isDead())
1020 continue;
1021
1022 if (prev != NULL && entry->getID() == mFocusedAvatar)
1023 {
1024 break;
1025 }
1026
1027 if ((!marked_only && entry->isDrawn()) || entry->isMarked())
1028 {
1029 prev = entry;
1030 }
1031 }
1032
1033 if (prev != NULL)
1034 {
1035 removeFocusFromAll();
1036 prev->setFocus(TRUE);
1037 mFocusedAvatar = prev->getID();
1038 gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
1039 }
1040}
1041
1042void LLFloaterAvatarList::focusOnNext(BOOL marked_only)
1043{
1044 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
1045 BOOL found = FALSE;
1046 LLAvatarListEntry *next = NULL;
1047 LLAvatarListEntry *entry;
1048
1049 if (mAvatars.size() == 0)
1050 {
1051 return;
1052 }
1053
1054 for (iter = mAvatars.begin(); iter != mAvatars.end(); iter++)
1055 {
1056 entry = &iter->second;
1057
1058 if (entry->isDead())
1059 continue;
1060
1061 if (next == NULL && ((!marked_only && entry->isDrawn()) || entry->isMarked()))
1062 {
1063 next = entry;
1064 }
1065
1066 if (found && ((!marked_only && entry->isDrawn()) || entry->isMarked()))
1067 {
1068 next = entry;
1069 break;
1070 }
1071
1072 if (entry->getID() == mFocusedAvatar)
1073 {
1074 found = TRUE;
1075 }
1076 }
1077
1078 if (next != NULL)
1079 {
1080 removeFocusFromAll();
1081 next->setFocus(TRUE);
1082 mFocusedAvatar = next->getID();
1083 gAgent.lookAtObject(mFocusedAvatar, CAMERA_POSITION_OBJECT);
1084 }
1085}
1086
1087//static
1088void LLFloaterAvatarList::onClickPrevInList(void *userdata)
1089{
1090 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1091 self->focusOnPrev(FALSE);
1092}
1093
1094//static
1095void LLFloaterAvatarList::onClickNextInList(void *userdata)
1096{
1097 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1098 self->focusOnNext(FALSE);
1099}
1100
1101//static
1102void LLFloaterAvatarList::onClickPrevMarked(void *userdata)
1103{
1104 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1105 self->focusOnPrev(TRUE);
1106}
1107
1108//static
1109void LLFloaterAvatarList::onClickNextMarked(void *userdata)
1110{
1111 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1112 self->focusOnNext(TRUE);
1113}
1114
1115//static
1116void LLFloaterAvatarList::onClickGetKey(void *userdata)
1117{
1118 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1119 LLScrollListItem *item = self->mAvatarList->getFirstSelected();
1120
1121 if (NULL == item) return;
1122
1123 LLUUID agent_id = item->getUUID();
1124
1125 char buffer[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
1126 agent_id.toString(buffer);
1127
1128 gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer));
1129}
1130
1131void LLFloaterAvatarList::onClickSendKeys(void *userdata)
1132{
1133 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1134 std::map<LLUUID, LLAvatarListEntry>::iterator iter;
1135 LLAvatarListEntry *entry;
1136
1137 if (self->mAvatars.size() == 0)
1138 return;
1139
1140 for (iter = self->mAvatars.begin(); iter != self->mAvatars.end(); iter++)
1141 {
1142 entry = &iter->second;
1143
1144 if (!entry->isDead() && entry->isInSim())
1145 announce(entry->getID().asString());
1146 }
1147}
1148
1149static void send_freeze(const LLUUID& avatar_id, bool freeze)
1150{
1151 U32 flags = 0x0;
1152 if (!freeze)
1153 {
1154 // unfreeze
1155 flags |= 0x1;
1156 }
1157
1158 LLMessageSystem* msg = gMessageSystem;
1159 LLViewerObject* avatar = gObjectList.findObject(avatar_id);
1160
1161 if (avatar)
1162 {
1163 msg->newMessage("FreezeUser");
1164 msg->nextBlock("AgentData");
1165 msg->addUUID("AgentID", gAgent.getID());
1166 msg->addUUID("SessionID", gAgent.getSessionID());
1167 msg->nextBlock("Data");
1168 msg->addUUID("TargetID", avatar_id);
1169 msg->addU32("Flags", flags);
1170 msg->sendReliable( avatar->getRegion()->getHost());
1171 }
1172}
1173
1174static void send_eject(const LLUUID& avatar_id, bool ban)
1175{
1176 LLMessageSystem* msg = gMessageSystem;
1177 LLViewerObject* avatar = gObjectList.findObject(avatar_id);
1178
1179 if (avatar)
1180 {
1181 U32 flags = 0x0;
1182 if (ban)
1183 {
1184 // eject and add to ban list
1185 flags |= 0x1;
1186 }
1187
1188 msg->newMessage("EjectUser");
1189 msg->nextBlock("AgentData");
1190 msg->addUUID("AgentID", gAgent.getID());
1191 msg->addUUID("SessionID", gAgent.getSessionID());
1192 msg->nextBlock("Data");
1193 msg->addUUID("TargetID", avatar_id);
1194 msg->addU32("Flags", flags);
1195 msg->sendReliable( avatar->getRegion()->getHost());
1196 }
1197}
1198
1199static void send_estate_message(
1200 const char* request,
1201 const LLUUID &target)
1202{
1203
1204 LLMessageSystem* msg = gMessageSystem;
1205 LLUUID invoice;
1206
1207 // This seems to provide an ID so that the sim can say which request it's
1208 // replying to. I think this can be ignored for now.
1209 invoice.generate();
1210
1211 llinfos << "Sending estate request '" << request << "'" << llendl;
1212 msg->newMessage("EstateOwnerMessage");
1213 msg->nextBlockFast(_PREHASH_AgentData);
1214 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
1215 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
1216 msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); //not used
1217 msg->nextBlock("MethodData");
1218 msg->addString("Method", request);
1219 msg->addUUID("Invoice", invoice);
1220
1221 // Agent id
1222 msg->nextBlock("ParamList");
1223 msg->addString("Parameter", gAgent.getID().asString().c_str());
1224
1225 // Target
1226 msg->nextBlock("ParamList");
1227 msg->addString("Parameter", target.asString().c_str());
1228
1229 msg->sendReliable(gAgent.getRegion()->getHost());
1230}
1231
1232static void cmd_freeze(const LLUUID& avatar, const std::string &name) { send_freeze(avatar, true); }
1233static void cmd_unfreeze(const LLUUID& avatar, const std::string &name) { send_freeze(avatar, false); }
1234static void cmd_eject(const LLUUID& avatar, const std::string &name) { send_eject(avatar, false); }
1235static void cmd_ban(const LLUUID& avatar, const std::string &name) { send_eject(avatar, true); }
1236static void cmd_profile(const LLUUID& avatar, const std::string &name) { LLFloaterAvatarInfo::showFromDirectory(avatar); }
1237static void cmd_estate_eject(const LLUUID &avatar, const std::string &name){ send_estate_message("teleporthomeuser", avatar); }
1238
1239void LLFloaterAvatarList::doCommand(void (*func)(const LLUUID &avatar, const std::string &name))
1240{
1241 LLDynamicArray<LLUUID> ids = mAvatarList->getSelectedIDs();
1242
1243 for (LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
1244 {
1245 LLUUID avid = *itr;
1246 LLAvatarListEntry *entry = getAvatarEntry(avid);
1247 if (entry != NULL)
1248 {
1249 llinfos << "Executing command on " << entry->getDisplayName() << llendl;
1250 func(avid, entry->getName());
1251 }
1252 }
1253}
1254
1255std::string LLFloaterAvatarList::getSelectedNames(const std::string& separator)
1256{
1257 std::string ret = "";
1258
1259 LLDynamicArray<LLUUID> ids = mAvatarList->getSelectedIDs();
1260 for (LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
1261 {
1262 LLUUID avid = *itr;
1263 LLAvatarListEntry *entry = getAvatarEntry(avid);
1264 if (entry != NULL)
1265 {
1266 if (!ret.empty()) ret += separator;
1267 ret += entry->getName();
1268 }
1269 }
1270
1271 return ret;
1272}
1273
1274std::string LLFloaterAvatarList::getSelectedName()
1275{
1276 LLUUID id = getSelectedID();
1277 LLAvatarListEntry *entry = getAvatarEntry(id);
1278 if (entry)
1279 {
1280 return entry->getName();
1281 }
1282 return "";
1283}
1284
1285LLUUID LLFloaterAvatarList::getSelectedID()
1286{
1287 LLScrollListItem *item = mAvatarList->getFirstSelected();
1288 if (item) return item->getUUID();
1289 return LLUUID::null;
1290}
1291
1292//static
1293void LLFloaterAvatarList::callbackFreeze(const LLSD& notification, const LLSD& response)
1294{
1295 S32 option = LLNotification::getSelectedOption(notification, response);
1296
1297 LLFloaterAvatarList *self = LLFloaterAvatarList::sInstance;
1298
1299 if (option == 0)
1300 {
1301 self->doCommand(cmd_freeze);
1302 }
1303 else if (option == 1)
1304 {
1305 self->doCommand(cmd_unfreeze);
1306 }
1307}
1308
1309//static
1310void LLFloaterAvatarList::callbackEject(const LLSD& notification, const LLSD& response)
1311{
1312 S32 option = LLNotification::getSelectedOption(notification, response);
1313
1314 LLFloaterAvatarList *self = LLFloaterAvatarList::sInstance;
1315
1316 if (option == 0)
1317 {
1318 self->doCommand(cmd_eject);
1319 }
1320 else if (option == 1)
1321 {
1322 self->doCommand(cmd_ban);
1323 }
1324}
1325
1326//static
1327void LLFloaterAvatarList::callbackEjectFromEstate(const LLSD& notification, const LLSD& response)
1328{
1329 S32 option = LLNotification::getSelectedOption(notification, response);
1330
1331 LLFloaterAvatarList *self = LLFloaterAvatarList::sInstance;
1332
1333 if (option == 0)
1334 {
1335 self->doCommand(cmd_estate_eject);
1336 }
1337}
1338
1339//static
1340void LLFloaterAvatarList::callbackIdle(void *userdata) {
1341 if (LLFloaterAvatarList::sInstance != NULL)
1342 {
1343 // Do not update at every frame: this would be insane !
1344 if (gFrameCount % LLFloaterAvatarList::sInstance->mUpdateRate == 0)
1345 {
1346 LLFloaterAvatarList::sInstance->updateAvatarList();
1347 }
1348 }
1349}
1350
1351void LLFloaterAvatarList::onClickFreeze(void *userdata)
1352{
1353 LLSD args;
1354 LLSD payload;
1355 args["AVATAR_NAME"] = ((LLFloaterAvatarList*)userdata)->getSelectedNames();
1356 LLNotifications::instance().add("FreezeAvatarFullname", args, payload, callbackFreeze);
1357}
1358
1359//static
1360void LLFloaterAvatarList::onClickEject(void *userdata)
1361{
1362 LLSD args;
1363 LLSD payload;
1364 args["AVATAR_NAME"] = ((LLFloaterAvatarList*)userdata)->getSelectedNames();
1365 LLNotifications::instance().add("EjectAvatarFullname", args, payload, callbackEject);
1366}
1367
1368//static
1369void LLFloaterAvatarList::onClickMute(void *userdata)
1370{
1371 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1372
1373 LLDynamicArray<LLUUID> ids = self->mAvatarList->getSelectedIDs();
1374 if (ids.size() > 0)
1375 {
1376 for (LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
1377 {
1378 LLUUID agent_id = *itr;
1379
1380 std::string agent_name;
1381 if (gCacheName->getFullName(agent_id, agent_name))
1382 {
1383 if (LLMuteList::getInstance()->isMuted(agent_id))
1384 {
1385 LLMute mute(agent_id, agent_name, LLMute::AGENT);
1386 LLMuteList::getInstance()->remove(mute);
1387 }
1388 else
1389 {
1390 LLMute mute(agent_id, agent_name, LLMute::AGENT);
1391 LLMuteList::getInstance()->add(mute);
1392 }
1393 }
1394 }
1395 }
1396}
1397
1398//static
1399void LLFloaterAvatarList::onClickEjectFromEstate(void *userdata)
1400{
1401 LLSD args;
1402 LLSD payload;
1403 args["EVIL_USER"] = ((LLFloaterAvatarList*)userdata)->getSelectedNames();
1404 LLNotifications::instance().add("EstateKickUser", args, payload, callbackEjectFromEstate);
1405}
1406
1407//static
1408void LLFloaterAvatarList::onClickAR(void *userdata)
1409{
1410 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1411 LLScrollListItem *item = self->mAvatarList->getFirstSelected();
1412 if (item)
1413 {
1414 LLUUID agent_id = item->getUUID();
1415 LLAvatarListEntry *entry = self->getAvatarEntry(agent_id);
1416 if (entry)
1417 {
1418 LLFloaterReporter::showFromObject(entry->getID());
1419 }
1420 }
1421}
1422
1423// static
1424void LLFloaterAvatarList::onClickProfile(void* userdata)
1425{
1426 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1427 self->doCommand(cmd_profile);
1428}
1429
1430//static
1431void LLFloaterAvatarList::onClickTeleport(void* userdata)
1432{
1433 LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata;
1434 LLScrollListItem *item = self->mAvatarList->getFirstSelected();
1435 if (item)
1436 {
1437 LLUUID agent_id = item->getUUID();
1438 LLAvatarListEntry *entry = self->getAvatarEntry(agent_id);
1439 if (entry)
1440 {
1441// llinfos << "Trying to teleport to " << entry->getDisplayName() << " at " << entry->getPosition() << llendl;
1442 gAgent.teleportViaLocation(entry->getPosition());
1443 }
1444 }
1445}
1446
1447void LLFloaterAvatarList::onSelectName(LLUICtrl*, void* userdata)
1448{
1449 LLFloaterAvatarList* self = (LLFloaterAvatarList*)userdata;
1450
1451 LLScrollListItem *item = self->mAvatarList->getFirstSelected();
1452 if (item)
1453 {
1454 LLUUID agent_id = item->getUUID();
1455 LLAvatarListEntry *entry = self->getAvatarEntry(agent_id);
1456 if (entry)
1457 {
1458 BOOL enabled = entry->isDrawn();
1459 self->childSetEnabled("focus_btn", enabled);
1460 self->childSetEnabled("prev_in_list_btn", enabled);
1461 self->childSetEnabled("next_in_list_btn", enabled);
1462 }
1463 }
1464}
1465
1466void LLFloaterAvatarList::onCommitUpdateRate(LLUICtrl*, void* userdata)
1467{
1468 LLFloaterAvatarList* self = (LLFloaterAvatarList*)userdata;
1469
1470 self->mUpdateRate = gSavedSettings.getU32("RadarUpdateRate") * 3 + 3;
1471}