diff options
Diffstat (limited to 'linden')
-rw-r--r-- | linden/indra/newview/CMakeLists.txt | 2 | ||||
-rw-r--r-- | linden/indra/newview/app_settings/settings.xml | 131 | ||||
-rw-r--r-- | linden/indra/newview/llagent.cpp | 110 | ||||
-rw-r--r-- | linden/indra/newview/llagent.h | 7 | ||||
-rw-r--r-- | linden/indra/newview/llfloateravatarlist.cpp | 1471 | ||||
-rw-r--r-- | linden/indra/newview/llfloateravatarlist.h | 316 | ||||
-rw-r--r-- | linden/indra/newview/llstartup.cpp | 6 | ||||
-rw-r--r-- | linden/indra/newview/llviewermenu.cpp | 9 | ||||
-rw-r--r-- | linden/indra/newview/skins/default/xui/en-us/floater_radar.xml | 327 | ||||
-rw-r--r-- | linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml | 4 |
10 files changed, 2382 insertions, 1 deletions
diff --git a/linden/indra/newview/CMakeLists.txt b/linden/indra/newview/CMakeLists.txt index 9a4e2ed..df6d8b5 100644 --- a/linden/indra/newview/CMakeLists.txt +++ b/linden/indra/newview/CMakeLists.txt | |||
@@ -155,6 +155,7 @@ set(viewer_SOURCE_FILES | |||
155 | llfloaterassetbrowser.cpp | 155 | llfloaterassetbrowser.cpp |
156 | llfloaterauction.cpp | 156 | llfloaterauction.cpp |
157 | llfloateravatarinfo.cpp | 157 | llfloateravatarinfo.cpp |
158 | llfloateravatarlist.cpp | ||
158 | llfloateravatarpicker.cpp | 159 | llfloateravatarpicker.cpp |
159 | llfloateravatartextures.cpp | 160 | llfloateravatartextures.cpp |
160 | llfloaterbeacons.cpp | 161 | llfloaterbeacons.cpp |
@@ -617,6 +618,7 @@ set(viewer_HEADER_FILES | |||
617 | llfloaterassetbrowser.h | 618 | llfloaterassetbrowser.h |
618 | llfloaterauction.h | 619 | llfloaterauction.h |
619 | llfloateravatarinfo.h | 620 | llfloateravatarinfo.h |
621 | llfloateravatarlist.h | ||
620 | llfloateravatarpicker.h | 622 | llfloateravatarpicker.h |
621 | llfloateravatartextures.h | 623 | llfloateravatartextures.h |
622 | llfloaterbeacons.h | 624 | llfloaterbeacons.h |
diff --git a/linden/indra/newview/app_settings/settings.xml b/linden/indra/newview/app_settings/settings.xml index 3523c7f..c8ad381 100644 --- a/linden/indra/newview/app_settings/settings.xml +++ b/linden/indra/newview/app_settings/settings.xml | |||
@@ -2359,6 +2359,137 @@ | |||
2359 | 2359 | ||
2360 | <!-- End: socks5 --> | 2360 | <!-- End: socks5 --> |
2361 | 2361 | ||
2362 | <!-- Begin: full radar (Advanced menu) --> | ||
2363 | |||
2364 | <key>ShowRadar</key> | ||
2365 | <map> | ||
2366 | <key>Comment</key> | ||
2367 | <string>Show the radar floater</string> | ||
2368 | <key>Persist</key> | ||
2369 | <integer>1</integer> | ||
2370 | <key>Type</key> | ||
2371 | <string>Boolean</string> | ||
2372 | <key>Value</key> | ||
2373 | <integer>0</integer> | ||
2374 | </map> | ||
2375 | <key>FloaterRadarRect</key> | ||
2376 | <map> | ||
2377 | <key>Comment</key> | ||
2378 | <string>Rectangle for Radar</string> | ||
2379 | <key>Persist</key> | ||
2380 | <integer>1</integer> | ||
2381 | <key>Type</key> | ||
2382 | <string>Rect</string> | ||
2383 | <key>Value</key> | ||
2384 | <array> | ||
2385 | <integer>0</integer> | ||
2386 | <integer>400</integer> | ||
2387 | <integer>200</integer> | ||
2388 | <integer>0</integer> | ||
2389 | </array> | ||
2390 | </map> | ||
2391 | <key>RadarKeepOpen</key> | ||
2392 | <map> | ||
2393 | <key>Comment</key> | ||
2394 | <string>Keeps radar updates running in background</string> | ||
2395 | <key>Persist</key> | ||
2396 | <integer>1</integer> | ||
2397 | <key>Type</key> | ||
2398 | <string>Boolean</string> | ||
2399 | <key>Value</key> | ||
2400 | <integer>0</integer> | ||
2401 | </map> | ||
2402 | <key>RadarUpdateRate</key> | ||
2403 | <map> | ||
2404 | <key>Comment</key> | ||
2405 | <string>Radar update rate (0 = high, 1 = medium, 2 = low)</string> | ||
2406 | <key>Persist</key> | ||
2407 | <integer>1</integer> | ||
2408 | <key>Type</key> | ||
2409 | <string>U32</string> | ||
2410 | <key>Value</key> | ||
2411 | <integer>1</integer> | ||
2412 | </map> | ||
2413 | <key>RadarAlertSim</key> | ||
2414 | <map> | ||
2415 | <key>Comment</key> | ||
2416 | <string>Whether the radar emits chat alerts for avatars entering/exiting sim</string> | ||
2417 | <key>Persist</key> | ||
2418 | <integer>1</integer> | ||
2419 | <key>Type</key> | ||
2420 | <string>Boolean</string> | ||
2421 | <key>Value</key> | ||
2422 | <integer>0</integer> | ||
2423 | </map> | ||
2424 | <key>RadarAlertDraw</key> | ||
2425 | <map> | ||
2426 | <key>Comment</key> | ||
2427 | <string>Whether the radar emits chat alerts for avatars entering/exiting draw distance</string> | ||
2428 | <key>Persist</key> | ||
2429 | <integer>1</integer> | ||
2430 | <key>Type</key> | ||
2431 | <string>Boolean</string> | ||
2432 | <key>Value</key> | ||
2433 | <integer>0</integer> | ||
2434 | </map> | ||
2435 | <key>RadarAlertShoutRange</key> | ||
2436 | <map> | ||
2437 | <key>Comment</key> | ||
2438 | <string>Whether the radar emits chat alerts for avatars entering/exiting shout range</string> | ||
2439 | <key>Persist</key> | ||
2440 | <integer>1</integer> | ||
2441 | <key>Type</key> | ||
2442 | <string>Boolean</string> | ||
2443 | <key>Value</key> | ||
2444 | <integer>0</integer> | ||
2445 | </map> | ||
2446 | <key>RadarAlertChatRange</key> | ||
2447 | <map> | ||
2448 | <key>Comment</key> | ||
2449 | <string>Whether the radar emits chat alerts for avatars entering/exiting chat range</string> | ||
2450 | <key>Persist</key> | ||
2451 | <integer>1</integer> | ||
2452 | <key>Type</key> | ||
2453 | <string>Boolean</string> | ||
2454 | <key>Value</key> | ||
2455 | <integer>1</integer> | ||
2456 | </map> | ||
2457 | <key>RadarChatAlerts</key> | ||
2458 | <map> | ||
2459 | <key>Comment</key> | ||
2460 | <string>Whether the radar emits chat alerts regarding the status of avatars it displays</string> | ||
2461 | <key>Persist</key> | ||
2462 | <integer>1</integer> | ||
2463 | <key>Type</key> | ||
2464 | <string>Boolean</string> | ||
2465 | <key>Value</key> | ||
2466 | <integer>0</integer> | ||
2467 | </map> | ||
2468 | <key>RadarChatKeys</key> | ||
2469 | <map> | ||
2470 | <key>Comment</key> | ||
2471 | <string>Enable private chat alerts for avatars entering the region</string> | ||
2472 | <key>Persist</key> | ||
2473 | <integer>1</integer> | ||
2474 | <key>Type</key> | ||
2475 | <string>Boolean</string> | ||
2476 | <key>Value</key> | ||
2477 | <integer>0</integer> | ||
2478 | </map> | ||
2479 | <key>RadarChatKeysChannel</key> | ||
2480 | <map> | ||
2481 | <key>Comment</key> | ||
2482 | <string>Private channel used to broadcast the key of incoming avatars</string> | ||
2483 | <key>Persist</key> | ||
2484 | <integer>1</integer> | ||
2485 | <key>Type</key> | ||
2486 | <string>S32</string> | ||
2487 | <key>Value</key> | ||
2488 | <integer>-8888888</integer> | ||
2489 | </map> | ||
2490 | |||
2491 | <!-- End: full radar (Advanced menu) --> | ||
2492 | |||
2362 | <!-- END IMPRUDENCE-SPECIFIC SETTINGS --> | 2493 | <!-- END IMPRUDENCE-SPECIFIC SETTINGS --> |
2363 | 2494 | ||
2364 | <key>AFKTimeout</key> | 2495 | <key>AFKTimeout</key> |
diff --git a/linden/indra/newview/llagent.cpp b/linden/indra/newview/llagent.cpp index 871c90d..88ec2ca 100644 --- a/linden/indra/newview/llagent.cpp +++ b/linden/indra/newview/llagent.cpp | |||
@@ -4772,6 +4772,116 @@ void LLAgent::lookAtLastChat() | |||
4772 | } | 4772 | } |
4773 | } | 4773 | } |
4774 | 4774 | ||
4775 | void LLAgent::lookAtObject(LLUUID object_id, ECameraPosition camera_pos) | ||
4776 | { | ||
4777 | // Block if camera is animating or not in normal third person camera mode | ||
4778 | if (mCameraAnimating || !cameraThirdPerson()) | ||
4779 | { | ||
4780 | return; | ||
4781 | } | ||
4782 | |||
4783 | LLViewerObject *chatter = gObjectList.findObject(object_id); | ||
4784 | if (chatter) | ||
4785 | { | ||
4786 | LLVector3 delta_pos; | ||
4787 | if (chatter->isAvatar()) | ||
4788 | { | ||
4789 | LLVOAvatar *chatter_av = (LLVOAvatar*)chatter; | ||
4790 | if (!mAvatarObject.isNull() && chatter_av->mHeadp) | ||
4791 | { | ||
4792 | delta_pos = chatter_av->mHeadp->getWorldPosition() - mAvatarObject->mHeadp->getWorldPosition(); | ||
4793 | } | ||
4794 | else | ||
4795 | { | ||
4796 | delta_pos = chatter->getPositionAgent() - getPositionAgent(); | ||
4797 | } | ||
4798 | delta_pos.normVec(); | ||
4799 | |||
4800 | setControlFlags(AGENT_CONTROL_STOP); | ||
4801 | |||
4802 | changeCameraToThirdPerson(); | ||
4803 | |||
4804 | LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition(); | ||
4805 | LLVector3 left = delta_pos % LLVector3::z_axis; | ||
4806 | left.normVec(); | ||
4807 | LLVector3 up = left % delta_pos; | ||
4808 | up.normVec(); | ||
4809 | new_camera_pos -= delta_pos * 0.4f; | ||
4810 | new_camera_pos += left * 0.3f; | ||
4811 | new_camera_pos += up * 0.2f; | ||
4812 | |||
4813 | F32 radius = chatter_av->getVObjRadius(); | ||
4814 | LLVector3d view_dist(radius, radius, 0.0f); | ||
4815 | |||
4816 | if (chatter_av->mHeadp) | ||
4817 | { | ||
4818 | setFocusGlobal(getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), object_id); | ||
4819 | mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()); | ||
4820 | |||
4821 | switch(camera_pos) | ||
4822 | { | ||
4823 | case CAMERA_POSITION_SELF: | ||
4824 | mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()); | ||
4825 | break; | ||
4826 | case CAMERA_POSITION_OBJECT: | ||
4827 | mCameraFocusOffsetTarget = view_dist; | ||
4828 | break; | ||
4829 | } | ||
4830 | } | ||
4831 | else | ||
4832 | { | ||
4833 | setFocusGlobal(chatter->getPositionGlobal(), object_id); | ||
4834 | mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal(); | ||
4835 | |||
4836 | switch(camera_pos) | ||
4837 | { | ||
4838 | case CAMERA_POSITION_SELF: | ||
4839 | mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal(); | ||
4840 | break; | ||
4841 | case CAMERA_POSITION_OBJECT: | ||
4842 | mCameraFocusOffsetTarget = view_dist; | ||
4843 | break; | ||
4844 | } | ||
4845 | } | ||
4846 | setFocusOnAvatar(FALSE, TRUE); | ||
4847 | } | ||
4848 | else | ||
4849 | { | ||
4850 | delta_pos = chatter->getRenderPosition() - getPositionAgent(); | ||
4851 | delta_pos.normVec(); | ||
4852 | |||
4853 | setControlFlags(AGENT_CONTROL_STOP); | ||
4854 | |||
4855 | changeCameraToThirdPerson(); | ||
4856 | |||
4857 | LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition(); | ||
4858 | LLVector3 left = delta_pos % LLVector3::z_axis; | ||
4859 | left.normVec(); | ||
4860 | LLVector3 up = left % delta_pos; | ||
4861 | up.normVec(); | ||
4862 | new_camera_pos -= delta_pos * 0.4f; | ||
4863 | new_camera_pos += left * 0.3f; | ||
4864 | new_camera_pos += up * 0.2f; | ||
4865 | |||
4866 | setFocusGlobal(chatter->getPositionGlobal(), object_id); | ||
4867 | |||
4868 | switch(camera_pos) | ||
4869 | { | ||
4870 | case CAMERA_POSITION_SELF: | ||
4871 | mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal(); | ||
4872 | break; | ||
4873 | case CAMERA_POSITION_OBJECT: | ||
4874 | F32 radius = chatter->getVObjRadius(); | ||
4875 | LLVector3d view_dist(radius, radius, 0.0f); | ||
4876 | mCameraFocusOffsetTarget = view_dist; | ||
4877 | break; | ||
4878 | } | ||
4879 | |||
4880 | setFocusOnAvatar(FALSE, TRUE); | ||
4881 | } | ||
4882 | } | ||
4883 | } | ||
4884 | |||
4775 | const F32 SIT_POINT_EXTENTS = 0.2f; | 4885 | const F32 SIT_POINT_EXTENTS = 0.2f; |
4776 | 4886 | ||
4777 | void LLAgent::setStartPosition( U32 location_id ) | 4887 | void LLAgent::setStartPosition( U32 location_id ) |
diff --git a/linden/indra/newview/llagent.h b/linden/indra/newview/llagent.h index cea55fb..83490d3 100644 --- a/linden/indra/newview/llagent.h +++ b/linden/indra/newview/llagent.h | |||
@@ -82,6 +82,12 @@ typedef enum e_camera_modes | |||
82 | CAMERA_MODE_FOLLOW | 82 | CAMERA_MODE_FOLLOW |
83 | } ECameraMode; | 83 | } ECameraMode; |
84 | 84 | ||
85 | typedef enum e_camera_position | ||
86 | { | ||
87 | CAMERA_POSITION_SELF, /** Camera positioned at our position */ | ||
88 | CAMERA_POSITION_OBJECT /** Camera positioned at observed object's position */ | ||
89 | } ECameraPosition; | ||
90 | |||
85 | typedef enum e_anim_request | 91 | typedef enum e_anim_request |
86 | { | 92 | { |
87 | ANIM_REQUEST_START, | 93 | ANIM_REQUEST_START, |
@@ -214,6 +220,7 @@ public: | |||
214 | 220 | ||
215 | void heardChat(const LLUUID& id); | 221 | void heardChat(const LLUUID& id); |
216 | void lookAtLastChat(); | 222 | void lookAtLastChat(); |
223 | void lookAtObject(LLUUID avatar_id, ECameraPosition camera_pos); | ||
217 | F32 getTypingTime() { return mTypingTimer.getElapsedTimeF32(); } | 224 | F32 getTypingTime() { return mTypingTimer.getElapsedTimeF32(); } |
218 | 225 | ||
219 | void setAFK(); | 226 | void setAFK(); |
diff --git a/linden/indra/newview/llfloateravatarlist.cpp b/linden/indra/newview/llfloateravatarlist.cpp new file mode 100644 index 0000000..59cd278 --- /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 | */ | ||
61 | const F32 DEAD_KEEP_TIME = 10.0f; | ||
62 | |||
63 | extern U32 gFrameCount; | ||
64 | |||
65 | typedef 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 | |||
73 | void 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 | |||
88 | void 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("RadarAlertSim")) | ||
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("RadarAlertChatRange")) | ||
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 | |||
132 | LLAvatarListEntry::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 | |||
139 | void 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 | |||
189 | bool 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 | |||
215 | F32 LLAvatarListEntry::getEntryAgeSeconds() | ||
216 | { | ||
217 | return mUpdateTimer.getElapsedTimeF32(); | ||
218 | } | ||
219 | |||
220 | BOOL LLAvatarListEntry::isDead() | ||
221 | { | ||
222 | return getEntryAgeSeconds() > DEAD_KEEP_TIME; | ||
223 | } | ||
224 | |||
225 | LLFloaterAvatarList* LLFloaterAvatarList::sInstance = NULL; | ||
226 | |||
227 | LLFloaterAvatarList::LLFloaterAvatarList() : LLFloater(std::string("radar")) | ||
228 | { | ||
229 | llassert_always(sInstance == NULL); | ||
230 | sInstance = this; | ||
231 | mUpdateRate = gSavedSettings.getU32("RadarUpdateRate") * 3 + 3; | ||
232 | } | ||
233 | |||
234 | LLFloaterAvatarList::~LLFloaterAvatarList() | ||
235 | { | ||
236 | gIdleCallbacks.deleteFunction(LLFloaterAvatarList::callbackIdle); | ||
237 | sInstance = NULL; | ||
238 | } | ||
239 | |||
240 | //static | ||
241 | void 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 | ||
270 | void 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 | |||
292 | void LLFloaterAvatarList::draw() | ||
293 | { | ||
294 | LLFloater::draw(); | ||
295 | } | ||
296 | |||
297 | void LLFloaterAvatarList::onOpen() | ||
298 | { | ||
299 | gSavedSettings.setBOOL("ShowRadar", TRUE); | ||
300 | sInstance->setVisible(TRUE); | ||
301 | } | ||
302 | |||
303 | void 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 | |||
316 | BOOL 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 | |||
364 | void 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 | |||
577 | void 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 | */ | ||
611 | void 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 | ||
807 | void 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 | |||
836 | void 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 | |||
847 | void 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 | |||
874 | void LLFloaterAvatarList::stopTracker() | ||
875 | { | ||
876 | LLTracker::stopTracking(NULL); | ||
877 | mTracking = FALSE; | ||
878 | } | ||
879 | |||
880 | void 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 | |||
919 | LLAvatarListEntry * 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 | ||
938 | void 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 | |||
954 | void 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 | |||
966 | void 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 | |||
977 | void 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 | |||
1004 | void 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 | |||
1042 | void 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 | ||
1088 | void LLFloaterAvatarList::onClickPrevInList(void *userdata) | ||
1089 | { | ||
1090 | LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata; | ||
1091 | self->focusOnPrev(FALSE); | ||
1092 | } | ||
1093 | |||
1094 | //static | ||
1095 | void LLFloaterAvatarList::onClickNextInList(void *userdata) | ||
1096 | { | ||
1097 | LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata; | ||
1098 | self->focusOnNext(FALSE); | ||
1099 | } | ||
1100 | |||
1101 | //static | ||
1102 | void LLFloaterAvatarList::onClickPrevMarked(void *userdata) | ||
1103 | { | ||
1104 | LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata; | ||
1105 | self->focusOnPrev(TRUE); | ||
1106 | } | ||
1107 | |||
1108 | //static | ||
1109 | void LLFloaterAvatarList::onClickNextMarked(void *userdata) | ||
1110 | { | ||
1111 | LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata; | ||
1112 | self->focusOnNext(TRUE); | ||
1113 | } | ||
1114 | |||
1115 | //static | ||
1116 | void 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 | |||
1131 | void 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 | |||
1149 | static 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 | |||
1174 | static 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 | |||
1199 | static 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 | |||
1232 | static void cmd_freeze(const LLUUID& avatar, const std::string &name) { send_freeze(avatar, true); } | ||
1233 | static void cmd_unfreeze(const LLUUID& avatar, const std::string &name) { send_freeze(avatar, false); } | ||
1234 | static void cmd_eject(const LLUUID& avatar, const std::string &name) { send_eject(avatar, false); } | ||
1235 | static void cmd_ban(const LLUUID& avatar, const std::string &name) { send_eject(avatar, true); } | ||
1236 | static void cmd_profile(const LLUUID& avatar, const std::string &name) { LLFloaterAvatarInfo::showFromDirectory(avatar); } | ||
1237 | static void cmd_estate_eject(const LLUUID &avatar, const std::string &name){ send_estate_message("teleporthomeuser", avatar); } | ||
1238 | |||
1239 | void 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 | |||
1255 | std::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 | |||
1274 | std::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 | |||
1285 | LLUUID LLFloaterAvatarList::getSelectedID() | ||
1286 | { | ||
1287 | LLScrollListItem *item = mAvatarList->getFirstSelected(); | ||
1288 | if (item) return item->getUUID(); | ||
1289 | return LLUUID::null; | ||
1290 | } | ||
1291 | |||
1292 | //static | ||
1293 | void 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 | ||
1310 | void 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 | ||
1327 | void 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 | ||
1340 | void 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 | |||
1351 | void 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 | ||
1360 | void 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 | ||
1369 | void 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 | ||
1399 | void 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 | ||
1408 | void 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 | ||
1424 | void LLFloaterAvatarList::onClickProfile(void* userdata) | ||
1425 | { | ||
1426 | LLFloaterAvatarList *self = (LLFloaterAvatarList*)userdata; | ||
1427 | self->doCommand(cmd_profile); | ||
1428 | } | ||
1429 | |||
1430 | //static | ||
1431 | void 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 | |||
1447 | void 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 | |||
1466 | void LLFloaterAvatarList::onCommitUpdateRate(LLUICtrl*, void* userdata) | ||
1467 | { | ||
1468 | LLFloaterAvatarList* self = (LLFloaterAvatarList*)userdata; | ||
1469 | |||
1470 | self->mUpdateRate = gSavedSettings.getU32("RadarUpdateRate") * 3 + 3; | ||
1471 | } | ||
diff --git a/linden/indra/newview/llfloateravatarlist.h b/linden/indra/newview/llfloateravatarlist.h new file mode 100644 index 0000000..75a95a7 --- /dev/null +++ b/linden/indra/newview/llfloateravatarlist.h | |||
@@ -0,0 +1,316 @@ | |||
1 | // | ||
2 | // C++ Interface: llfloateravatarlist | ||
3 | // | ||
4 | // Description: | ||
5 | // | ||
6 | // | ||
7 | // Original author: Dale Glass <dale@daleglass.net>, (C) 2007 | ||
8 | // Heavily modified by Henri Beauchamp 10/2009. | ||
9 | // | ||
10 | // Copyright: See COPYING file that comes with this distribution | ||
11 | // | ||
12 | // | ||
13 | #include "llfloater.h" | ||
14 | #include "llfloaterreporter.h" | ||
15 | #include "lluuid.h" | ||
16 | #include "lltimer.h" | ||
17 | #include "llscrolllistctrl.h" | ||
18 | |||
19 | #include <time.h> | ||
20 | #include <map> | ||
21 | #include <set> | ||
22 | |||
23 | class LLFloaterAvatarList; | ||
24 | |||
25 | /** | ||
26 | * @brief This class is used to hold data about avatars. | ||
27 | * We cache data about avatars to avoid repeating requests in this class. | ||
28 | * Instances are kept in a map<LLAvatarListEntry>. We keep track of the | ||
29 | * frame where the avatar was last seen. | ||
30 | */ | ||
31 | class LLAvatarListEntry { | ||
32 | |||
33 | public: | ||
34 | |||
35 | /** | ||
36 | * @brief Initializes a list entry | ||
37 | * @param id Avatar's key | ||
38 | * @param name Avatar's name | ||
39 | * @param position Avatar's current position | ||
40 | */ | ||
41 | LLAvatarListEntry(const LLUUID& id = LLUUID::null, const std::string &name = "", const LLVector3d &position = LLVector3d::zero); | ||
42 | |||
43 | /** | ||
44 | * Update world position. | ||
45 | * Affects age. | ||
46 | */ | ||
47 | void setPosition(LLVector3d position, bool this_sim, bool drawn, bool chatrange, bool shoutrange); | ||
48 | |||
49 | LLVector3d getPosition() { return mPosition; } | ||
50 | |||
51 | /** | ||
52 | * @brief Returns the age of this entry in frames | ||
53 | * | ||
54 | * This is only used for determining whether the avatar is still around. | ||
55 | * @see getEntryAgeSeconds | ||
56 | */ | ||
57 | bool getAlive(); | ||
58 | |||
59 | /** | ||
60 | * @brief Returns the age of this entry in seconds | ||
61 | */ | ||
62 | F32 getEntryAgeSeconds(); | ||
63 | |||
64 | /** | ||
65 | * @brief Returns the name of the avatar | ||
66 | */ | ||
67 | std::string getName() { return mName; } | ||
68 | |||
69 | /** | ||
70 | * @brief Sets the display name of the avatar | ||
71 | */ | ||
72 | void setDisplayName(std::string name) { mDisplayName = name; } | ||
73 | |||
74 | /** | ||
75 | * @brief Returns the display name of the avatar | ||
76 | */ | ||
77 | std::string getDisplayName() { return mDisplayName; } | ||
78 | |||
79 | /** | ||
80 | * @brief Returns the ID of the avatar | ||
81 | */ | ||
82 | LLUUID getID() { return mID; } | ||
83 | |||
84 | /** | ||
85 | * @brief Sets the 'focus' status on this entry (camera focused on this avatar) | ||
86 | */ | ||
87 | void setFocus(BOOL value) { mFocused = value; } | ||
88 | |||
89 | BOOL isFocused() { return mFocused; } | ||
90 | |||
91 | BOOL isMarked() { return mMarked; } | ||
92 | |||
93 | BOOL isDrawn() { return (mInDrawFrame != U32_MAX); } | ||
94 | |||
95 | BOOL isInSim() { return (mInSimFrame != U32_MAX); } | ||
96 | |||
97 | /** | ||
98 | * @brief Returns whether the item is dead and shouldn't appear in the list | ||
99 | * @returns TRUE if dead | ||
100 | */ | ||
101 | BOOL isDead(); | ||
102 | |||
103 | void toggleMark() { mMarked = !mMarked; } | ||
104 | |||
105 | private: | ||
106 | friend class LLFloaterAvatarList; | ||
107 | |||
108 | LLUUID mID; | ||
109 | std::string mName; | ||
110 | std::string mDisplayName; | ||
111 | LLVector3d mPosition; | ||
112 | LLVector3d mDrawPosition; | ||
113 | BOOL mMarked; | ||
114 | BOOL mFocused; | ||
115 | |||
116 | /** | ||
117 | * @brief Timer to keep track of whether avatars are still there | ||
118 | */ | ||
119 | LLTimer mUpdateTimer; | ||
120 | |||
121 | /** | ||
122 | * @brief Last frame when this avatar was updated | ||
123 | */ | ||
124 | U32 mFrame; | ||
125 | //last frame when this avatar was in sim | ||
126 | U32 mInSimFrame; | ||
127 | //last frame when this avatar was in draw | ||
128 | U32 mInDrawFrame; | ||
129 | //last frame when this avatar was in shout range | ||
130 | U32 mInShoutFrame; | ||
131 | //last frame when this avatar was in chat range | ||
132 | U32 mInChatFrame; | ||
133 | }; | ||
134 | |||
135 | |||
136 | /** | ||
137 | * @brief Avatar List | ||
138 | * Implements an avatar scanner in the client. | ||
139 | * | ||
140 | * This is my first attempt to modify the SL source. This code is intended | ||
141 | * to have a dual purpose: doing the task, and providing an example of how | ||
142 | * to do it. For that reason, it's going to be commented as exhaustively | ||
143 | * as possible. | ||
144 | * | ||
145 | * Since I'm very new to C++ any suggestions on coding, style, etc are very | ||
146 | * welcome. | ||
147 | */ | ||
148 | class LLFloaterAvatarList : public LLFloater | ||
149 | { | ||
150 | /** | ||
151 | * @brief Creates and initializes the LLFloaterAvatarList | ||
152 | * Here the interface is created, and callbacks are initialized. | ||
153 | */ | ||
154 | private: | ||
155 | LLFloaterAvatarList(); | ||
156 | public: | ||
157 | ~LLFloaterAvatarList(); | ||
158 | |||
159 | /*virtual*/ void onClose(bool app_quitting); | ||
160 | /*virtual*/ void onOpen(); | ||
161 | /*virtual*/ BOOL postBuild(); | ||
162 | /*virtual*/ void draw(); | ||
163 | |||
164 | /** | ||
165 | * @brief Toggles interface visibility | ||
166 | * There is only one instance of the avatar scanner at any time. | ||
167 | */ | ||
168 | static void toggle(void*); | ||
169 | |||
170 | static void showInstance(); | ||
171 | |||
172 | /** | ||
173 | * @brief Updates the internal avatar list with the currently present avatars. | ||
174 | */ | ||
175 | void updateAvatarList(); | ||
176 | |||
177 | /** | ||
178 | * @brief Refresh avatar list (display) | ||
179 | */ | ||
180 | void refreshAvatarList(); | ||
181 | |||
182 | /** | ||
183 | * @brief Returns the entry for an avatar, if preset | ||
184 | * @returns Pointer to avatar entry, NULL if not found. | ||
185 | */ | ||
186 | LLAvatarListEntry* getAvatarEntry(LLUUID avatar); | ||
187 | |||
188 | /** | ||
189 | * @brief Returns a string with the selected names in the list | ||
190 | */ | ||
191 | std::string getSelectedNames(const std::string& separator = ", "); | ||
192 | std::string getSelectedName(); | ||
193 | LLUUID getSelectedID(); | ||
194 | |||
195 | private: | ||
196 | static LLFloaterAvatarList* sInstance; | ||
197 | |||
198 | public: | ||
199 | static LLFloaterAvatarList* getInstance() { return sInstance; } | ||
200 | private: | ||
201 | // when a line editor loses keyboard focus, it is committed. | ||
202 | // commit callbacks are named onCommitWidgetName by convention. | ||
203 | //void onCommitBaz(LLUICtrl* ctrl, void *userdata); | ||
204 | |||
205 | enum AVATARS_COLUMN_ORDER | ||
206 | { | ||
207 | LIST_MARK, | ||
208 | LIST_AVATAR_NAME, | ||
209 | LIST_DISTANCE, | ||
210 | LIST_POSITION, | ||
211 | LIST_ALTITUDE | ||
212 | }; | ||
213 | |||
214 | typedef void (*avlist_command_t)(const LLUUID &avatar, const std::string &name); | ||
215 | |||
216 | /** | ||
217 | * @brief Removes focus status from all avatars in list | ||
218 | */ | ||
219 | void removeFocusFromAll(); | ||
220 | |||
221 | /** | ||
222 | * @brief Focus camera on current avatar | ||
223 | */ | ||
224 | void focusOnCurrent(); | ||
225 | |||
226 | /** | ||
227 | * @brief Focus camera on previous avatar | ||
228 | * @param marked_only Whether to choose only marked avatars | ||
229 | */ | ||
230 | void focusOnPrev(BOOL marked_only); | ||
231 | |||
232 | /** | ||
233 | * @brief Focus camera on next avatar | ||
234 | * @param marked_only Whether to choose only marked avatars | ||
235 | */ | ||
236 | void focusOnNext(BOOL marked_only); | ||
237 | |||
238 | /** | ||
239 | * @brief Handler for the "refresh" button click. | ||
240 | * I am unsure whether this is actually necessary at the time. | ||
241 | * | ||
242 | * LL: By convention, button callbacks are named onClickButtonLabel | ||
243 | * @param userdata Pointer to user data (LLFloaterAvatarList instance) | ||
244 | */ | ||
245 | |||
246 | static void onClickProfile(void *userdata); | ||
247 | static void onClickIM(void *userdata); | ||
248 | static void onClickTeleportOffer(void *userdata); | ||
249 | static void onClickTrack(void *userdata); | ||
250 | static void onClickMark(void *userdata); | ||
251 | static void onClickFocus(void *userdata); | ||
252 | |||
253 | static void onClickPrevInList(void *userdata); | ||
254 | static void onClickNextInList(void *userdata); | ||
255 | static void onClickPrevMarked(void *userdata); | ||
256 | static void onClickNextMarked(void *userdata); | ||
257 | static void onClickGetKey(void *userdata); | ||
258 | |||
259 | static void onClickFreeze(void *userdata); | ||
260 | static void onClickEject(void *userdata); | ||
261 | static void onClickMute(void *userdata); | ||
262 | static void onClickAR(void *userdata); | ||
263 | static void onClickTeleport(void *userdata); | ||
264 | static void onClickEjectFromEstate(void *userdata); | ||
265 | |||
266 | static void callbackFreeze(const LLSD& notification, const LLSD& response); | ||
267 | static void callbackEject(const LLSD& notification, const LLSD& response); | ||
268 | static void callbackAR(void *userdata); | ||
269 | static void callbackEjectFromEstate(const LLSD& notification, const LLSD& response); | ||
270 | |||
271 | static void onSelectName(LLUICtrl*, void *userdata); | ||
272 | |||
273 | static void onCommitUpdateRate(LLUICtrl*, void *userdata); | ||
274 | static void onClickSendKeys(void *userdata); | ||
275 | |||
276 | static void callbackIdle(void *userdata); | ||
277 | |||
278 | void doCommand(avlist_command_t cmd); | ||
279 | |||
280 | /** | ||
281 | * @brief Cleanup avatar list, removing dead entries from it. | ||
282 | * This lets dead entries remain for some time. This makes it possible | ||
283 | * to keep people passing by in the list long enough that it's possible | ||
284 | * to do something to them. | ||
285 | */ | ||
286 | void expireAvatarList(); | ||
287 | |||
288 | private: | ||
289 | /** | ||
290 | * @brief Pointer to the avatar scroll list | ||
291 | */ | ||
292 | LLScrollListCtrl* mAvatarList; | ||
293 | std::map<LLUUID, LLAvatarListEntry> mAvatars; | ||
294 | |||
295 | /** | ||
296 | * @brief TRUE when Updating | ||
297 | */ | ||
298 | BOOL mUpdate; | ||
299 | |||
300 | /** | ||
301 | * @brief Update rate (if min frames per update) | ||
302 | */ | ||
303 | U32 mUpdateRate; | ||
304 | |||
305 | void stopTracker(); | ||
306 | void refreshTracker(); | ||
307 | |||
308 | // tracking data | ||
309 | BOOL mTracking; // Tracking ? | ||
310 | LLUUID mTrackedAvatar; // Who we are tracking | ||
311 | |||
312 | /** | ||
313 | * @brief Avatar the camera is focused on | ||
314 | */ | ||
315 | LLUUID mFocusedAvatar; | ||
316 | }; | ||
diff --git a/linden/indra/newview/llstartup.cpp b/linden/indra/newview/llstartup.cpp index 80ddfa4..88445c3 100644 --- a/linden/indra/newview/llstartup.cpp +++ b/linden/indra/newview/llstartup.cpp | |||
@@ -82,6 +82,7 @@ | |||
82 | 82 | ||
83 | #include "llagent.h" | 83 | #include "llagent.h" |
84 | #include "llagentpilot.h" | 84 | #include "llagentpilot.h" |
85 | #include "llfloateravatarlist.h" | ||
85 | #include "llfloateravatarpicker.h" | 86 | #include "llfloateravatarpicker.h" |
86 | #include "llcallbacklist.h" | 87 | #include "llcallbacklist.h" |
87 | #include "llcallingcard.h" | 88 | #include "llcallingcard.h" |
@@ -2060,7 +2061,10 @@ bool idle_startup() | |||
2060 | { | 2061 | { |
2061 | LLFloaterMap::showInstance(); | 2062 | LLFloaterMap::showInstance(); |
2062 | } | 2063 | } |
2063 | 2064 | if (gSavedSettings.getBOOL("ShowRadar")) | |
2065 | { | ||
2066 | LLFloaterAvatarList::showInstance(); | ||
2067 | } | ||
2064 | if (gSavedSettings.getBOOL("ShowCameraControls")) | 2068 | if (gSavedSettings.getBOOL("ShowCameraControls")) |
2065 | { | 2069 | { |
2066 | LLFloaterCamera::showInstance(); | 2070 | LLFloaterCamera::showInstance(); |
diff --git a/linden/indra/newview/llviewermenu.cpp b/linden/indra/newview/llviewermenu.cpp index b641ce9..e270dbb 100644 --- a/linden/indra/newview/llviewermenu.cpp +++ b/linden/indra/newview/llviewermenu.cpp | |||
@@ -89,6 +89,7 @@ | |||
89 | #include "llfloateractivespeakers.h" | 89 | #include "llfloateractivespeakers.h" |
90 | #include "llfloateranimpreview.h" | 90 | #include "llfloateranimpreview.h" |
91 | #include "llfloateravatarinfo.h" | 91 | #include "llfloateravatarinfo.h" |
92 | #include "llfloateravatarlist.h" | ||
92 | #include "llfloateravatartextures.h" | 93 | #include "llfloateravatartextures.h" |
93 | #include "llfloaterbeacons.h" | 94 | #include "llfloaterbeacons.h" |
94 | #include "llfloaterbuildoptions.h" | 95 | #include "llfloaterbuildoptions.h" |
@@ -6147,6 +6148,10 @@ class LLShowFloater : public view_listener_t | |||
6147 | { | 6148 | { |
6148 | LLFloaterPerms::toggleInstance(LLSD()); | 6149 | LLFloaterPerms::toggleInstance(LLSD()); |
6149 | } | 6150 | } |
6151 | else if (floater_name == "full radar") | ||
6152 | { | ||
6153 | LLFloaterAvatarList::toggle(NULL); | ||
6154 | } | ||
6150 | return true; | 6155 | return true; |
6151 | } | 6156 | } |
6152 | }; | 6157 | }; |
@@ -6221,6 +6226,10 @@ class LLFloaterVisible : public view_listener_t | |||
6221 | if (!instn) new_value = false; | 6226 | if (!instn) new_value = false; |
6222 | else new_value = instn->getVisible(); | 6227 | else new_value = instn->getVisible(); |
6223 | } | 6228 | } |
6229 | else if (floater_name == "full radar") | ||
6230 | { | ||
6231 | new_value = (LLFloaterAvatarList::getInstance() != NULL); | ||
6232 | } | ||
6224 | gMenuHolder->findControl(control_name)->setValue(new_value); | 6233 | gMenuHolder->findControl(control_name)->setValue(new_value); |
6225 | return true; | 6234 | return true; |
6226 | } | 6235 | } |
diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_radar.xml b/linden/indra/newview/skins/default/xui/en-us/floater_radar.xml new file mode 100644 index 0000000..7db7330 --- /dev/null +++ b/linden/indra/newview/skins/default/xui/en-us/floater_radar.xml | |||
@@ -0,0 +1,327 @@ | |||
1 | <?xml version="1.0" encoding="utf-8" standalone="yes"?> | ||
2 | <floater name="radar" title="Radar" | ||
3 | can_resize="true" can_minimize="true" can_close="true" can_drag_on_left="false" | ||
4 | rect_control="FloaterRadarRect" min_width="300" min_height="300"> | ||
5 | |||
6 | <scroll_list name="avatar_list" | ||
7 | left="10" right="-10" top="-20" bottom="140" can_resize="true" | ||
8 | column_padding="0" follows="left|top|bottom|right" | ||
9 | draw_heading="true" multi_select="true" search_column="1" | ||
10 | tool_tip="Hold shift or control while clicking to select multiple avatars"> | ||
11 | <column name="marked" label="Mark" width="12" tool_tip="Marked avatars"/> | ||
12 | <column name="avatar_name" label="Name" dynamicwidth="true" tool_tip="Hold shift or control while clicking to select multiple avatars"/> | ||
13 | <column name="distance" label="Dist." width="48" tool_tip="Distance from your avatar (red=within chat range, yellow=within shout range, green=within draw distance)"/> | ||
14 | <column name="position" label="Pos." width="60" tool_tip="Position (X, Y) within this sim, or general direction (cardinal point) for outside sims"/> | ||
15 | <column name="altitude" label="Alt." width="48" tool_tip="Altitude"/> | ||
16 | </scroll_list> | ||
17 | |||
18 | <tab_container border="false" bottom_delta="-130" height="120" left="6" mouse_opaque="false" | ||
19 | name="actions_tab_container" tab_position="top" follows="left|right|bottom"> | ||
20 | <panel border="true" bottom_delta="-130" follows="left|top|right|bottom" height="255" | ||
21 | label="Avatar" left="1" mouse_opaque="true" | ||
22 | name="actions_tab" width="398"> | ||
23 | <!-- upper row --> | ||
24 | <button | ||
25 | name="profile_btn" | ||
26 | label="Profile" | ||
27 | tool_tip="Show picture, groups, and other information" | ||
28 | left="10" | ||
29 | bottom_delta="-180" | ||
30 | width="80" | ||
31 | height="20" | ||
32 | font="SansSerifSmall" | ||
33 | follows="bottom|left" | ||
34 | /> | ||
35 | <button | ||
36 | name="track_btn" | ||
37 | label="(Un)Track" | ||
38 | tool_tip="Toggle tracking of this avatar's position" | ||
39 | left_delta="90" | ||
40 | bottom_delta="0" | ||
41 | width="80" | ||
42 | height="20" | ||
43 | font="SansSerifSmall" | ||
44 | follows="bottom|left" | ||
45 | /> | ||
46 | <button | ||
47 | name="get_key_btn" | ||
48 | label="Get Key" | ||
49 | tool_tip="Copies avatar's key to the clipboard" | ||
50 | left_delta="90" | ||
51 | bottom_delta="0" | ||
52 | width="80" | ||
53 | height="20" | ||
54 | font="SansSerifSmall" | ||
55 | follows="bottom|left" | ||
56 | /> | ||
57 | <!-- upper middle row --> | ||
58 | <button | ||
59 | name="im_btn" | ||
60 | label="IM" | ||
61 | tool_tip="Open Instant Message session" | ||
62 | left="10" | ||
63 | bottom_delta="-22" | ||
64 | width="80" | ||
65 | height="20" | ||
66 | font="SansSerifSmall" | ||
67 | follows="bottom|left" | ||
68 | /> | ||
69 | <button | ||
70 | name="offer_btn" | ||
71 | label="Offer TP" | ||
72 | tool_tip="Offer Teleport" | ||
73 | left_delta="90" | ||
74 | bottom_delta="0" | ||
75 | width="80" | ||
76 | height="20" | ||
77 | font="SansSerifSmall" | ||
78 | follows="bottom|left" | ||
79 | /> | ||
80 | <button | ||
81 | name="teleport_btn" | ||
82 | label="TP to" | ||
83 | tool_tip="Teleport to avatar's position" | ||
84 | left_delta="90" | ||
85 | bottom_delta="0" | ||
86 | width="80" | ||
87 | height="20" | ||
88 | font="SansSerifSmall" | ||
89 | follows="bottom|left" | ||
90 | /> | ||
91 | <!-- lower middle row--> | ||
92 | <button | ||
93 | name="mark_btn" | ||
94 | label="(Un)Mark" | ||
95 | tool_tip="(Un)Mark this avatar in the list" | ||
96 | left="10" | ||
97 | bottom_delta="-22" | ||
98 | width="80" | ||
99 | height="20" | ||
100 | font="SansSerifSmall" | ||
101 | follows="bottom|left" | ||
102 | /> | ||
103 | <button | ||
104 | name="prev_marked_btn" | ||
105 | label="Prev Marked" | ||
106 | tool_tip="Focus camera on previous marked avatar" | ||
107 | left_delta="90" | ||
108 | bottom_delta="0" | ||
109 | width="80" | ||
110 | height="20" | ||
111 | font="SansSerifSmall" | ||
112 | follows="bottom|left" | ||
113 | /> | ||
114 | <button | ||
115 | name="next_marked_btn" | ||
116 | label="Next Marked" | ||
117 | tool_tip="Focus camera on next marked avatar" | ||
118 | left_delta="90" | ||
119 | bottom_delta="0" | ||
120 | width="80" | ||
121 | height="20" | ||
122 | font="SansSerifSmall" | ||
123 | follows="bottom|left" | ||
124 | /> | ||
125 | <!-- lower row--> | ||
126 | <button | ||
127 | name="focus_btn" | ||
128 | label="Focus" | ||
129 | tool_tip="Focus camera on this avatar in the list" | ||
130 | left="10" | ||
131 | bottom_delta="-22" | ||
132 | width="80" | ||
133 | height="20" | ||
134 | font="SansSerifSmall" | ||
135 | follows="bottom|left" | ||
136 | /> | ||
137 | <button | ||
138 | name="prev_in_list_btn" | ||
139 | label="Previous" | ||
140 | tool_tip="Focus camera on previous avatar in list" | ||
141 | left_delta="90" | ||
142 | bottom_delta="0" | ||
143 | width="80" | ||
144 | height="20" | ||
145 | font="SansSerifSmall" | ||
146 | follows="bottom|left" | ||
147 | /> | ||
148 | <button | ||
149 | name="next_in_list_btn" | ||
150 | label="Next" | ||
151 | tool_tip="Focus camera on next avatar in list" | ||
152 | left_delta="90" | ||
153 | bottom_delta="0" | ||
154 | width="80" | ||
155 | height="20" | ||
156 | font="SansSerifSmall" | ||
157 | follows="bottom|left" | ||
158 | /> | ||
159 | |||
160 | </panel> | ||
161 | |||
162 | <panel border="true" bottom_delta="-150" follows="left|top|right|bottom" height="255" | ||
163 | label="Alerts" left="1" mouse_opaque="true" | ||
164 | name="alerts_tab" width="398"> | ||
165 | <check_box height="16" label="Display alerts in chat" | ||
166 | left="10" bottom_delta="-176" name="radar_alerts" | ||
167 | width="200" follows="bottom|left" | ||
168 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
169 | initial_value="true" enabled="true" radio_style="false" | ||
170 | control_name="RadarChatAlerts" | ||
171 | tool_tip="Announce alerts about incoming and outgoing avatars in chat" /> | ||
172 | |||
173 | <check_box height="16" label="Avatars entering/exiting the sim" | ||
174 | left="20" bottom_delta="-18" name="alerts_sim" | ||
175 | width="200" follows="bottom|left" | ||
176 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
177 | initial_value="true" enabled="true" radio_style="false" | ||
178 | control_name="RadarAlertSim" | ||
179 | tool_tip="Report avatars entering or exiting the sim" /> | ||
180 | |||
181 | <check_box height="16" label="Avatars within/beyond the draw distance" | ||
182 | left="20" bottom_delta="-18" name="alerts_draw" | ||
183 | width="200" follows="bottom|left" | ||
184 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
185 | initial_value="true" enabled="true" radio_style="false" | ||
186 | control_name="RadarAlertDraw" | ||
187 | tool_tip="Report avatars getting within or beyond the draw distance" /> | ||
188 | |||
189 | <check_box height="16" label="Avatars within/beyond the shout range" | ||
190 | left="20" bottom_delta="-18" name="alerts_chat" | ||
191 | width="200" follows="bottom|left" | ||
192 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
193 | initial_value="true" enabled="true" radio_style="false" | ||
194 | control_name="RadarAlertShoutRange" | ||
195 | tool_tip="Report avatars getting within or beyond the shout range" /> | ||
196 | |||
197 | <check_box height="16" label="Avatars within/beyond the chat range" | ||
198 | left="20" bottom_delta="-18" name="alerts_chat" | ||
199 | width="200" follows="bottom|left" | ||
200 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
201 | initial_value="true" enabled="true" radio_style="false" | ||
202 | control_name="RadarAlertChatRange" | ||
203 | tool_tip="Report avatars getting within or beyond the chat range" /> | ||
204 | </panel> | ||
205 | |||
206 | <panel border="true" bottom_delta="-130" follows="left|top|right|bottom" height="255" | ||
207 | label="Moderation" left="1" mouse_opaque="true" | ||
208 | name="land_tab" width="398"> | ||
209 | |||
210 | <!-- Upper row --> | ||
211 | <button | ||
212 | name="mute_btn" | ||
213 | label="(Un)Mute" | ||
214 | tool_tip="Mute or unmute this avatar (muted avatars' names are highlighted in red in the list)." | ||
215 | left="10" | ||
216 | bottom_delta="-200" | ||
217 | width="80" | ||
218 | height="20" | ||
219 | font="SansSerifSmall" | ||
220 | follows="bottom|left" | ||
221 | /> | ||
222 | <button | ||
223 | name="freeze_btn" | ||
224 | label="Freeze" | ||
225 | tool_tip="Freeze the avatar, preventing it from moving" | ||
226 | left_delta="90" | ||
227 | bottom_delta="0" | ||
228 | width="80" | ||
229 | height="20" | ||
230 | font="SansSerifSmall" | ||
231 | follows="bottom|left" | ||
232 | /> | ||
233 | <button | ||
234 | name="ar_btn" | ||
235 | label="AR" | ||
236 | tool_tip="Report abuse on this avatar" | ||
237 | left_delta="90" | ||
238 | bottom_delta="0" | ||
239 | width="80" | ||
240 | height="20" | ||
241 | font="SansSerifSmall" | ||
242 | follows="bottom|left" | ||
243 | /> | ||
244 | <!-- Lower row --> | ||
245 | <button | ||
246 | name="eject_btn" | ||
247 | label="Eject" | ||
248 | tool_tip="Eject the avatar from the parcel" | ||
249 | left="10" | ||
250 | bottom_delta="-24" | ||
251 | width="80" | ||
252 | height="20" | ||
253 | font="SansSerifSmall" | ||
254 | follows="bottom|left" | ||
255 | /> | ||
256 | |||
257 | <button | ||
258 | name="estate_eject_btn" | ||
259 | label="Eject from estate" | ||
260 | tool_tip="Eject this avatar from the estate" | ||
261 | left_delta="90" | ||
262 | bottom_delta="0" | ||
263 | width="170" | ||
264 | height="20" | ||
265 | font="SansSerifSmall" | ||
266 | follows="bottom|left" | ||
267 | /> | ||
268 | </panel> | ||
269 | |||
270 | <panel border="true" bottom_delta="-150" follows="left|top|right|bottom" height="255" | ||
271 | label="Options" left="1" mouse_opaque="true" | ||
272 | name="options_tab" width="398"> | ||
273 | <check_box height="16" label="Update" | ||
274 | left="10" bottom_delta="-180" name="update_enabled_cb" | ||
275 | width="200" follows="bottom|left" | ||
276 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
277 | initial_value="true" enabled="true" radio_style="false" | ||
278 | tool_tip="Set whether the avatar list should update" /> | ||
279 | <text type="string" length="1" bg_visible="false" border_drop_shadow_visible="false" border_visible="false" | ||
280 | bottom_delta="-24" drop_shadow_visible="true" enabled="true" font="SansSerifSmall" height="18" left="24" | ||
281 | mouse_opaque="false" name="rate" width="30"> | ||
282 | Rate: | ||
283 | </text> | ||
284 | <radio_group draw_border="true" bottom_delta="2" left_delta="44" follows="bottom|left" width="196" height="20" | ||
285 | mouse_opaque="true" name="update_rate" control_name="RadarUpdateRate" enabled="true" | ||
286 | tool_tip="Rate of the radar updates (eats up more FPS when higher)"> | ||
287 | <radio_item type="string" length="1" bottom_delta="0" height="16" left_delta="4" mouse_opaque="true" | ||
288 | name="high" width="20" follows="bottom|left"> | ||
289 | High | ||
290 | </radio_item> | ||
291 | <radio_item type="string" length="1" bottom_delta="0" height="16" left_delta="30" mouse_opaque="true" | ||
292 | name="medium" width="25" follows="bottom|left"> | ||
293 | Medium | ||
294 | </radio_item> | ||
295 | <radio_item type="string" length="1" bottom_delta="0" height="16" left_delta="35" mouse_opaque="true" | ||
296 | name="low" width="20" follows="bottom|left"> | ||
297 | Low | ||
298 | </radio_item> | ||
299 | </radio_group> | ||
300 | <check_box height="16" label="Announce even when closed" | ||
301 | left="10" bottom_delta="-24" name="keep_open" | ||
302 | width="200" follows="bottom|left" | ||
303 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
304 | initial_value="true" enabled="true" radio_style="false" | ||
305 | control_name="RadarKeepOpen" | ||
306 | tool_tip="Hides floater instead of closing to still announce in chat" /> | ||
307 | <check_box height="16" label="Announce keys to HUD" | ||
308 | left="10" bottom_delta="-20" name="radar_chat_keys" | ||
309 | width="200" follows="bottom|left" | ||
310 | hidden="false" mouse_opaque="true" font="SansSerifSmall" | ||
311 | initial_value="true" enabled="true" radio_style="false" | ||
312 | control_name="RadarChatKeys" | ||
313 | tool_tip="Set whether to send keys of avatars to LSL scripts" /> | ||
314 | <button | ||
315 | name="send_keys_btn" | ||
316 | label="Announce now" | ||
317 | tool_tip="Send all keys to HUD now" | ||
318 | left_delta="150" | ||
319 | bottom_delta="-2" | ||
320 | width="100" | ||
321 | height="20" | ||
322 | font="SansSerifSmall" | ||
323 | follows="bottom|left" | ||
324 | /> | ||
325 | </panel> | ||
326 | </tab_container> | ||
327 | </floater> \ No newline at end of file | ||
diff --git a/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml index 5fbf337..b31122a 100644 --- a/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml +++ b/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml | |||
@@ -1007,6 +1007,10 @@ | |||
1007 | <on_click function="ShowFloater" userdata="animation list" /> | 1007 | <on_click function="ShowFloater" userdata="animation list" /> |
1008 | <on_check function="FloaterVisible" userdata="animation list" /> | 1008 | <on_check function="FloaterVisible" userdata="animation list" /> |
1009 | </menu_item_check> | 1009 | </menu_item_check> |
1010 | <menu_item_check label="Full Radar" name="Full Radar"> | ||
1011 | <on_click function="ShowFloater" userdata="full radar" /> | ||
1012 | <on_check function="FloaterVisible" userdata="full radar" /> | ||
1013 | </menu_item_check> | ||
1010 | <menu_item_check label="Area Object Search" name="Area Object Search" | 1014 | <menu_item_check label="Area Object Search" name="Area Object Search" |
1011 | shortcut="alt|A"> | 1015 | shortcut="alt|A"> |
1012 | <on_click function="ShowFloater" userdata="areasearch" /> | 1016 | <on_click function="ShowFloater" userdata="areasearch" /> |