diff options
| author | simon <none@none> | 2015-04-14 10:46:45 -0700 | 
|---|---|---|
| committer | simon <none@none> | 2015-04-14 10:46:45 -0700 | 
| commit | 99c5f36f204759af1a81268be0bc72dd69789de4 (patch) | |
| tree | 4c2449aa915f1b538788f69bf540f028847a334a /indra/newview | |
| parent | 00f18efe75cdf5d70b2f7f66ce702fc3bd25dd5d (diff) | |
| parent | a647b8f1cbab13f07ea889c80df28414bc906129 (diff) | |
Merge viewer-release, become version 3.7.28
Diffstat (limited to 'indra/newview')
24 files changed, 613 insertions, 40 deletions
| diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a7c5dbff82..78f04dccae 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -245,6 +245,7 @@ set(viewer_SOURCE_FILES      llfloaterhandler.cpp      llfloaterhardwaresettings.cpp      llfloaterhelpbrowser.cpp +    llfloaterhoverheight.cpp      llfloaterhud.cpp      llfloaterimagepreview.cpp      llfloaterimsessiontab.cpp @@ -849,6 +850,7 @@ set(viewer_HEADER_FILES      llfloaterhandler.h      llfloaterhardwaresettings.h      llfloaterhelpbrowser.h +    llfloaterhoverheight.h      llfloaterhud.h      llfloaterimagepreview.h      llfloaterimnearbychat.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index e5403775b2..7c35ff8c4b 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.7.26 +3.7.28 diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 15cb5bc0eb..de3732f339 100755 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -43,6 +43,7 @@  					<key>tags</key>  						<array>  						<!-- sample entry for debugging specific items	 +						     <string>Avatar</string>  						     <string>Inventory</string>  						     <string>SceneLoadTiming</string>  						     <string>Avatar</string> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index e3ab45c53e..88d74deb08 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -665,6 +665,21 @@        <key>Value</key>        <integer>2</integer>      </map> +    <key>AvatarPosFinalOffset</key> +    <map> +      <key>Comment</key> +      <string>After-everything-else fixup for avatar position.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Vector3</string> +      <key>Value</key> +      <array> +        <real>0.0</real> +        <real>0.0</real> +        <real>0.0</real> +      </array> +    </map>      <key>AvatarPickerURL</key>      <map>        <key>Comment</key> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index fc6f1f6395..d119504017 100755 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -1,5 +1,16 @@  <llsd>      <map> +    <key>AvatarHoverOffsetZ</key> +    <map> +      <key>Comment</key> +      <string>After-everything-else fixup for avatar Z position.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>F32</string> +      <key>Value</key> +        <real>0.0</real> +    </map>      <key>DoNotDisturbResponseChanged</key>          <map>          <key>Comment</key> diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 0e30c079ff..8dc8a2ff20 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -745,7 +745,7 @@ namespace action_give_inventory  		}  		std::string residents; -		LLAvatarActions::buildResidentsString(avatar_names, residents); +		LLAvatarActions::buildResidentsString(avatar_names, residents, true);  		std::string items;  		build_items_string(inventory_selected_uuids, items); @@ -777,7 +777,7 @@ namespace action_give_inventory  }  // static -void LLAvatarActions::buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string) +void LLAvatarActions::buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string, bool complete_name)  {  	llassert(avatar_names.size() > 0); @@ -785,7 +785,15 @@ void LLAvatarActions::buildResidentsString(std::vector<LLAvatarName> avatar_name  	const std::string& separator = LLTrans::getString("words_separator");  	for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; )  	{ -		residents_string.append((*it).getCompleteName()); +		if(complete_name) +		{ +			residents_string.append((*it).getCompleteName()); +		} +		else +		{ +			residents_string.append((*it).getDisplayName()); +		} +  		if	(++it == avatar_names.end())  		{  			break; diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 85813f2152..bd0ac24e93 100755 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -221,7 +221,7 @@ public:  	 * @param avatar_names - a vector of given avatar names from which resulting string is built  	 * @param residents_string - the resulting string  	 */ -	static void buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string); +	static void buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string, bool complete_name = false);  	/**  	 * Builds a string of residents' display names separated by "words_separator" string. diff --git a/indra/newview/llfloaterhoverheight.cpp b/indra/newview/llfloaterhoverheight.cpp new file mode 100755 index 0000000000..8908626de6 --- /dev/null +++ b/indra/newview/llfloaterhoverheight.cpp @@ -0,0 +1,157 @@ +/**  +* @file llfloaterhoverheight.cpp +* @brief Controller for self avatar hover height +* @author vir@lindenlab.com +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterhoverheight.h" +#include "llsliderctrl.h" +#include "llviewercontrol.h" +#include "llsdserialize.h" +#include "llhttpclient.h" +#include "llagent.h" +#include "llviewerregion.h" +#include "llvoavatarself.h" + +LLFloaterHoverHeight::LLFloaterHoverHeight(const LLSD& key) : LLFloater(key) +{ +} + +void LLFloaterHoverHeight::syncFromPreferenceSetting(void *user_data) +{ +	F32 value = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); + +	LLFloaterHoverHeight *self = static_cast<LLFloaterHoverHeight*>(user_data); +	LLSliderCtrl* sldrCtrl = self->getChild<LLSliderCtrl>("HoverHeightSlider"); +	sldrCtrl->setValue(value,FALSE); + +	if (isAgentAvatarValid()) +	{ +		LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z)); +		LL_INFOS("Avatar") << "setting hover from preference setting " << offset[2] << LL_ENDL; +		gAgentAvatarp->setHoverOffset(offset); +		//gAgentAvatarp->sendHoverHeight(); +	} +} + +BOOL LLFloaterHoverHeight::postBuild() +{ +	LLSliderCtrl* sldrCtrl = getChild<LLSliderCtrl>("HoverHeightSlider"); +	sldrCtrl->setMinValue(MIN_HOVER_Z); +	sldrCtrl->setMaxValue(MAX_HOVER_Z); +	sldrCtrl->setSliderMouseUpCallback(boost::bind(&LLFloaterHoverHeight::onFinalCommit,this)); +	sldrCtrl->setSliderEditorCommitCallback(boost::bind(&LLFloaterHoverHeight::onFinalCommit,this)); +	childSetCommitCallback("HoverHeightSlider", &LLFloaterHoverHeight::onSliderMoved, NULL); + +	// Initialize slider from pref setting. +	syncFromPreferenceSetting(this); +	// Update slider on future pref changes. +	if (gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ")) +	{ +		gSavedPerAccountSettings.getControl("AvatarHoverOffsetZ")->getCommitSignal()->connect(boost::bind(&syncFromPreferenceSetting, this)); +	} +	else +	{ +		LL_WARNS() << "Control not found for AvatarHoverOffsetZ" << LL_ENDL; +	} + +	updateEditEnabled(); + +	if (!mRegionChangedSlot.connected()) +	{ +		mRegionChangedSlot = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterHoverHeight::onRegionChanged,this)); +	} +	// Set up based on initial region. +	onRegionChanged(); + +	return TRUE; +} + +void LLFloaterHoverHeight::onClose(bool app_quitting) +{ +	if (mRegionChangedSlot.connected()) +	{ +		mRegionChangedSlot.disconnect(); +	} +} + +// static +void LLFloaterHoverHeight::onSliderMoved(LLUICtrl* ctrl, void* userData) +{ +	LLSliderCtrl* sldrCtrl = static_cast<LLSliderCtrl*>(ctrl); +	F32 value = sldrCtrl->getValueF32(); +	LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z)); +	LL_INFOS("Avatar") << "setting hover from slider moved" << offset[2] << LL_ENDL; +	gAgentAvatarp->setHoverOffset(offset, false); +} + +// Do send-to-the-server work when slider drag completes, or new +// value entered as text. +void LLFloaterHoverHeight::onFinalCommit() +{ +	LLSliderCtrl* sldrCtrl = getChild<LLSliderCtrl>("HoverHeightSlider"); +	F32 value = sldrCtrl->getValueF32(); +	gSavedPerAccountSettings.setF32("AvatarHoverOffsetZ",value); + +	LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z)); +	LL_INFOS("Avatar") << "setting hover from slider final commit " << offset[2] << LL_ENDL; +	gAgentAvatarp->setHoverOffset(offset, true); // will send update this time. +} + +void LLFloaterHoverHeight::onRegionChanged() +{ +	LLViewerRegion *region = gAgent.getRegion(); +	if (region && region->simulatorFeaturesReceived()) +	{ +		updateEditEnabled(); +	} +	else if (region) +	{ +		region->setSimulatorFeaturesReceivedCallback(boost::bind(&LLFloaterHoverHeight::onSimulatorFeaturesReceived,this,_1)); +	} +} + +void LLFloaterHoverHeight::onSimulatorFeaturesReceived(const LLUUID ®ion_id) +{ +	LLViewerRegion *region = gAgent.getRegion(); +	if (region && (region->getRegionID()==region_id)) +	{ +		updateEditEnabled(); +	} +} + +void LLFloaterHoverHeight::updateEditEnabled() +{ +	bool enabled = gAgent.getRegion() && gAgent.getRegion()->avatarHoverHeightEnabled(); +	LLSliderCtrl* sldrCtrl = getChild<LLSliderCtrl>("HoverHeightSlider"); +	sldrCtrl->setEnabled(enabled); +	if (enabled) +	{ +		syncFromPreferenceSetting(this); +	} +} + + diff --git a/indra/newview/llfloaterhoverheight.h b/indra/newview/llfloaterhoverheight.h new file mode 100755 index 0000000000..ee065bc184 --- /dev/null +++ b/indra/newview/llfloaterhoverheight.h @@ -0,0 +1,52 @@ +/**  +* @file   llfloaterhoverheight.h +* @brief  Controller for self avatar hover height. +* @author vir@lindenlab.com +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLFLOATERHOVERHEIGHT_H +#define LL_LLFLOATERHOVERHEIGHT_H + +#include "llfloater.h" + +class LLFloaterHoverHeight: public LLFloater +{ +public: +	LLFloaterHoverHeight(const LLSD& key); +	BOOL postBuild(); + +	static void onSliderMoved(LLUICtrl* ctrl, void* userData); + +	void onFinalCommit(); + +	static void syncFromPreferenceSetting(void *user_data); + +	void onRegionChanged(); +	void onSimulatorFeaturesReceived(const LLUUID ®ion_id); +	void updateEditEnabled(); + +	/*virtual*/ void onClose(bool app_quitting); +	boost::signals2::connection mRegionChangedSlot; +}; + +#endif diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp index 0880a5f35a..849aa7cd14 100755 --- a/indra/newview/llfloaterperms.cpp +++ b/indra/newview/llfloaterperms.cpp @@ -35,6 +35,8 @@  #include "llagent.h"  #include "llviewerregion.h"  #include "llnotificationsutil.h" +#include "llsdserialize.h" +#include "llvoavatar.h"  LLFloaterPerms::LLFloaterPerms(const LLSD& seed)  : LLFloater(seed) @@ -171,8 +173,9 @@ public:  private:  	static	std::string sPreviousReason; -	void error(U32 status, const std::string& reason) +	void httpFailure()  	{ +		const std::string& reason = getReason();  		// Do not display the same error more than once in a row  		if (reason != sPreviousReason)  		{ @@ -182,8 +185,12 @@ private:  			LLNotificationsUtil::add("DefaultObjectPermissions", args);  		}  	} -	void result(const LLSD& content) + +	void httpSuccess()  	{ +		//const LLSD& content = getContent(); +		//dump_sequential_xml("perms_responder_result.xml", content); +  		// Since we have had a successful POST call be sure to display the next error message  		// even if it is the same as a previous one.  		sPreviousReason = ""; diff --git a/indra/newview/llinspecttoast.cpp b/indra/newview/llinspecttoast.cpp index 0bc7bd188d..d04378daaf 100755 --- a/indra/newview/llinspecttoast.cpp +++ b/indra/newview/llinspecttoast.cpp @@ -89,6 +89,11 @@ void LLInspectToast::onOpen(const LLSD& notification_id)  	mConnection = toast->setOnToastDestroyedCallback(boost::bind(&LLInspectToast::onToastDestroy, this, _1));  	LLPanel * panel = toast->getPanel(); +	if (panel == NULL) +	{ +		LL_WARNS() << "Could not get toast's panel." << LL_ENDL; +		return; +	}  	panel->setVisible(TRUE);  	panel->setMouseOpaque(FALSE);  	if(mPanel != NULL && mPanel->getParent() == this) diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 8b927a0c6b..1ae9d916b3 100755 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -689,10 +689,6 @@ void LLTaskInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  	else if (canOpenItem())  	{  		items.push_back(std::string("Task Open")); -		if (!isItemCopyable()) -		{ -			disabled_items.push_back(std::string("Task Open")); -		}  	}  	items.push_back(std::string("Task Properties"));  	if(isItemRenameable()) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b3278b2437..e519032b9e 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1353,11 +1353,11 @@ bool idle_startup()  			{  				LLStringUtil::format_map_t args;  				args["[NUMBER]"] = llformat("%d", num_retries + 1); -				set_startup_status(0.4f, LLTrans::getString("LoginRetrySeedCapGrant", args), gAgent.mMOTD); +				set_startup_status(0.4f, LLTrans::getString("LoginRetrySeedCapGrant", args), gAgent.mMOTD.c_str());  			}  			else  			{ -				set_startup_status(0.4f, LLTrans::getString("LoginRequestSeedCapGrant"), gAgent.mMOTD); +				set_startup_status(0.4f, LLTrans::getString("LoginRequestSeedCapGrant"), gAgent.mMOTD.c_str());  			}  		}  		display_startup(); @@ -2071,7 +2071,7 @@ bool idle_startup()  			update_texture_fetch();  			set_startup_status(0.60f + 0.30f * timeout_frac,  				LLTrans::getString("LoginPrecaching"), -					gAgent.mMOTD); +					gAgent.mMOTD.c_str());  			display_startup();  		} @@ -2211,6 +2211,8 @@ bool idle_startup()  		llassert(LLPathfindingManager::getInstance() != NULL);  		LLPathfindingManager::getInstance()->initSystem(); +		gAgentAvatarp->sendHoverHeight(); +  		return TRUE;  	} diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index e19fe9ca75..fc18b20758 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -70,6 +70,7 @@  #include "llfloatergroups.h"  #include "llfloaterhardwaresettings.h"  #include "llfloaterhelpbrowser.h" +#include "llfloaterhoverheight.h"  #include "llfloaterhud.h"  #include "llfloaterimagepreview.h"  #include "llfloaterimsession.h" @@ -221,7 +222,8 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("god_tools", "floater_god_tools.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGodTools>);  	LLFloaterReg::add("group_picker", "floater_choose_group.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGroupPicker>); -	LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHelpBrowser>);	 +	LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHelpBrowser>); +	LLFloaterReg::add("edit_hover_height", "floater_edit_hover_height.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHoverHeight>);  	LLFloaterReg::add("hud", "floater_hud.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHUD>);  	LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterIMSession>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 22a032563e..9d680e23d1 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3891,6 +3891,14 @@ class LLEnableEditShape : public view_listener_t  	}  }; +class LLEnableHoverHeight : public view_listener_t +{ +	bool handleEvent(const LLSD& userdata) +	{ +		return gAgent.getRegion() && gAgent.getRegion()->avatarHoverHeightEnabled(); +	} +}; +  class LLEnableEditPhysics : public view_listener_t  {  	bool handleEvent(const LLSD& userdata) @@ -6088,6 +6096,11 @@ void handle_edit_shape()  	LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_shape"));  } +void handle_hover_height() +{ +	LLFloaterReg::showInstance("edit_hover_height"); +} +  void handle_edit_physics()  {  	LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_physics")); @@ -8584,10 +8597,12 @@ void initialize_menus()  	view_listener_t::addMenu(new LLEditEnableTakeOff(), "Edit.EnableTakeOff");  	view_listener_t::addMenu(new LLEditEnableCustomizeAvatar(), "Edit.EnableCustomizeAvatar");  	view_listener_t::addMenu(new LLEnableEditShape(), "Edit.EnableEditShape"); +	view_listener_t::addMenu(new LLEnableHoverHeight(), "Edit.EnableHoverHeight");  	view_listener_t::addMenu(new LLEnableEditPhysics(), "Edit.EnableEditPhysics");  	commit.add("CustomizeAvatar", boost::bind(&handle_customize_avatar));  	commit.add("EditOutfit", boost::bind(&handle_edit_outfit));  	commit.add("EditShape", boost::bind(&handle_edit_shape)); +	commit.add("HoverHeight", boost::bind(&handle_hover_height));  	commit.add("EditPhysics", boost::bind(&handle_edit_physics));  	// View menu diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index bac742b0bb..75cb0d16ef 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -425,6 +425,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,  	mCacheDirty(FALSE),  	mReleaseNotesRequested(FALSE),  	mCapabilitiesReceived(false), +	mSimulatorFeaturesReceived(false),  	mBitsReceived(0.f),  	mPacketsReceived(0.f),  	mDead(FALSE), @@ -2038,6 +2039,26 @@ void LLViewerRegion::getInfo(LLSD& info)  	info["Region"]["Handle"]["y"] = (LLSD::Integer)y;  } +boost::signals2::connection LLViewerRegion::setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb) +{ +	return mSimulatorFeaturesReceivedSignal.connect(cb); +} + +void LLViewerRegion::setSimulatorFeaturesReceived(bool received) +{ +	mSimulatorFeaturesReceived = received; +	if (received) +	{ +		mSimulatorFeaturesReceivedSignal(getRegionID()); +		mSimulatorFeaturesReceivedSignal.disconnect_all_slots(); +	} +} + +bool LLViewerRegion::simulatorFeaturesReceived() const +{ +	return mSimulatorFeaturesReceived; +} +  void LLViewerRegion::getSimulatorFeatures(LLSD& sim_features) const  {  	sim_features = mSimulatorFeatures; @@ -2051,6 +2072,9 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)  	LLSDSerialize::toPrettyXML(sim_features, str);  	LL_INFOS() << str.str() << LL_ENDL;  	mSimulatorFeatures = sim_features; + +	setSimulatorFeaturesReceived(true); +	  }  //this is called when the parent is not cacheable. @@ -3099,6 +3123,12 @@ bool LLViewerRegion::dynamicPathfindingEnabled() const  	return ( mSimulatorFeatures.has("DynamicPathfindingEnabled") &&  			 mSimulatorFeatures["DynamicPathfindingEnabled"].asBoolean());  } + +bool LLViewerRegion::avatarHoverHeightEnabled() const +{ +	return ( mSimulatorFeatures.has("AvatarHoverHeightEnabled") && +			 mSimulatorFeatures["AvatarHoverHeightEnabled"].asBoolean()); +}  /* Static Functions */  void log_capabilities(const CapabilityMap &capmap) diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index d01e746936..c14fa5aee8 100755 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -309,12 +309,19 @@ public:  	bool meshRezEnabled() const;  	bool meshUploadEnabled() const; +	// has region received its simulator features list? Requires an additional query after caps received. +	void setSimulatorFeaturesReceived(bool); +	bool simulatorFeaturesReceived() const; +	boost::signals2::connection setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb); +	  	void getSimulatorFeatures(LLSD& info) const;	  	void setSimulatorFeatures(const LLSD& info);  	bool dynamicPathfindingEnabled() const; +	bool avatarHoverHeightEnabled() const; +  	typedef enum  	{  		CACHE_MISS_TYPE_FULL = 0, @@ -512,6 +519,7 @@ private:  	BOOL                                    mCacheDirty;  	BOOL	mAlive;					// can become false if circuit disconnects  	BOOL	mCapabilitiesReceived; +	BOOL	mSimulatorFeaturesReceived;  	BOOL    mReleaseNotesRequested;  	BOOL    mDead;  //if true, this region is in the process of deleting.  	BOOL    mPaused; //pause processing the objects in the region @@ -532,11 +540,13 @@ private:  	CacheMissItem::cache_miss_list_t   mCacheMissList;  	caps_received_signal_t mCapabilitiesReceivedSignal;		 +	caps_received_signal_t mSimulatorFeaturesReceivedSignal;		 +  	LLSD mSimulatorFeatures;  	// the materials capability throttle  	LLFrameTimer mMaterialsCapThrottleTimer; -LLFrameTimer	mRenderInfoRequestTimer; +	LLFrameTimer mRenderInfoRequestTimer;  };  inline BOOL LLViewerRegion::getRegionProtocol(U64 protocol) const diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 7292af99a4..75b81cdeed 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -111,6 +111,9 @@ extern F32 ANIM_SPEED_MAX;  extern F32 ANIM_SPEED_MIN;  extern U32 JOINT_COUNT_REQUIRED_FOR_FULLRIG; +const F32 MAX_HOVER_Z = 2.0; +const F32 MIN_HOVER_Z = -2.0; +  // #define OUTPUT_BREAST_DATA  using namespace LLAvatarAppearanceDefines; @@ -252,6 +255,8 @@ struct LLAppearanceMessageContents  	//U32 appearance_flags = 0;  	std::vector<F32> mParamWeights;  	std::vector<LLVisualParam*> mParams; +	LLVector3 mHoverOffset; +	bool mHoverOffsetWasSet;  };  struct LLVOAvatarChildJoint : public LLInitParam::ChoiceBlock<LLVOAvatarChildJoint> @@ -721,7 +726,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mCachedInMuteList(false)  {  	//VTResume();  // VTune -	 +	setHoverOffset(LLVector3(0.0, 0.0, 0.0)); +  	// mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline  	const BOOL needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job  	mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim ); @@ -774,6 +780,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mRuthTimer.reset();  	mRuthDebugTimer.reset();  	mDebugExistenceTimer.reset(); +	mLastAppearanceMessageTimer.reset();      if(LLSceneMonitor::getInstance()->isEnabled())  	{ @@ -1957,6 +1964,11 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys,  	// Do base class updates...  	U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); +	//LLTEContents tec; +	//S32 te_retval = parseTEMessage(mesgsys, _PREHASH_ObjectData, block_num, tec); + +	LL_DEBUGS("Avatar") << avString() << update_type << LL_ENDL;  +  	// Print out arrival information once we have name of avatar.  		if (has_name && getNVPair("FirstName"))  		{ @@ -3185,12 +3197,8 @@ bool LLVOAvatar::isInMuteList()  	return muted;  } -//------------------------------------------------------------------------ -// updateCharacter() -// called on both your avatar and other avatars -//------------------------------------------------------------------------ -BOOL LLVOAvatar::updateCharacter(LLAgent &agent) -{	 +void LLVOAvatar::updateDebugText() +{  	// clear debug text  	mDebugText.clear(); @@ -3227,6 +3235,22 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  			debug_line += llformat(" - cof rcv:%d", last_received_cof_version);  		}  		debug_line += llformat(" bsz-z: %f avofs-z: %f", mBodySize[2], mAvatarOffset[2]); +		bool hover_enabled = getRegion() && getRegion()->avatarHoverHeightEnabled(); +		debug_line += hover_enabled ? " H" : " h"; +		const LLVector3& hover_offset = getHoverOffset(); +		if (hover_offset[2] != 0.0) +		{ +			debug_line += llformat(" hov_z: %f", hover_offset[2]); +			debug_line += llformat(" %s", (mIsSitting ? "S" : "T")); +			debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-")); +		} +		F32 elapsed = mLastAppearanceMessageTimer.getElapsedTimeF32(); +		static const char *elapsed_chars = "Xx*..."; +		U32 bucket = U32(elapsed*2); +		if (bucket < strlen(elapsed_chars)) +		{ +			debug_line += llformat(" %c", elapsed_chars[bucket]); +		}  		addDebugText(debug_line);  	}  	if (gSavedSettings.getBOOL("DebugAvatarCompositeBaked")) @@ -3234,7 +3258,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		if (!mBakedTextureDebugText.empty())  			addDebugText(mBakedTextureDebugText);  	} -				  +  	if (LLVOAvatar::sShowAnimationDebug)  	{  		for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); @@ -3263,6 +3287,27 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		}  	} +	if (!mDebugText.size() && mText.notNull()) +	{ +		mText->markDead(); +		mText = NULL; +	} +	else if (mDebugText.size()) +	{ +		setDebugText(mDebugText); +	} +	mDebugText.clear(); + +} + +//------------------------------------------------------------------------ +// updateCharacter() +// called on both your avatar and other avatars +//------------------------------------------------------------------------ +BOOL LLVOAvatar::updateCharacter(LLAgent &agent) +{	 +	updateDebugText(); +	  	if (!mIsBuilt)  	{  		return FALSE; @@ -3371,9 +3416,15 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  	LLVector3 xyVel = getVelocity();  	xyVel.mV[VZ] = 0.0f;  	speed = xyVel.length(); - +	// remembering the value here prevents a display glitch if the +	// animation gets toggled during this update. +	bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED); +	  	if (!(mIsSitting && getParent()))  	{ +		// This case includes all configurations except sitting on an +		// object, so does include ground sit. +  		//--------------------------------------------------------------------  		// get timing info  		// handle initial condition case @@ -3427,9 +3478,14 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		// correct for the fact that the pelvis is not necessarily the center   		// of the agent's physical representation  		root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot; +		if (!mIsSitting && !was_sit_ground_constrained) +		{ +			root_pos += LLVector3d(getHoverOffset()); +		}  		LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); +  		if (newPosition != mRoot->getXform()->getWorldPosition())  		{		  			mRoot->touch(); @@ -3594,7 +3650,9 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  	}  	else if (mDrawable.notNull())  	{ -		mRoot->setPosition(mDrawable->getPosition()); +		LLVector3 pos = mDrawable->getPosition(); +		pos += getHoverOffset() * mDrawable->getRotation(); +		mRoot->setPosition(pos);  		mRoot->setRotation(mDrawable->getRotation());  	} @@ -3613,7 +3671,21 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  	{  		updateMotions(LLCharacter::NORMAL_UPDATE);  	} -	 + +	// Special handling for sitting on ground. +	if (!getParent() && (mIsSitting || was_sit_ground_constrained)) +	{ +		 +		F32 off_z = LLVector3d(getHoverOffset()).mdV[VZ]; +		if (off_z != 0.0) +		{ +			LLVector3 pos = mRoot->getWorldPosition(); +			pos.mV[VZ] += off_z; +			mRoot->touch(); +			mRoot->setWorldPosition(pos); +		} +	} +  	// update head position  	updateHeadOffset(); @@ -3697,17 +3769,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  	mRoot->updateWorldMatrixChildren(); -	if (!mDebugText.size() && mText.notNull()) -	{ -		mText->markDead(); -		mText = NULL; -	} -	else if (mDebugText.size()) -	{ -		setDebugText(mDebugText); -	} -	mDebugText.clear(); -  	//mesh vertices need to be reskinned  	mNeedsSkin = TRUE;  	return TRUE; @@ -7145,6 +7206,17 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe  		// For future use:  		//mesgsys->getU32Fast(_PREHASH_AppearanceData, _PREHASH_Flags, appearance_flags, 0);  	} + +	// Parse the AppearanceData field, if any. +	contents.mHoverOffsetWasSet = false; +	if (mesgsys->has(_PREHASH_AppearanceHover)) +	{ +		LLVector3 hover; +		mesgsys->getVector3Fast(_PREHASH_AppearanceHover, _PREHASH_HoverHeight, hover); +		LL_DEBUGS("Avatar") << avString() << " hover received " << hover.mV[ VX ] << "," << hover.mV[ VY ] << "," << hover.mV[ VZ ] << LL_ENDL; +		contents.mHoverOffset = hover; +		contents.mHoverOffsetWasSet = true; +	}  	// Parse visual params, if any.  	S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam); @@ -7262,6 +7334,8 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )  		return;  	} +	mLastAppearanceMessageTimer.reset(); +  	ESex old_sex = getSex();  	LLAppearanceMessageContents contents; @@ -7446,6 +7520,22 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )  		}  	} +	if (contents.mHoverOffsetWasSet && !isSelf()) +	{ +		// Got an update for some other avatar +		// Ignore updates for self, because we have a more authoritative value in the preferences. +		setHoverOffset(contents.mHoverOffset); +		LL_INFOS("Avatar") << avString() << "setting hover from message" << contents.mHoverOffset[2] << LL_ENDL; +	} + +	if (!contents.mHoverOffsetWasSet && !isSelf()) +	{ +		// If we don't get a value at all, we are presumably in a +		// region that does not support hover height. +		LL_WARNS() << avString() << "zeroing hover because not defined in appearance message" << LL_ENDL; +		setHoverOffset(LLVector3(0.0, 0.0, 0.0)); +	} +  	setCompositeUpdatesEnabled( TRUE );  	// If all of the avatars are completely baked, release the global image caches to conserve memory. diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 7034f8349f..1c3f4f2aa7 100755 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -214,7 +214,6 @@ public:  	/*virtual*/ LLVector3		getPosAgentFromGlobal(const LLVector3d &position);  	virtual void				updateVisualParams(); -  /**                    Inherited   **                                                                            **   *******************************************************************************/ @@ -234,6 +233,7 @@ private: //aligned members  	// Updates  	//--------------------------------------------------------------------  public: +	void			updateDebugText();  	virtual BOOL 	updateCharacter(LLAgent &agent);  	void 			idleUpdateVoiceVisualizer(bool voice_enabled);  	void 			idleUpdateMisc(bool detailed_update); @@ -992,6 +992,7 @@ public:  protected:  	LLFrameTimer	mRuthDebugTimer; // For tracking how long it takes for av to rez  	LLFrameTimer	mDebugExistenceTimer; // Debugging for how long the avatar has been in memory. +	LLFrameTimer	mLastAppearanceMessageTimer; // Time since last appearance message received.  	//--------------------------------------------------------------------  	// COF monitoring @@ -1024,6 +1025,9 @@ protected: // Shared with LLVOAvatarSelf  extern const F32 SELF_ADDITIONAL_PRI;  extern const S32 MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL; +extern const F32 MAX_HOVER_Z; +extern const F32 MIN_HOVER_Z; +  std::string get_sequential_numbered_file_name(const std::string& prefix,  											  const std::string& suffix);  void dump_sequential_xml(const std::string outprefix, const LLSD& content); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 3f3cd25f95..ae7a233876 100755 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -127,6 +127,25 @@ struct LocalTextureData  	LLTextureEntry *mTexEntry;  }; +// TODO - this class doesn't really do anything, could just use a base +// class responder if nothing else gets added. +class LLHoverHeightResponder: public LLHTTPClient::Responder +{ +public: +	LLHoverHeightResponder(): LLHTTPClient::Responder() {} + +private: +	void httpFailure() +	{ +		LL_WARNS() << dumpResponse() << LL_ENDL; +	} + +	void httpSuccess() +	{ +		LL_INFOS() << dumpResponse() << LL_ENDL; +	} +}; +  //-----------------------------------------------------------------------------  // Callback data  //----------------------------------------------------------------------------- @@ -159,7 +178,10 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id,  	mScreenp(NULL),  	mLastRegionHandle(0),  	mRegionCrossingCount(0), -	mInitialBakesLoaded(false) +	mInitialBakesLoaded(false), +	// Value outside legal range, so will always be a mismatch the +	// first time through. +	mLastHoverOffsetSent(LLVector3(0.0f, 0.0f, -999.0f))  {  	mMotionController.mIsSelf = TRUE; @@ -220,11 +242,40 @@ void LLVOAvatarSelf::initInstance()  		return;  	} +	setHoverIfRegionEnabled(); +  	//doPeriodically(output_self_av_texture_diagnostics, 30.0);  	doPeriodically(update_avatar_rez_metrics, 5.0);  	doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0);  } +void LLVOAvatarSelf::setHoverIfRegionEnabled() +{ +	if (getRegion() && getRegion()->simulatorFeaturesReceived()) +	{ +		if (getRegion()->avatarHoverHeightEnabled()) +		{ +			F32 hover_z = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); +			setHoverOffset(LLVector3(0.0, 0.0, llclamp(hover_z,MIN_HOVER_Z,MAX_HOVER_Z))); +			LL_INFOS("Avatar") << avString() << " set hover height from debug setting " << hover_z << LL_ENDL; +		} +		else  +		{ +			setHoverOffset(LLVector3(0.0, 0.0, 0.0)); +			LL_INFOS("Avatar") << avString() << " zeroing hover height, region does not support" << LL_ENDL; +		} +	} +	else +	{ +		LL_INFOS("Avatar") << avString() << " region or simulator features not known, no change on hover" << LL_ENDL; +		if (getRegion()) +		{ +			getRegion()->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1)); +		} + +	} +} +  bool LLVOAvatarSelf::checkStuckAppearance()  {  	const F32 CONDITIONAL_UNSTICK_INTERVAL = 300.0; @@ -835,6 +886,12 @@ void LLVOAvatarSelf::removeMissingBakedTextures()  	}  } +void LLVOAvatarSelf::onSimulatorFeaturesReceived(const LLUUID& region_id) +{ +	LL_INFOS("Avatar") << "simulator features received, setting hover based on region props" << LL_ENDL; +	setHoverIfRegionEnabled(); +} +  //virtual  void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp)  { @@ -853,6 +910,17 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp)  		//LL_INFOS() << "pos_from_old_region is " << global_pos_from_old_region  		//	<< " while pos_from_new_region is " << pos_from_new_region  		//	<< LL_ENDL; + +		// Update hover height, or schedule callback, based on whether +		// it's supported in this region. +		if (regionp->simulatorFeaturesReceived()) +		{ +			setHoverIfRegionEnabled(); +		} +		else +		{ +			regionp->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1)); +		}  	}  	if (!regionp || (regionp->getHandle() != mLastRegionHandle)) @@ -2709,6 +2777,39 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const  	return success;  } +//------------------------------------------------------------------------ +// sendHoverHeight() +//------------------------------------------------------------------------ +void LLVOAvatarSelf::sendHoverHeight() const +{ +	std::string url = gAgent.getRegion()->getCapability("AgentPreferences"); + +	if (!url.empty()) +	{ +		LLSD update = LLSD::emptyMap(); +		const LLVector3& hover_offset = getHoverOffset(); +		update["hover_height"] = hover_offset[2]; + +		LL_DEBUGS("Avatar") << avString() << "sending hover height value " << hover_offset[2] << LL_ENDL; +		LLHTTPClient::post(url, update, new LLHoverHeightResponder); + +		mLastHoverOffsetSent = hover_offset; +	} +} + +void LLVOAvatarSelf::setHoverOffset(const LLVector3& hover_offset, bool send_update) +{ +	if (getHoverOffset() != hover_offset) +	{ +		LL_INFOS("Avatar") << avString() << " setting hover due to change " << hover_offset[2] << LL_ENDL; +		LLVOAvatar::setHoverOffset(hover_offset, send_update); +	} +	if (send_update && (hover_offset != mLastHoverOffsetSent)) +	{ +		LL_INFOS("Avatar") << avString() << " sending hover due to change " << hover_offset[2] << LL_ENDL; +		sendHoverHeight(); +	} +}  //------------------------------------------------------------------------  // needsRenderBeam() diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 5f36872575..b8e9bbb77a 100755 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -75,6 +75,9 @@ protected:  	// LLViewerObject interface and related  	//--------------------------------------------------------------------  public: +	boost::signals2::connection                   mRegionChangedSlot; + +	void					onSimulatorFeaturesReceived(const LLUUID& region_id);  	/*virtual*/ void 		updateRegion(LLViewerRegion *regionp);  	/*virtual*/ void   	 	idleUpdate(LLAgent &agent, const F64 &time); @@ -328,6 +331,14 @@ public:  public:  	bool			sendAppearanceMessage(LLMessageSystem *mesgsys) const; +	// -- care and feeding of hover height. +	void 			setHoverIfRegionEnabled(); +	void			sendHoverHeight() const; +	/*virtual*/ void setHoverOffset(const LLVector3& hover_offset, bool send_update=true); + +private: +	mutable LLVector3 mLastHoverOffsetSent; +  /**                    Appearance   **                                                                            **   *******************************************************************************/ diff --git a/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml b/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml new file mode 100755 index 0000000000..8ec6735a01 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_edit_hover_height.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + positioning="cascading" + ignore_ui_scale="false" + legacy_header_height="225" + can_minimize="true" + can_close="true" + can_resize="true" + min_height="65" + min_width="515" + height="65" + layout="topleft" + name="HoverHeight" + single_instance="true" + help_topic="hover_height" + save_rect="true" + save_visibility="true" + title="SET HOVER HEIGHT" + width="515"> +      <slider +     enabled="false" +     control_name="HoverHeightSlider" +     decimal_digits="3" +     follows="top|left" +     height="15" +     increment="0.001" +     initial_value="0.0" +     label="Height" +     label_width="60" +     left="10" +     width="501" +     layout="topleft" +     name="HoverHeightSlider" +     top="35" +     can_edit_text="true" +     > +    </slider> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_attachment_self.xml b/indra/newview/skins/default/xui/en/menu_attachment_self.xml index bcbc8d5b86..c6ae844d67 100755 --- a/indra/newview/skins/default/xui/en/menu_attachment_self.xml +++ b/indra/newview/skins/default/xui/en/menu_attachment_self.xml @@ -91,6 +91,14 @@ name="Edit Outfit">      <menu_item_call.on_enable       function="Edit.EnableEditShape" />    </menu_item_call> +  <menu_item_call label="Hover Height" +     layout="topleft" +     name="Hover Height"> +     <menu_item_call.on_click +      function="HoverHeight" /> +     <menu_item_call.on_enable +      function="Edit.EnableHoverHeight" /> +  </menu_item_call>    <menu_item_call      label="My Friends"      layout="topleft" diff --git a/indra/newview/skins/default/xui/en/menu_avatar_self.xml b/indra/newview/skins/default/xui/en/menu_avatar_self.xml index ca0c9bd5e4..d3b0b07f70 100755 --- a/indra/newview/skins/default/xui/en/menu_avatar_self.xml +++ b/indra/newview/skins/default/xui/en/menu_avatar_self.xml @@ -229,6 +229,14 @@         <menu_item_call.on_enable          function="Edit.EnableEditShape" />     </menu_item_call> +   <menu_item_call label="Hover Height" +     layout="topleft" +     name="Hover Height"> +     <menu_item_call.on_click +      function="HoverHeight" /> +     <menu_item_call.on_enable +      function="Edit.EnableHoverHeight" /> +   </menu_item_call>     <menu_item_call       label="My Friends"       layout="topleft" | 
