diff options
Diffstat (limited to 'indra/newview/llinspectavatar.cpp')
-rw-r--r-- | indra/newview/llinspectavatar.cpp | 334 |
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>); } |