summaryrefslogtreecommitdiff
path: root/indra/newview/llinspectavatar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llinspectavatar.cpp')
-rw-r--r--indra/newview/llinspectavatar.cpp334
1 files changed, 301 insertions, 33 deletions
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index 72f89d2e72..9b75c15597 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -34,15 +34,106 @@
#include "llinspectavatar.h"
// viewer files
+#include "llagent.h"
#include "llagentdata.h"
#include "llavataractions.h"
#include "llavatarpropertiesprocessor.h"
#include "llcallingcard.h"
-
-// linden libraries
+#include "lldateutil.h" // ageFromDate()
+#include "llfloaterreporter.h"
+#include "llfloaterworldmap.h"
+#include "llmutelist.h"
+#include "llpanelblockedlist.h"
+#include "llviewermenu.h"
+#include "llvoiceclient.h"
+
+// Linden libraries
+#include "llcontrol.h" // LLCachedControl
+#include "llfloater.h"
+#include "llfloaterreg.h"
#include "lltooltip.h" // positionViewNearMouse()
#include "lluictrl.h"
+class LLFetchAvatarData;
+
+
+//////////////////////////////////////////////////////////////////////////////
+// LLInspectAvatar
+//////////////////////////////////////////////////////////////////////////////
+
+// Avatar Inspector, a small information window used when clicking
+// on avatar names in the 2D UI and in the ambient inspector widget for
+// the 3D world.
+class LLInspectAvatar : public LLFloater
+{
+ friend class LLFloaterReg;
+
+public:
+ // avatar_id - Avatar ID for which to show information
+ // Inspector will be positioned relative to current mouse position
+ LLInspectAvatar(const LLSD& avatar_id);
+ virtual ~LLInspectAvatar();
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void draw();
+
+ // Because floater is single instance, need to re-parse data on each spawn
+ // (for example, inspector about same avatar but in different position)
+ /*virtual*/ void onOpen(const LLSD& avatar_id);
+
+ // Inspectors close themselves when they lose focus
+ /*virtual*/ void onFocusLost();
+
+ // Update view based on information from avatar properties processor
+ void processAvatarData(LLAvatarData* data);
+
+private:
+ // Make network requests for all the data to display in this view.
+ // Used on construction and if avatar id changes.
+ void requestUpdate();
+
+ // Set the volume slider to this user's current client-side volume setting,
+ // hiding/disabling if the user is not nearby.
+ void updateVolumeSlider();
+
+ // Button callbacks
+ void onClickAddFriend();
+ void onClickViewProfile();
+ void onClickIM();
+ void onClickTeleport();
+ void onClickInviteToGroup();
+ void onClickPay();
+ void onClickBlock();
+ void onClickReport();
+ bool onVisibleFindOnMap();
+ bool onVisibleGodMode();
+ void onClickMuteVolume();
+ void onFindOnMap();
+ void onVolumeChange(const LLSD& data);
+
+ // Callback for gCacheName to look up avatar name
+ void nameUpdatedCallback(
+ const LLUUID& id,
+ const std::string& first,
+ const std::string& last,
+ BOOL is_group);
+
+private:
+ LLUUID mAvatarID;
+ // Need avatar name information to spawn friend add request
+ std::string mAvatarName;
+ LLUUID mPartnerID;
+ // an in-flight request for avatar properties from LLAvatarPropertiesProcessor
+ // is represented by this object
+ LLFetchAvatarData* mPropertiesRequest;
+ LLFrameTimer mCloseTimer;
+ LLFrameTimer mOpenTimer;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// LLFetchAvatarData
+//////////////////////////////////////////////////////////////////////////////
+
// This object represents a pending request for avatar properties information
class LLFetchAvatarData : public LLAvatarPropertiesObserver
{
@@ -50,8 +141,8 @@ public:
// If the inspector closes it will delete the pending request object, so the
// inspector pointer will be valid for the lifetime of this object
LLFetchAvatarData(const LLUUID& avatar_id, LLInspectAvatar* inspector)
- : mAvatarID(avatar_id),
- mInspector(inspector)
+ : mAvatarID(avatar_id),
+ mInspector(inspector)
{
LLAvatarPropertiesProcessor* processor =
LLAvatarPropertiesProcessor::getInstance();
@@ -61,14 +152,14 @@ public:
// properties processor)
processor->sendAvatarPropertiesRequest(mAvatarID);
}
-
+
~LLFetchAvatarData()
{
// remove ourselves as an observer
LLAvatarPropertiesProcessor::getInstance()->
- removeObserver(mAvatarID, this);
+ removeObserver(mAvatarID, this);
}
-
+
void processProperties(void* data, EAvatarProcessorType type)
{
// route the data to the inspector
@@ -79,7 +170,7 @@ public:
mInspector->processAvatarData(avatar_data);
}
}
-
+
// Store avatar ID so we can un-register the observer on destruction
LLUUID mAvatarID;
LLInspectAvatar* mInspector;
@@ -88,10 +179,24 @@ public:
LLInspectAvatar::LLInspectAvatar(const LLSD& sd)
: LLFloater( LLSD() ), // single_instance, doesn't really need key
mAvatarID(), // set in onOpen()
- mFirstName(),
- mLastName(),
- mPropertiesRequest(NULL)
+ mPartnerID(),
+ mAvatarName(),
+ mPropertiesRequest(NULL),
+ mCloseTimer()
{
+ mCommitCallbackRegistrar.add("InspectAvatar.ViewProfile", boost::bind(&LLInspectAvatar::onClickViewProfile, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.AddFriend", boost::bind(&LLInspectAvatar::onClickAddFriend, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.IM", boost::bind(&LLInspectAvatar::onClickIM, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.Teleport", boost::bind(&LLInspectAvatar::onClickTeleport, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.InviteToGroup", boost::bind(&LLInspectAvatar::onClickInviteToGroup, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.Block", boost::bind(&LLInspectAvatar::onClickBlock, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.Report", boost::bind(&LLInspectAvatar::onClickReport, this));
+ mCommitCallbackRegistrar.add("InspectAvatar.FindOnMap", boost::bind(&LLInspectAvatar::onFindOnMap, this));
+ mVisibleCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this));
+ mVisibleCallbackRegistrar.add("InspectAvatar.VisibleGodMode", boost::bind(&LLInspectAvatar::onVisibleGodMode, this));
+
+
// can't make the properties request until the widgets are constructed
// as it might return immediately, so do it in postBuild.
}
@@ -113,18 +218,35 @@ BOOL LLInspectAvatar::postBuild(void)
getChild<LLUICtrl>("view_profile_btn")->setCommitCallback(
boost::bind(&LLInspectAvatar::onClickViewProfile, this) );
+ getChild<LLUICtrl>("mute_btn")->setCommitCallback(
+ boost::bind(&LLInspectAvatar::onClickMuteVolume, this) );
+
+ getChild<LLUICtrl>("volume_slider")->setCommitCallback(
+ boost::bind(&LLInspectAvatar::onVolumeChange, this, _2));
+
return TRUE;
}
void LLInspectAvatar::draw()
{
- static LLCachedControl<F32> FADE_OUT_TIME(*LLUI::sSettingGroups["config"], "InspectorFadeTime", 1.f);
- if (mCloseTimer.getStarted())
+ static LLCachedControl<F32> FADE_TIME(*LLUI::sSettingGroups["config"], "InspectorFadeTime", 1.f);
+ if (mOpenTimer.getStarted())
{
- F32 alpha = clamp_rescale(mCloseTimer.getElapsedTimeF32(), 0.f, FADE_OUT_TIME, 1.f, 0.f);
+ F32 alpha = clamp_rescale(mOpenTimer.getElapsedTimeF32(), 0.f, FADE_TIME, 0.f, 1.f);
LLViewDrawContext context(alpha);
LLFloater::draw();
- if (mCloseTimer.getElapsedTimeF32() > FADE_OUT_TIME)
+ if (alpha == 1.f)
+ {
+ mOpenTimer.stop();
+ }
+
+ }
+ else if (mCloseTimer.getStarted())
+ {
+ F32 alpha = clamp_rescale(mCloseTimer.getElapsedTimeF32(), 0.f, FADE_TIME, 1.f, 0.f);
+ LLViewDrawContext context(alpha);
+ LLFloater::draw();
+ if (mCloseTimer.getElapsedTimeF32() > FADE_TIME)
{
closeFloater(false);
}
@@ -142,9 +264,11 @@ void LLInspectAvatar::draw()
void LLInspectAvatar::onOpen(const LLSD& data)
{
mCloseTimer.stop();
+ mOpenTimer.start();
// Extract appropriate avatar id
- mAvatarID = data.isUUID() ? data : data["avatar_id"];
+ mAvatarID = data["avatar_id"];
+ mPartnerID = LLUUID::null;
// Position the inspector relative to the mouse cursor
// Similar to how tooltips are positioned
@@ -160,6 +284,8 @@ void LLInspectAvatar::onOpen(const LLSD& data)
// can't call from constructor as widgets are not built yet
requestUpdate();
+
+ updateVolumeSlider();
}
//virtual
@@ -167,6 +293,7 @@ void LLInspectAvatar::onFocusLost()
{
// Start closing when we lose focus
mCloseTimer.start();
+ mOpenTimer.stop();
}
void LLInspectAvatar::requestUpdate()
@@ -178,7 +305,9 @@ void LLInspectAvatar::requestUpdate()
getChild<LLUICtrl>("user_subtitle")->
setValue("Test subtitle");
getChild<LLUICtrl>("user_details")->
- setValue("Test details\nTest line 2");
+ setValue("Test details");
+ getChild<LLUICtrl>("user_partner")->
+ setValue("Test partner");
return;
}
@@ -186,6 +315,7 @@ void LLInspectAvatar::requestUpdate()
getChild<LLUICtrl>("user_name")->setValue("");
getChild<LLUICtrl>("user_subtitle")->setValue("");
getChild<LLUICtrl>("user_details")->setValue("");
+ getChild<LLUICtrl>("user_partner")->setValue("");
// Make a new request for properties
delete mPropertiesRequest;
@@ -212,49 +342,187 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data)
{
LLStringUtil::format_map_t args;
args["[BORN_ON]"] = data->born_on;
- args["[AGE]"] = LLAvatarPropertiesProcessor::ageFromDate(data->born_on);
+ args["[AGE]"] = LLDateUtil::ageFromDate(data->born_on);
args["[SL_PROFILE]"] = data->about_text;
args["[RW_PROFILE"] = data->fl_about_text;
args["[ACCTTYPE]"] = LLAvatarPropertiesProcessor::accountType(data);
- args["[PAYMENTINFO]"] = LLAvatarPropertiesProcessor::paymentInfo(data);
+ std::string payment_info = LLAvatarPropertiesProcessor::paymentInfo(data);
+ args["[PAYMENTINFO]"] = payment_info;
+ args["[COMMA]"] = (payment_info.empty() ? "" : ",");
std::string subtitle = getString("Subtitle", args);
getChild<LLUICtrl>("user_subtitle")->setValue( LLSD(subtitle) );
std::string details = getString("Details", args);
getChild<LLUICtrl>("user_details")->setValue( LLSD(details) );
+ // Look up partner name, if there is one
+ mPartnerID = data->partner_id;
+ if (mPartnerID.notNull())
+ {
+ gCacheName->get(mPartnerID, FALSE,
+ boost::bind(&LLInspectAvatar::nameUpdatedCallback,
+ this, _1, _2, _3, _4));
+ }
+
// Delete the request object as it has been satisfied
delete mPropertiesRequest;
mPropertiesRequest = NULL;
}
+void LLInspectAvatar::updateVolumeSlider()
+{
+ // By convention, we only display and toggle voice mutes, not all mutes
+ bool is_muted = LLMuteList::getInstance()->
+ isMuted(mAvatarID, LLMute::flagVoiceChat);
+ bool voice_enabled = gVoiceClient->getVoiceEnabled(mAvatarID);
+
+ LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn");
+ mute_btn->setEnabled( voice_enabled );
+ mute_btn->setValue( is_muted );
+
+ LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider");
+ volume_slider->setEnabled( voice_enabled && !is_muted );
+ const F32 DEFAULT_VOLUME = 0.5f;
+ F32 volume;
+ if (is_muted)
+ {
+ // it's clearer to display their volume as zero
+ volume = 0.f;
+ }
+ else if (!voice_enabled)
+ {
+ // use nominal value rather than 0
+ volume = DEFAULT_VOLUME;
+ }
+ else
+ {
+ // actual volume
+ volume = gVoiceClient->getUserVolume(mAvatarID);
+
+ // *HACK: Voice client doesn't have any data until user actually
+ // says something.
+ if (volume == 0.f)
+ {
+ volume = DEFAULT_VOLUME;
+ }
+ }
+ volume_slider->setValue( (F64)volume );
+}
+
+void LLInspectAvatar::onClickMuteVolume()
+{
+ // By convention, we only display and toggle voice mutes, not all mutes
+ LLMuteList* mute_list = LLMuteList::getInstance();
+ bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat);
+
+ LLMute mute(mAvatarID, mAvatarName, LLMute::AGENT);
+ if (!is_muted)
+ {
+ mute_list->add(mute, LLMute::flagVoiceChat);
+ }
+ else
+ {
+ mute_list->remove(mute, LLMute::flagVoiceChat);
+ }
+
+ updateVolumeSlider();
+}
+
+void LLInspectAvatar::onVolumeChange(const LLSD& data)
+{
+ F32 volume = (F32)data.asReal();
+ gVoiceClient->setUserVolume(mAvatarID, volume);
+}
+
void LLInspectAvatar::nameUpdatedCallback(
const LLUUID& id,
const std::string& first,
const std::string& last,
BOOL is_group)
{
- // Possibly a request for an older inspector
- if (id != mAvatarID) return;
-
- mFirstName = first;
- mLastName = last;
- std::string name = first + " " + last;
-
- childSetValue("user_name", LLSD(name) );
+ if (id == mAvatarID)
+ {
+ mAvatarName = first + " " + last;
+ childSetValue("user_name", LLSD(mAvatarName) );
+ }
+
+ if (id == mPartnerID)
+ {
+ LLStringUtil::format_map_t args;
+ args["[PARTNER]"] = first + " " + last;
+ std::string partner = getString("Partner", args);
+ getChild<LLUICtrl>("user_partner")->setValue(partner);
+ }
+ // Otherwise possibly a request for an older inspector, ignore it
}
void LLInspectAvatar::onClickAddFriend()
{
- std::string name;
- name.assign(mFirstName);
- name.append(" ");
- name.append(mLastName);
-
- LLAvatarActions::requestFriendshipDialog(mAvatarID, name);
+ LLAvatarActions::requestFriendshipDialog(mAvatarID, mAvatarName);
}
void LLInspectAvatar::onClickViewProfile()
{
+ // hide inspector when showing profile
+ setFocus(FALSE);
LLAvatarActions::showProfile(mAvatarID);
+
+}
+
+bool LLInspectAvatar::onVisibleFindOnMap()
+{
+ return gAgent.isGodlike() || is_agent_mappable(mAvatarID);
+}
+
+bool LLInspectAvatar::onVisibleGodMode()
+{
+ return gAgent.isGodlike();
+}
+
+void LLInspectAvatar::onClickIM()
+{
+ LLAvatarActions::startIM(mAvatarID);
+}
+
+void LLInspectAvatar::onClickTeleport()
+{
+ LLAvatarActions::offerTeleport(mAvatarID);
+}
+
+void LLInspectAvatar::onClickInviteToGroup()
+{
+ LLAvatarActions::inviteToGroup(mAvatarID);
+}
+
+void LLInspectAvatar::onClickPay()
+{
+ LLAvatarActions::pay(mAvatarID);
+}
+
+void LLInspectAvatar::onClickBlock()
+{
+ LLMute mute(mAvatarID, mAvatarName, LLMute::AGENT);
+ LLMuteList::getInstance()->add(mute);
+ LLPanelBlockedList::showPanelAndSelect(mute.mID);
+}
+
+void LLInspectAvatar::onClickReport()
+{
+ LLFloaterReporter::showFromObject(mAvatarID);
+}
+
+
+void LLInspectAvatar::onFindOnMap()
+{
+ gFloaterWorldMap->trackAvatar(mAvatarID, mAvatarName);
+ LLFloaterReg::showInstance("world_map");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// LLInspectAvatarUtil
+//////////////////////////////////////////////////////////////////////////////
+void LLInspectAvatarUtil::registerFloater()
+{
+ LLFloaterReg::add("inspect_avatar", "inspect_avatar.xml",
+ &LLFloaterReg::build<LLInspectAvatar>);
}