diff options
Diffstat (limited to 'indra')
35 files changed, 3343 insertions, 731 deletions
| diff --git a/indra/llcommon/roles_constants.h b/indra/llcommon/roles_constants.h index effd15ea72..65ec290200 100755 --- a/indra/llcommon/roles_constants.h +++ b/indra/llcommon/roles_constants.h @@ -53,7 +53,7 @@ enum LLRoleChangeType  // KNOWN HOLES: use these for any single bit powers you need  // bit 0x1 << 46 -// bit 0x1 << 49 and above +// bit 0x1 << 51 and above  // These powers were removed to make group roles simpler  // bit 0x1 << 41 (GP_ACCOUNTING_VIEW) @@ -146,6 +146,9 @@ const U64 GP_SESSION_JOIN = 0x1LL << 16; //can join session  const U64 GP_SESSION_VOICE = 0x1LL << 27; //can hear/talk  const U64 GP_SESSION_MODERATOR = 0x1LL << 37; //can mute people's session +const U64 GP_EXPERIENCE_ADMIN	= 0x1LL << 49; // has admin rights to any experiences owned by this group +const U64 GP_EXPERIENCE_CREATOR = 0x1LL << 50; // can sign scripts for experiences owned by this group +  const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE  								| GP_LAND_ALLOW_SET_HOME  								| GP_NOTICES_RECEIVE diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index d193e367eb..5740584edb 100755 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -38,6 +38,7 @@ set(llmessage_SOURCE_FILES      llcurl.cpp      lldatapacker.cpp      lldispatcher.cpp +	llexperiencecache.cpp      llfiltersd2xmlrpc.cpp      llhost.cpp      llhttpassetstorage.cpp @@ -128,6 +129,7 @@ set(llmessage_HEADER_FILES      lldbstrings.h      lldispatcher.h      lleventflags.h +	llexperiencecache.h      llfiltersd2xmlrpc.h      llfollowcamparams.h      llhost.h diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp new file mode 100644 index 0000000000..219e68b51c --- /dev/null +++ b/indra/llmessage/llexperiencecache.cpp @@ -0,0 +1,650 @@ +/**  + * @file llexperiencecache.cpp + * @brief llexperiencecache and related class definitions + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llexperiencecache.h" + +#include "llavatarname.h" +#include "llframetimer.h" +#include "llhttpclient.h" +#include "llsdserialize.h" +#include <set> +#include <map> +#include "boost/tokenizer.hpp" + + +namespace LLExperienceCache +{ + +	typedef std::map<LLUUID, LLUUID> PrivateKeyMap; +	PrivateKeyMap experinceKeyMap; +	 +	void mapPrivateKeys(const LLSD& legacyKeys); + + +	std::string sLookupURL; + +	typedef std::map<LLUUID, std::string> ask_queue_t; +	ask_queue_t sAskQueue; + +	typedef std::map<LLUUID, F64> pending_queue_t; +	pending_queue_t sPendingQueue; + +	cache_t sCache; +	int sMaximumLookups = 10; + +	LLFrameTimer sRequestTimer; + +	// Periodically clean out expired entries from the cache +	LLFrameTimer sEraseExpiredTimer; + +	// May have multiple callbacks for a single ID, which are +	// represented as multiple slots bound to the signal. +	// Avoid copying signals via pointers. +	typedef std::map<LLUUID, callback_signal_t*> signal_map_t; +	signal_map_t sSignalMap; + + +	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); +	void eraseExpired(); + +	void processExperience( const LLUUID& public_key, const LLSD& experience )  +	{ +		sCache[public_key]=experience; +		LLSD & row = sCache[public_key]; + +		if(row.has(EXPIRES)) +		{ +			row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds(); +		} + +		if(row.has(EXPERIENCE_ID)) +		{ +			sPendingQueue.erase(row[EXPERIENCE_ID].asUUID()); +		} + +		if(row.has(OWNER_ID)) +		{ +			sPendingQueue.erase(row[OWNER_ID].asUUID()); +		} + +			 +		//signal +		signal_map_t::iterator sig_it =	sSignalMap.find(public_key); +		if (sig_it != sSignalMap.end()) +		{ +			callback_signal_t* signal = sig_it->second; +			(*signal)(experience); + +			sSignalMap.erase(public_key); + +			delete signal; +		} +	} + +	void initClass( ) +	{ +	} + +	const cache_t& getCached() +	{ +		return sCache; +	} + +	void setMaximumLookups( int maximumLookups) +	{ +		sMaximumLookups = maximumLookups; +	} + +	void bootstrap(const LLSD& legacyKeys, int initialExpiration) +	{ +		mapPrivateKeys(legacyKeys); +		LLSD::array_const_iterator it = legacyKeys.beginArray(); +		for(/**/; it != legacyKeys.endArray(); ++it) +		{ +			LLSD experience = *it; +			if(experience.has(EXPERIENCE_ID)) +			{ +				if(!experience.has(EXPIRES)) +				{ +					experience[EXPIRES] = initialExpiration; +				} +				processExperience(experience[EXPERIENCE_ID].asUUID(), experience); +			} +			else +			{ +				LL_WARNS("ExperienceCache")  +					<< "Skipping bootstrap entry which is missing " << EXPERIENCE_ID  +					<< LL_ENDL; +			} +		}		 +	} + + + +	bool expirationFromCacheControl(LLSD headers, F64 *expires) +	{ +		// Allow the header to override the default +		LLSD cache_control_header = headers["cache-control"]; +		if (cache_control_header.isDefined()) +		{ +			S32 max_age = 0; +			std::string cache_control = cache_control_header.asString(); +			if (max_age_from_cache_control(cache_control, &max_age)) +			{ +				LL_WARNS("ExperienceCache")  +					<< "got EXPIRES from headers, max_age " << max_age  +					<< LL_ENDL; +				F64 now = LLFrameTimer::getTotalSeconds(); +				*expires = now + (F64)max_age; +				return true; +			} +		} +		return false; +	} + + +	static const std::string MAX_AGE("max-age"); +	static const boost::char_separator<char> EQUALS_SEPARATOR("="); +	static const boost::char_separator<char> COMMA_SEPARATOR(","); + +	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age) +	{ +		// Split the string on "," to get a list of directives +		typedef boost::tokenizer<boost::char_separator<char> > tokenizer; +		tokenizer directives(cache_control, COMMA_SEPARATOR); + +		tokenizer::iterator token_it = directives.begin(); +		for ( ; token_it != directives.end(); ++token_it) +		{ +			// Tokens may have leading or trailing whitespace +			std::string token = *token_it; +			LLStringUtil::trim(token); + +			if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0) +			{ +				// ...this token starts with max-age, so let's chop it up by "=" +				tokenizer subtokens(token, EQUALS_SEPARATOR); +				tokenizer::iterator subtoken_it = subtokens.begin(); + +				// Must have a token +				if (subtoken_it == subtokens.end()) return false; +				std::string subtoken = *subtoken_it; + +				// Must exactly equal "max-age" +				LLStringUtil::trim(subtoken); +				if (subtoken != MAX_AGE) return false; + +				// Must have another token +				++subtoken_it; +				if (subtoken_it == subtokens.end()) return false; +				subtoken = *subtoken_it; + +				// Must be a valid integer +				// *NOTE: atoi() returns 0 for invalid values, so we have to +				// check the string first. +				// *TODO: Do servers ever send "0000" for zero?  We don't handle it +				LLStringUtil::trim(subtoken); +				if (subtoken == "0") +				{ +					*max_age = 0; +					return true; +				} +				S32 val = atoi( subtoken.c_str() ); +				if (val > 0 && val < S32_MAX) +				{ +					*max_age = val; +					return true; +				} +				return false; +			} +		} +		return false; +	} + + +	void importFile(std::istream& istr) +	{ +		LLSD data; +		S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); +		if(parse_count < 1) return; + +		LLSD experiences = data["experiences"]; + +		LLUUID public_key; +		LLSD::map_const_iterator it = experiences.beginMap(); +		for(; it != experiences.endMap() ; ++it) +		{ +			public_key.set(it->first); +			sCache[public_key]=it->second; +		} + +		LL_INFOS("ExperienceCache") << "loaded " << sCache.size() << LL_ENDL; +	} + +	void exportFile(std::ostream& ostr) +	{ +		LLSD experiences; + +		cache_t::const_iterator it =sCache.begin(); +		for( ; it != sCache.end() ; ++it) +		{ +			if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || +				it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)) +				continue; + +			experiences[it->first.asString()] = it->second; +		} + +		LLSD data; +		data["experiences"] = experiences; + +		LLSDSerialize::toPrettyXML(data, ostr); +	} + +	class LLExperienceResponder : public LLHTTPClient::Responder +	{ +	public: +		LLExperienceResponder(const ask_queue_t& keys) +			:mKeys(keys) +		{ + +		} + +		virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content) +		{ +			mHeaders = content; +		} + +		virtual void result(const LLSD& content) +		{ +			LLSD experiences = content["experience_keys"]; +			LLSD::array_const_iterator it = experiences.beginArray(); +			for( /**/ ; it != experiences.endArray(); ++it) +			{ +				const LLSD& row = *it; +				LLUUID public_key = row[EXPERIENCE_ID].asUUID(); + + +				LL_INFOS("ExperienceCache") << "Received result for " << public_key  +					<< " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ; + +				processExperience(public_key, row); +			} + +			LLSD error_ids = content["error_ids"]; +			LLSD::array_const_iterator errIt = error_ids.beginArray(); +			for( /**/ ; errIt != error_ids.endArray() ; ++errIt ) +			{ +				LLUUID id = errIt->asUUID();		 +				LLSD exp; +				exp[EXPIRES]=DEFAULT_EXPIRATION; +				exp[EXPERIENCE_ID] = id; +				exp[PROPERTIES]=PROPERTY_INVALID; +				exp["DoesNotExist"]=true; + +				processExperience(id, exp); +				LL_INFOS("ExperienceCache") << "Error result for " << id << LL_ENDL ; +			} + +			LL_INFOS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL; +		} + +		virtual void error(U32 status, const std::string& reason) +		{ + 			LL_WARNS("ExperienceCache") << "Request failed "<<status<<" "<<reason<< LL_ENDL; + 			// We're going to construct a dummy record and cache it for a while, + 			// either briefly for a 503 Service Unavailable, or longer for other + 			// errors. + 			F64 retry_timestamp = errorRetryTimestamp(status); +  +  + 			// Add dummy records for all agent IDs in this request + 			ask_queue_t::const_iterator it = mKeys.begin(); + 			for ( ; it != mKeys.end(); ++it) +			{ +				LLSD exp; +				exp[EXPIRES]=retry_timestamp; +				exp[EXPERIENCE_ID] = it->first; +				exp["key_type"] = it->second; +				exp["uuid"] = it->first; +				exp["error"] = (LLSD::Integer)status; +				exp[PROPERTIES]=PROPERTY_INVALID; + 				LLExperienceCache::processExperience(it->first, exp); + 			} + +		} + +		// Return time to retry a request that generated an error, based on +		// error type and headers.  Return value is seconds-since-epoch. +		F64 errorRetryTimestamp(S32 status) +		{ + +			// Retry-After takes priority +			LLSD retry_after = mHeaders["retry-after"]; +			if (retry_after.isDefined()) +			{ +				// We only support the delta-seconds type +				S32 delta_seconds = retry_after.asInteger(); +				if (delta_seconds > 0) +				{ +					// ...valid delta-seconds +					return F64(delta_seconds); +				} +			} + +			// If no Retry-After, look for Cache-Control max-age +			F64 expires = 0.0; +			if (LLExperienceCache::expirationFromCacheControl(mHeaders, &expires)) +			{ +				return expires; +			} + +			// No information in header, make a guess +			if (status == 503) +			{ +				// ...service unavailable, retry soon +				const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min +				return SERVICE_UNAVAILABLE_DELAY; +			} +			else if (status == 499) +			{ +				// ...we were probably too busy, retry quickly +				const F64 BUSY_DELAY = 10.0; // 10 seconds +				return BUSY_DELAY; + +			} +			else +			{ +				// ...other unexpected error +				const F64 DEFAULT_DELAY = 3600.0; // 1 hour +				return DEFAULT_DELAY; +			} +		} + +	private: +		ask_queue_t mKeys; +		LLSD mHeaders; +	}; + +	void requestExperiences()  +	{ +		if(sAskQueue.empty() || sLookupURL.empty()) +			return; + +		F64 now = LLFrameTimer::getTotalSeconds(); + +		const U32 EXP_URL_SEND_THRESHOLD = 3000; + + +		std::ostringstream ostr; + +		ask_queue_t keys; + +		ostr << sLookupURL; + +		char arg='?'; + +		int request_count = 0; +		for(ask_queue_t::const_iterator it = sAskQueue.begin() ; it != sAskQueue.end() && request_count < sMaximumLookups; ++it) +		{ +			const LLUUID& key = it->first; +			const std::string& key_type = it->second; + +			ostr << arg << key_type << '=' << key.asString() ; +		 +			keys[key]=key_type; +			request_count++; + +			sPendingQueue[key] = now; + +			arg='&'; + +			if(ostr.tellp() > EXP_URL_SEND_THRESHOLD) +			{ +				LL_INFOS("ExperienceCache") <<  " query: " << ostr.str() << LL_ENDL; +				LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); +				ostr.clear(); +				ostr.str(sLookupURL); +				arg='?'; +				keys.clear(); +			} +		} + +		if(ostr.tellp() > sLookupURL.size()) +		{ +			LL_INFOS("ExperienceCache") <<  " query: " << ostr.str() << LL_ENDL; +			LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); +		} + +		sAskQueue.clear(); +	} + +	bool isRequestPending(const LLUUID& public_key) +	{ +		bool isPending = false; +		const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; + +		pending_queue_t::const_iterator it = sPendingQueue.find(public_key); + +		if(it != sPendingQueue.end()) +		{ +			F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; +			isPending = (it->second > expire_time); +		} + +		return isPending; +	} + + +	void setLookupURL( const std::string& lookup_url ) +	{ +		sLookupURL = lookup_url; +		if(!sLookupURL.empty()) +		{ +			sLookupURL += "id/"; +		} +	} + +	bool hasLookupURL() +	{ +		return !sLookupURL.empty(); +	} + +	void idle() +	{ + +		const F32 SECS_BETWEEN_REQUESTS = 0.1f; +		if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) +		{ +			return; +		} + +		// Must be large relative to above +		const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds +		if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) +		{ +			eraseExpired(); +		} + + +		if(!sAskQueue.empty()) +		{ +			requestExperiences(); +		} +	} + +	void erase( const LLUUID& key ) +	{ +		cache_t::iterator it = sCache.find(key); +				 +		if(it != sCache.end()) +		{ +			sCache.erase(it); +		} +	} + +	void eraseExpired() +	{ +		F64 now = LLFrameTimer::getTotalSeconds(); +		cache_t::iterator it = sCache.begin(); +		while (it != sCache.end()) +		{ +			cache_t::iterator cur = it; +			LLSD& exp = cur->second; +			++it; +			if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) +			{ +				if(exp.has(EXPERIENCE_ID)) +				{ +					LLUUID id = exp[EXPERIENCE_ID].asUUID(); +					S32 properties = PROPERTY_INVALID; +					if(exp.has(PROPERTIES)) +					{ +						properties = exp[PROPERTIES].asInteger(); +					} +					if(id.notNull() && ((properties & PROPERTY_INVALID) == 0)) +					{ +						fetch(id, true); +					} +					else +					{ +						sCache.erase(cur); +					} +				} +			} +		} +	} + +	 +	bool fetch( const LLUUID& key, bool refresh/* = true*/ )  +	{ +		if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end())) +		{ +			LL_INFOS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ; +			sAskQueue[key]=EXPERIENCE_ID; + +			return true; +		} +		return false; +	} + +	void insert(const LLSD& experience_data ) +	{ +		if(experience_data.has(EXPERIENCE_ID)) +		{ +			sCache[experience_data[EXPERIENCE_ID].asUUID()]=experience_data; +		} +		else +		{ +			LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; +		} +	} + +	bool get( const LLUUID& key, LLSD& experience_data ) +	{ +		if(key.isNull()) return false; +		cache_t::const_iterator it = sCache.find(key); +	 +		if (it != sCache.end()) +		{ +			experience_data = it->second; +			return true; +		} +		 +		fetch(key); + +		return false; +	} + + +	void get( const LLUUID& key, callback_slot_t slot ) +	{ +		if(key.isNull()) return; + +		cache_t::const_iterator it = sCache.find(key); +		if (it != sCache.end()) +		{ +			// ...name already exists in cache, fire callback now +			callback_signal_t signal; +			signal.connect(slot); +			 +			signal(it->second); +			return; +		} + +		fetch(key); + +		// always store additional callback, even if request is pending +		signal_map_t::iterator sig_it = sSignalMap.find(key); +		if (sig_it == sSignalMap.end()) +		{ +			// ...new callback for this id +			callback_signal_t* signal = new callback_signal_t(); +			signal->connect(slot); +			sSignalMap[key] = signal; +		} +		else +		{ +			// ...existing callback, bind additional slot +			callback_signal_t* signal = sig_it->second; +			signal->connect(slot); +		} +	} + +} + + + +void LLExperienceCache::mapPrivateKeys( const LLSD& legacyKeys ) +{ +	LLSD::array_const_iterator exp = legacyKeys.beginArray(); +	for(/**/ ; exp != legacyKeys.endArray() ; ++exp) +	{ +		if(exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY)) +		{ +			experinceKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID(); +		} +	} +} + + +LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +{ +	if (private_key.isNull()) +		return LLUUID::null; + + +	PrivateKeyMap::const_iterator it=experinceKeyMap.find(private_key); +	if(it == experinceKeyMap.end()) +	{ +		if(null_if_not_found) +		{ +			return LLUUID::null; +		} +		return private_key; +	} +	LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL; +	return it->second; +} diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h new file mode 100644 index 0000000000..fb00ea31f0 --- /dev/null +++ b/indra/llmessage/llexperiencecache.h @@ -0,0 +1,94 @@ +/**  + * @file llexperiencecache.h + * @brief Caches information relating to experience keys + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLEXPERIENCECACHE_H +#define LL_LLEXPERIENCECACHE_H + +#include "linden_common.h" +#include <boost/signals2.hpp> + +class LLSD; +class LLUUID; + + + +namespace LLExperienceCache +{ +	const std::string PRIVATE_KEY	= "private_id"; + +	const std::string EXPERIENCE_ID	= "public_id"; +	const std::string OWNER_ID		= "owner_id"; +	const std::string NAME			= "name"; +	const std::string PROPERTIES	= "properties"; +	const std::string EXPIRES		= "expiration";   +	const std::string DESCRIPTION	= "description"; + +	// should be in sync with experience-api/experiences/models.py +	const int PROPERTY_INVALID		= 1 << 0; +	const int PROPERTY_PRIVILEGED	= 1 << 3; +	const int PROPERTY_GRID			= 1 << 4; +	const int PROPERTY_PRIVATE		= 1 << 5; +	const int PROPERTY_DISABLED		= 1 << 6;   +	const int PROPERTY_SUSPENDED	= 1 << 7; + + +	const static F64 DEFAULT_EXPIRATION = 600.0; + +	// Callback types for get() below +	typedef boost::signals2::signal<void (const LLSD& experience)> +		callback_signal_t; +	typedef callback_signal_t::slot_type callback_slot_t; +	typedef std::map<LLUUID, LLSD> cache_t; + + +	void setLookupURL(const std::string& lookup_url); +	bool hasLookupURL(); + +	void setMaximumLookups(int maximumLookups); + +	void idle(); +	void exportFile(std::ostream& ostr); +	void importFile(std::istream& istr); +	void initClass(); +	void bootstrap(const LLSD& legacyKeys, int initialExpiration); +	 +	void erase(const LLUUID& key); +	bool fetch(const LLUUID& key, bool refresh=false); +	void insert(LLSD& experience_data); +	bool get(const LLUUID& key, LLSD& experience_data); + +	// If name information is in cache, callback will be called immediately. +	void get(const LLUUID& key, callback_slot_t slot); + +	const cache_t& getCached(); + +	LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false); + +}; + +#endif // LL_LLEXPERIENCECACHE_H diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 89add3e21f..9caff9e038 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -188,6 +188,7 @@ set(viewer_SOURCE_FILES      lleventnotifier.cpp      lleventpoll.cpp      llexpandabletextbox.cpp +	llexperienceassociationresponder.cpp      llexternaleditor.cpp      llface.cpp      llfasttimerview.cpp @@ -226,6 +227,7 @@ set(viewer_SOURCE_FILES      llfloatereditwater.cpp      llfloaterenvironmentsettings.cpp      llfloaterevent.cpp +    llfloaterexperiences.cpp      llfloaterfonttest.cpp      llfloatergesture.cpp      llfloatergodtools.cpp @@ -395,6 +397,7 @@ set(viewer_SOURCE_FILES      llpanelclassified.cpp      llpanelcontents.cpp      llpaneleditwearable.cpp +    llpanelexperiences.cpp      llpanelface.cpp      llpanelgenerictip.cpp      llpanelgroup.cpp @@ -771,6 +774,7 @@ set(viewer_HEADER_FILES      lleventnotifier.h      lleventpoll.h      llexpandabletextbox.h +	llexperienceassociationresponder.h      llexternaleditor.h      llface.h      llfasttimerview.h @@ -809,6 +813,7 @@ set(viewer_HEADER_FILES      llfloatereditwater.h      llfloaterenvironmentsettings.h      llfloaterevent.h +    llfloaterexperiences.h      llfloaterfonttest.h      llfloatergesture.h      llfloatergodtools.h @@ -971,6 +976,7 @@ set(viewer_HEADER_FILES      llpanelclassified.h      llpanelcontents.h      llpaneleditwearable.h +    llpanelexperiences.h      llpanelface.h      llpanelgenerictip.h      llpanelgroup.h diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ffb168b43b..40281f61ec 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -100,6 +100,7 @@  // Linden library includes  #include "llavatarnamecache.h"  #include "lldiriterator.h" +#include "llexperiencecache.h"  #include "llimagej2c.h"  #include "llmemory.h"  #include "llprimitive.h" @@ -4409,7 +4410,7 @@ void LLAppViewer::loadNameCache()  }  void LLAppViewer::saveNameCache() -	{ +{  	// display names cache  	std::string filename =  		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); @@ -4417,7 +4418,7 @@ void LLAppViewer::saveNameCache()  	if(name_cache_stream.is_open())  	{  		LLAvatarNameCache::exportFile(name_cache_stream); -} +	}  	if (!gCacheName) return; @@ -4430,6 +4431,32 @@ void LLAppViewer::saveNameCache()  	}  } + +void LLAppViewer::saveExperienceCache() +{ +	std::string filename = +		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); +	LL_INFOS("ExperienceCache") << "Saving " << filename << LL_ENDL; +	llofstream cache_stream(filename); +	if(cache_stream.is_open()) +	{ +		LLExperienceCache::exportFile(cache_stream); +	} +} + +void LLAppViewer::loadExperienceCache() +{ +	std::string filename = +		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml"); +	LL_INFOS("ExperienceCache") << "Loading " << filename << LL_ENDL; +	llifstream cache_stream(filename); +	if(cache_stream.is_open()) +	{ +		LLExperienceCache::importFile(cache_stream); +	} +} + +  /*!	@brief		This class is an LLFrameTimer that can be created with  				an elapsed time that starts counting up from the given value  				rather than 0.0. @@ -4624,7 +4651,7 @@ void LLAppViewer::idle()  	    // floating throughout the various object lists.  	    //  		idleNameCache(); -     +		idleExperienceCache();  		idleNetwork(); @@ -5044,6 +5071,22 @@ void LLAppViewer::idleNameCache()  	LLAvatarNameCache::idle();  } +void LLAppViewer::idleExperienceCache() +{ +	LLViewerRegion* region = gAgent.getRegion(); +	if (!region) return; +	 +	std::string lookup_url=region->getCapability("GetExperienceInfo");  +	if(!lookup_url.empty() && *lookup_url.rbegin() != '/') +	{ +		lookup_url += '/'; +	} +	 +	LLExperienceCache::setLookupURL(lookup_url); + +	LLExperienceCache::idle(); +} +  //  // Handle messages, and all message related stuff  // @@ -5206,6 +5249,7 @@ void LLAppViewer::disconnectViewer()  	}  	saveNameCache(); +	saveExperienceCache();  	// close inventory interface, close all windows  	LLFloaterInventory::cleanup(); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index cd91ae8b2b..260eb7352e 100755 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -117,6 +117,10 @@ public:      void loadNameCache();      void saveNameCache(); +	void loadExperienceCache(); +	void saveExperienceCache(); + +  	void removeMarkerFile(bool leave_logout_marker = false);      // LLAppViewer testing helpers. @@ -222,6 +226,7 @@ private:      void idle();       void idleShutdown();  	// update avatar SLID and display name caches +	void idleExperienceCache();  	void idleNameCache();      void idleNetwork(); diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 3cf3c739d9..80a80f4298 100755 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -172,21 +172,20 @@ void ll_nvapi_init(NvDRSSessionHandle hSession)  			nvapi_error(status);  			return;  		} + +        // (5) Now we apply (or save) our changes to the system +        status = NvAPI_DRS_SaveSettings(hSession); +        if (status != NVAPI_OK)  +        { +            nvapi_error(status); +            return; +        }  	}  	else if (status != NVAPI_OK)  	{  		nvapi_error(status);  		return;  	} - -	 - -	// (5) Now we apply (or save) our changes to the system -	status = NvAPI_DRS_SaveSettings(hSession); -	if (status != NVAPI_OK)  -	{ -		nvapi_error(status); -	}  }  //#define DEBUGGING_SEH_FILTER 1 diff --git a/indra/newview/llexperienceassociationresponder.cpp b/indra/newview/llexperienceassociationresponder.cpp new file mode 100644 index 0000000000..33ada4906d --- /dev/null +++ b/indra/newview/llexperienceassociationresponder.cpp @@ -0,0 +1,97 @@ +/**  + * @file llexperienceassociationresponder.cpp + * @brief llexperienceassociationresponder implementation. This class combines  + * a lookup for a script association and an experience details request. The first + * is always async, but the second may be cached locally. + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, 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 "llexperienceassociationresponder.h" +#include "llexperiencecache.h" +#include "llviewerregion.h" +#include "llagent.h" + +ExperienceAssociationResponder::ExperienceAssociationResponder(ExperienceAssociationResponder::callback_t callback):mCallback(callback) +{ +    ref(); +} + +void ExperienceAssociationResponder::fetchAssociatedExperience( const LLUUID& object_id, const LLUUID& item_id, callback_t callback ) +{ +    LLSD request; +    request["object-id"]=object_id; +    request["item-id"]=item_id; +    fetchAssociatedExperience(request, callback); +} + +void ExperienceAssociationResponder::fetchAssociatedExperience(LLSD& request, callback_t callback) +{ +    LLViewerRegion* region = gAgent.getRegion(); +    if (region) +    { +        std::string lookup_url=region->getCapability("GetMetadata");  +        if(!lookup_url.empty()) +        { +            LLSD fields; +            fields.append("experience"); +            request["fields"] = fields; +            LLHTTPClient::post(lookup_url, request, new ExperienceAssociationResponder(callback)); +        } +    } +} + +void ExperienceAssociationResponder::error( U32 status, const std::string& reason ) +{ +    LLSD msg; +    msg["error"]=(LLSD::Integer)status; +    msg["message"]=reason; +    LL_INFOS("ExperienceAssociation") << "Failed to look up associated experience: " << status << ": " << reason << LL_ENDL; + +    sendResult(msg); +   +} +void ExperienceAssociationResponder::result( const LLSD& content ) +{ +    if(!content.has("experience")) +    { + +        LLSD msg; +        msg["message"]="no experience"; +        msg["error"]=-1; +        sendResult(msg); +        return; +    } + +    LLExperienceCache::get(content["experience"].asUUID(), boost::bind(&ExperienceAssociationResponder::sendResult, this, _1)); + +} + +void ExperienceAssociationResponder::sendResult( const LLSD& experience ) +{ +    mCallback(experience); +    unref(); +} + + + diff --git a/indra/newview/llexperienceassociationresponder.h b/indra/newview/llexperienceassociationresponder.h new file mode 100644 index 0000000000..8ff62a3dbc --- /dev/null +++ b/indra/newview/llexperienceassociationresponder.h @@ -0,0 +1,58 @@ +#include "llhttpclient.h" +#include "llsd.h" +/**  + * @file llexperienceassociationresponder.h + * @brief llexperienceassociationresponder and related class definitions + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, 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_LLEXPERIENCEASSOCIATIONRESPONDER_H +#define LL_LLEXPERIENCEASSOCIATIONRESPONDER_H + +#include "llhttpclient.h" +#include "llsd.h" + +class ExperienceAssociationResponder : public LLHTTPClient::Responder +{ +public: +    typedef boost::function<void(const LLSD& experience)> callback_t; + +    ExperienceAssociationResponder(callback_t callback); + +    virtual void result(const LLSD& content); +    virtual void error(U32 status, const std::string& reason); + +    static void fetchAssociatedExperience(const LLUUID& object_it, const LLUUID& item_id, callback_t callback); + +private:     +    static void fetchAssociatedExperience(LLSD& request, callback_t callback); +     +    void sendResult(const LLSD& experience); + +    callback_t mCallback; + +}; + +#endif // LL_LLEXPERIENCEASSOCIATIONRESPONDER_H diff --git a/indra/newview/llfloaterexperiences.cpp b/indra/newview/llfloaterexperiences.cpp new file mode 100644 index 0000000000..b862b41bba --- /dev/null +++ b/indra/newview/llfloaterexperiences.cpp @@ -0,0 +1,14 @@ +#include "llviewerprecompiledheaders.h" + +#include "llpanelexperiences.h" +#include "llfloaterexperiences.h" + +LLFloaterExperiences::LLFloaterExperiences(const LLSD& data) +	:LLFloater(data) +{ +} + +BOOL LLFloaterExperiences::postBuild() +{ +	return TRUE; +} diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h new file mode 100644 index 0000000000..1e5f216f8d --- /dev/null +++ b/indra/newview/llfloaterexperiences.h @@ -0,0 +1,45 @@ +/**  + * @file llfloaterexperiences.h + * @brief LLFloaterExperiences class definition + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLFLOATEREXPERIENCES_H +#define LL_LLFLOATEREXPERIENCES_H + +#include "llfloater.h" + +class LLFloaterExperiences : +	public LLFloater +{ +public: +	LLFloaterExperiences(const LLSD& data); + +protected: +	/*virtual*/ BOOL	postBuild(); + +private: + +}; + +#endif //LL_LLFLOATEREXPERIENCES_H diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp new file mode 100644 index 0000000000..617ceef615 --- /dev/null +++ b/indra/newview/llpanelexperiences.cpp @@ -0,0 +1,302 @@ +#include "llviewerprecompiledheaders.h" + + +#include "llpanelprofile.h" +#include "lluictrlfactory.h" +#include "llexperiencecache.h" +#include "llagent.h" + +#include "llpanelexperiences.h" + + +static LLRegisterPanelClassWrapper<LLPanelExperiences> register_experiences_panel("experiences_panel"); + + +LLPanelExperiences::LLPanelExperiences(  ) +	:	mExperiencesList(NULL), +	mExperiencesAccTab(NULL), +	mProfilePanel(NULL), +	mPanelExperienceInfo(NULL), +	mNoExperiences(false) +{ + +} + +void* LLPanelExperiences::create( void* data ) +{ +	return new LLPanelExperiences(); +} + +void ExperienceResult(LLHandle<LLPanelExperiences> panel, const LLSD& experience) +{ +	LLPanelExperiences* experiencePanel = panel.get(); +	if(experiencePanel) +	{ +		experiencePanel->addExperienceInfo(experience); +	} +} + +class LLExperienceListResponder : public LLHTTPClient::Responder +{ +public: +	LLExperienceListResponder(const LLHandle<LLPanelExperiences>& parent):mParent(parent) +	{ +	} + +	LLHandle<LLPanelExperiences> mParent; + +	virtual void result(const LLSD& content) +	{ +		if(mParent.isDead()) +			return; + +		LLSD experiences = content["experiences"]; +		LLSD::array_const_iterator it = experiences.beginArray(); +		for( /**/ ; it != experiences.endArray(); ++it) +		{ +			LLUUID public_key = it->asUUID(); + +			LLExperienceCache::get(public_key, boost::bind(ExperienceResult, mParent, _1)); +		} +	} +}; + +void LLPanelExperiences::addExperienceInfo(const LLSD& experience) +{ +	LLExperienceItem* item = new LLExperienceItem(); +	if(experience.has(LLExperienceCache::NAME)) +	{ +		item->setExperienceName(experience[LLExperienceCache::NAME].asString()); +	} +	else if(experience.has("error")) +	{ +		item->setExperienceName(experience["error"].asString()); +	} + +	if(experience.has(LLExperienceCache::DESCRIPTION)) +	{ +		item->setExperienceDescription(experience[LLExperienceCache::DESCRIPTION].asString()); +	} + +	mExperiencesList->addItem(item); + +} + + +BOOL LLPanelExperiences::postBuild( void ) +{ +	mExperiencesList = getChild<LLFlatListView>("experiences_list"); +	if(hasString("no_experiences")) +	{ +		mExperiencesList->setNoItemsCommentText(getString("no_experiences")); +	} + + +	LLViewerRegion* region = gAgent.getRegion(); +	if (region) +	{ +		std::string lookup_url=region->getCapability("GetExperiences");  +		if(!lookup_url.empty()) +		{ +			LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLPanelExperiences>())); +		} +	} + +	mExperiencesAccTab = getChild<LLAccordionCtrlTab>("tab_experiences"); +	mExperiencesAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelExperiences::onAccordionStateChanged, this, mExperiencesAccTab)); +	mExperiencesAccTab->setDisplayChildren(true); + +	return TRUE; +} + +void LLPanelExperiences::onOpen( const LLSD& key ) +{ +	LLPanel::onOpen(key); +} + +void LLPanelExperiences::onClosePanel() +{ +	if (mPanelExperienceInfo) +	{ +		onPanelExperienceClose(mPanelExperienceInfo); +	} +} + +void LLPanelExperiences::updateData() +{ +	if(isDirty()) +	{ +		mNoExperiences = false; +	} +} + +LLExperienceItem* LLPanelExperiences::getSelectedExperienceItem() +{ +	LLPanel* selected_item = mExperiencesList->getSelectedItem(); +	if (!selected_item) return NULL; + +	return dynamic_cast<LLExperienceItem*>(selected_item); +} + +void LLPanelExperiences::setProfilePanel( LLPanelProfile* profile_panel ) +{ +	mProfilePanel = profile_panel; +} + +void LLPanelExperiences::onListCommit( const LLFlatListView* f_list ) +{ +	if(f_list == mExperiencesList) +	{ +		mExperiencesList->resetSelection(true); +	} +	else +	{ +		llwarns << "Unknown list" << llendl; +	} +	 +	//updateButtons(); +} + +void LLPanelExperiences::onAccordionStateChanged( const LLAccordionCtrlTab* acc_tab ) +{ +	if(!mExperiencesAccTab->getDisplayChildren()) +	{ +		mExperiencesList->resetSelection(true); +	} + +} + +void LLPanelExperiences::openExperienceInfo() +{ +	LLSD selected_value = mExperiencesList->getSelectedValue(); +	if(selected_value.isUndefined()) +	{ +		return; +	} + +	LLExperienceItem* experience = (LLExperienceItem*)mExperiencesList->getSelectedItem(); + +	createExperienceInfoPanel(); + +	LLSD params; +	params["experience_name"] = experience->getExperienceName(); +	params["experience_desc"] = experience->getExperienceDescription(); + +	getProfilePanel()->openPanel(mPanelExperienceInfo, params); + +} + + +void LLPanelExperiences::createExperienceInfoPanel() +{ +	if(!mPanelExperienceInfo) +	{ +		mPanelExperienceInfo = LLPanelExperienceInfo::create(); +		mPanelExperienceInfo->setExitCallback(boost::bind(&LLPanelExperiences::onPanelExperienceClose, this, mPanelExperienceInfo)); +		mPanelExperienceInfo->setVisible(FALSE); +	} +} + +void LLPanelExperiences::onPanelExperienceClose( LLPanel* panel ) +{ +	getProfilePanel()->closePanel(panel); +} + +LLPanelProfile* LLPanelExperiences::getProfilePanel() +{ +	llassert_always(NULL != mProfilePanel); +	 +	return mProfilePanel; +} + + + + + + + + + + + +LLExperienceItem::LLExperienceItem() +{ +	buildFromFile("panel_experience_info.xml"); +} + +void LLExperienceItem::init( LLSD* experience_data ) +{ +	if(experience_data) +	{ +		setExperienceDescription(experience_data->has(LLExperienceCache::DESCRIPTION)?(*experience_data)[LLExperienceCache::DESCRIPTION].asString() : std::string()); +		setExperienceName(experience_data->has(LLExperienceCache::NAME)?(*experience_data)[LLExperienceCache::NAME].asString() : std::string()); +	} +} + +void LLExperienceItem::setExperienceDescription( const std::string& val ) +{ +	mExperienceDescription = val; +	getChild<LLUICtrl>("experience_desc")->setValue(val); +} + +void LLExperienceItem::setExperienceName( const std::string& val ) +{ +	mExperienceName = val; +	getChild<LLUICtrl>("experience_name")->setValue(val); +} + +BOOL LLExperienceItem::postBuild() +{ +	return TRUE; +} + +void LLExperienceItem::update() +{ + +} + +void LLExperienceItem::processProperties( void* data, EAvatarProcessorType type ) +{ + +} + +LLExperienceItem::~LLExperienceItem() +{ + +} + + +void LLPanelExperienceInfo::setExperienceName( const std::string& name ) +{ +	getChild<LLUICtrl>("experience_name")->setValue(name); +} + +void LLPanelExperienceInfo::setExperienceDesc( const std::string& desc ) +{ +	getChild<LLUICtrl>("experience_desc")->setValue(desc); +} + +void LLPanelExperienceInfo::onOpen( const LLSD& key ) +{ +	setExperienceName(key["experience_name"]); +	setExperienceDesc(key["experience_desc"]); + +	/* +	LLAvatarPropertiesProcessor::getInstance()->addObserver( +	getAvatarId(), this); +	LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest( +	getAvatarId(), getPickId()); +	*/ +} + +LLPanelExperienceInfo* LLPanelExperienceInfo::create() +{ +	LLPanelExperienceInfo* panel = new LLPanelExperienceInfo(); +	panel->buildFromFile("panel_experience_info.xml"); +	return panel; +} + +void LLPanelExperienceInfo::setExitCallback( const commit_callback_t& cb ) +{ +	getChild<LLButton>("back_btn")->setClickedCallback(cb); +} diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h new file mode 100644 index 0000000000..1fe3f6ae1d --- /dev/null +++ b/indra/newview/llpanelexperiences.h @@ -0,0 +1,119 @@ +/**  + * @file llpanelpicks.h + * @brief LLPanelPicks and related class definitions + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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_LLPANELEXPERIENCES_H +#define LL_LLPANELEXPERIENCES_H + +#include "llaccordionctrltab.h" +#include "llflatlistview.h" +#include "llpanelavatar.h" + +class LLExperienceItem; +class LLPanelProfile;  + +class LLPanelExperienceInfo +	: public LLPanel +{ +public: +	static LLPanelExperienceInfo* create(); +	 +	void onOpen(const LLSD& key); +	void setExperienceName( const std::string& name ); +	void setExperienceDesc( const std::string& desc ); + + +	virtual void setExitCallback(const commit_callback_t& cb); +}; + + +class LLPanelExperiences +	: public LLPanel /*LLPanelProfileTab*/ +{ +public: +	LLPanelExperiences(); + +	static void* create(void* data); + +	/*virtual*/ BOOL postBuild(void); + +	/*virtual*/ void onOpen(const LLSD& key); + +	/*virtual*/ void onClosePanel(); + +	void updateData(); + +	LLExperienceItem* getSelectedExperienceItem(); + +	void setProfilePanel(LLPanelProfile* profile_panel); +	void addExperienceInfo(const LLSD& experience); +protected: + +	void onListCommit(const LLFlatListView* f_list); +	void onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab); + + +	void openExperienceInfo(); +	void createExperienceInfoPanel(); +	void onPanelExperienceClose(LLPanel* panel); +	LLPanelProfile* getProfilePanel(); +private: +	LLFlatListView* mExperiencesList; +	LLAccordionCtrlTab* mExperiencesAccTab; +	LLPanelProfile* mProfilePanel; +	LLPanelExperienceInfo* mPanelExperienceInfo; +	bool mNoExperiences; +}; + + +class LLExperienceItem  +	: public LLPanel +	//, public LLAvatarPropertiesObserver +{ +public: +	LLExperienceItem(); +	~LLExperienceItem(); + +	void init(LLSD* experience_data); +	/*virtual*/ BOOL postBuild(); +	void update(); + +	/*virtual*/ void processProperties(void* data, EAvatarProcessorType type); + +	void setCreatorID(const LLUUID& val) { mCreatorID = val; } +	void setExperienceDescription(const std::string& val); +	void setExperienceName(const std::string& val); + +	const LLUUID& getCreatorID() const { return mCreatorID; } +	const std::string& getExperienceName() const { return mExperienceName; } +	const std::string& getExperienceDescription() const { return mExperienceDescription; } + +protected: +	LLUUID mCreatorID; + +	std::string mExperienceName; +	std::string mExperienceDescription; +}; +#endif // LL_LLPANELEXPERIENCES_H diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 968a912ea2..f5a6ec3dd3 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -86,6 +86,8 @@  #include "lltrans.h"  #include "llviewercontrol.h"  #include "llappviewer.h" +#include "llexperiencecache.h" +#include "llexperienceassociationresponder.h"  const std::string HELLO_LSL =  	"default\n" @@ -118,6 +120,26 @@ static bool have_script_upload_cap(LLUUID& object_id)  	return object && (! object->getRegion()->getCapability("UpdateScriptTask").empty());  } + +class ExperienceResponder : public LLHTTPClient::Responder +{ +public: +    ExperienceResponder(const LLHandle<LLLiveLSLEditor>& parent):mParent(parent) +    { +    } + +    LLHandle<LLLiveLSLEditor> mParent; + +    virtual void result(const LLSD& content) +    { +        LLLiveLSLEditor* parent = mParent.get(); +        if(!parent) +            return; + +        parent->setExperienceIds(content["experience_ids"]);		 +    } +}; +  /// ---------------------------------------------------------------------------  /// LLLiveLSLFile  /// --------------------------------------------------------------------------- @@ -374,12 +396,47 @@ LLScriptEdCore::~LLScriptEdCore()  	delete mLiveFile;  } +void LLLiveLSLEditor::experienceChanged() +{ +    if(mScriptEd->getAssociatedExperience() != mExperiences->getSelectedValue().asUUID()) +    { +        mScriptEd->enableSave(getIsModifiable()); +        //getChildView("Save_btn")->setEnabled(TRUE); +        mScriptEd->setAssociatedExperience(mExperiences->getSelectedValue().asUUID()); +        updateExperiencePanel(); +    } +} + +void LLLiveLSLEditor::onToggleExperience( LLUICtrl *ui, void* userdata ) +{ +    LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; + +    LLUUID id; +    if(self->mExperienceEnabled->get()) +    { +        if(self->mScriptEd->getAssociatedExperience().isNull()) +        { +            id=self->mExperienceIds.beginArray()->asUUID(); +        } +    } + +    if(id != self->mScriptEd->getAssociatedExperience()) +    { +        self->mScriptEd->enableSave(self->getIsModifiable()); +    } +    self->mScriptEd->setAssociatedExperience(id); + +    self->updateExperiencePanel(); +} +  BOOL LLScriptEdCore::postBuild()  {  	mErrorList = getChild<LLScrollListCtrl>("lsl errors");  	mFunctions = getChild<LLComboBox>( "Insert..."); + +     	childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this);  	mEditor = getChild<LLViewerTextEditor>("Script Editor"); @@ -389,6 +446,8 @@ BOOL LLScriptEdCore::postBuild()  	childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this));  	initMenu(); +	 +  	std::vector<std::string> funcs; @@ -1191,6 +1250,106 @@ bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata)  	return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : FALSE;  } +LLUUID LLScriptEdCore::getAssociatedExperience()const +{ +    return mAssociatedExperience; +} + +void LLLiveLSLEditor::setExperienceIds( const LLSD& experience_ids ) +{ +    mExperienceIds=experience_ids; +    updateExperiencePanel(); +} + + +void LLLiveLSLEditor::updateExperiencePanel() +{ +    BOOL editable = getIsModifiable(); + +    if(mScriptEd->getAssociatedExperience().isNull()) +    { +        mExperienceEnabled->set(FALSE); +        mExperiences->setVisible(FALSE); +        if(mExperienceIds.size()>0) +        { +            mExperienceEnabled->setEnabled(TRUE); +            mExperienceEnabled->setToolTip(getString("add_experiences")); +        } +        else +        { +            mExperienceEnabled->setEnabled(FALSE); +            mExperienceEnabled->setToolTip(getString("no_experiences")); +        } +        getChild<LLButton>("view_profile")->setVisible(FALSE); +    } +    else +    { +        mExperienceEnabled->setToolTip(getString("experience_enabled")); +        mExperienceEnabled->setEnabled(editable); +        mExperienceEnabled->set(TRUE); +        mExperiences->setVisible(TRUE); +        getChild<LLButton>("view_profile")->setVisible(TRUE); +        buildExperienceList(); +    } +} + +void LLLiveLSLEditor::addExperienceInfo(const LLSD& experience, BOOL enabled) +{   +    LLUUID id = experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); +    EAddPosition position = (id == mScriptEd->getAssociatedExperience())?ADD_TOP:ADD_BOTTOM; +    LLScrollListItem* item=mExperiences->add(experience[LLExperienceCache::NAME], id, position ); +    if(!enabled) +    { +        item->setEnabled(FALSE); +    } +} + +void LLLiveLSLEditor::buildExperienceList() +{ +    mExperiences->clear(); +    bool foundAssociated=false; +    for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it) +    { +        LLUUID id = it->asUUID(); +        foundAssociated |= (id == mScriptEd->getAssociatedExperience()); +        LLExperienceCache::get(id, boost::bind(&LLLiveLSLEditor::addExperienceInfo, this, _1, TRUE));   +    } + +    if(!foundAssociated ) +    { +        LLExperienceCache::get(mScriptEd->getAssociatedExperience(), boost::bind(&LLLiveLSLEditor::addExperienceInfo, this, _1, FALSE));   +    } + +} + + +void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) +{ +    mAssociatedExperience = experience_id; +} + + + +void LLLiveLSLEditor::requestExperiences() +{ +    if (!getIsModifiable()) +    { +        return; +    } + +    LLViewerRegion* region = gAgent.getRegion(); +    if (region) +    { +        std::string lookup_url=region->getCapability("GetCreatorExperiences");  +        if(!lookup_url.empty()) +        { +            LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle<LLLiveLSLEditor>())); +        } +    } +} + + +  /// ---------------------------------------------------------------------------  /// LLScriptEdContainer  /// --------------------------------------------------------------------------- @@ -1750,6 +1909,15 @@ BOOL LLLiveLSLEditor::postBuild()  	mScriptEd->mEditor->makePristine();  	mScriptEd->mEditor->setFocus(TRUE); + +    mExperiences = getChild<LLComboBox>("Experiences..."); +    mExperiences->setCommitCallback(boost::bind(&LLLiveLSLEditor::experienceChanged, this)); + +    mExperienceEnabled = getChild<LLCheckBoxCtrl>("enable_xp"); + +    childSetCommitCallback("enable_xp", onToggleExperience, this); +     +  	return LLPreview::postBuild();  } @@ -1798,8 +1966,8 @@ void LLLiveLSLEditor::loadAsset()  				   || gAgent.isGodlike()))  			{  				mItem = new LLViewerInventoryItem(item); -				//llinfos << "asset id " << mItem->getAssetUUID() << llendl;  			} +            ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1));  			if(!gAgent.isGodlike()  			   && (item @@ -1860,7 +2028,7 @@ void LLLiveLSLEditor::loadAsset()  			LLHost host(object->getRegion()->getIP(),  						object->getRegion()->getPort());  			gMessageSystem->sendReliable(host); -			*/ +			*/             		}  	}  	else @@ -1883,6 +2051,8 @@ void LLLiveLSLEditor::loadAsset()  										  time_corrected());  		mAssetStatus = PREVIEW_ASSET_LOADED;  	} + +    requestExperiences();  }  // static @@ -1893,7 +2063,8 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id,  	lldebugs << "LLLiveLSLEditor::onLoadComplete: got uuid " << asset_id  		 << llendl;  	LLUUID* xored_id = (LLUUID*)user_data; -	 + +     	LLLiveLSLEditor* instance = LLFloaterReg::findTypedInstance<LLLiveLSLEditor>("preview_scriptedit", *xored_id);  	if(instance ) @@ -2146,8 +2317,8 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/)  	mPendingUploads++;  	BOOL is_running = getChild<LLCheckBoxCtrl>( "running")->get();  	if (!url.empty()) -	{ -		uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running); +	{		 +		uploadAssetViaCaps(url, filename, mObjectUUID, mItemUUID, is_running, mScriptEd->getAssociatedExperience());  	}  	else if (gAssetStorage)  	{ @@ -2155,11 +2326,7 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/)  	}  } -void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url, -										 const std::string& filename, -										 const LLUUID& task_id, -										 const LLUUID& item_id, -										 BOOL is_running) +void LLLiveLSLEditor::uploadAssetViaCaps( const std::string& url, const std::string& filename, const LLUUID& task_id, const LLUUID& item_id, BOOL is_running, const LLUUID& experience_public_id )  {  	llinfos << "Update Task Inventory via capability " << url << llendl;  	LLSD body; @@ -2167,6 +2334,7 @@ void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url,  	body["item_id"] = item_id;  	body["is_script_running"] = is_running;  	body["target"] = monoChecked() ? "mono" : "lsl2"; +	body["experience"] = experience_public_id;  	LLHTTPClient::post(url, body,  		new LLUpdateTaskInventoryResponder(body, filename, LLAssetType::AT_LSL_TEXT));  } @@ -2418,3 +2586,18 @@ BOOL LLLiveLSLEditor::monoChecked() const  	}  	return FALSE;  } + +void LLLiveLSLEditor::setAssociatedExperience( LLHandle<LLLiveLSLEditor> editor, const LLSD& experience ) +{ +    LLLiveLSLEditor* scriptEd = editor.get(); +    if(scriptEd) +    { +        LLUUID id; +        if(experience.has(LLExperienceCache::EXPERIENCE_ID)) +        { +            id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); +        } +        scriptEd->mScriptEd->setAssociatedExperience(id); +        scriptEd->updateExperiencePanel(); +    } +} diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 7563cecd9d..faeb4a5b8a 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -100,12 +100,14 @@ public:  	static void		onBtnInsertSample(void*);  	static void		onBtnInsertFunction(LLUICtrl*, void*);  	static void		onBtnLoadFromFile(void*); -	static void		onBtnSaveToFile(void*); +    static void		onBtnSaveToFile(void*);  	static bool		enableSaveToFileMenu(void* userdata);  	static bool		enableLoadFromFileMenu(void* userdata); -	virtual bool	hasAccelerators() const { return true; } +    virtual bool	hasAccelerators() const { return true; } +    LLUUID 			getAssociatedExperience()const; +    void            setAssociatedExperience( const LLUUID& experience_id );  private:  	void		onBtnHelp(); @@ -133,8 +135,8 @@ private:  	void			(*mLoadCallback)(void* userdata);  	void			(*mSaveCallback)(void* userdata, BOOL close_after_save);  	void			(*mSearchReplaceCallback) (void* userdata); -	void*			mUserdata; -	LLComboBox		*mFunctions; +    void*			mUserdata; +    LLComboBox		*mFunctions;  	BOOL			mForceClose;  	LLPanel*		mCodePanel;  	LLScrollListCtrl* mErrorList; @@ -146,6 +148,7 @@ private:  	BOOL			mEnableSave;  	BOOL			mHasScriptData;  	LLLiveLSLFile*	mLiveFile; +    LLUUID          mAssociatedExperience;  	LLScriptEdContainer* mContainer; // parent view  }; @@ -227,7 +230,18 @@ public:  	/*virtual*/ BOOL postBuild(); -	void setIsNew() { mIsNew = TRUE; } +    void setIsNew() { mIsNew = TRUE; } + +    static void setAssociatedExperience( LLHandle<LLLiveLSLEditor> editor, const LLSD& experience ); +    static void onToggleExperience(LLUICtrl *ui, void* userdata); +     +    void addExperienceInfo( const LLSD& experience, BOOL enabled ); +    void setExperienceIds(const LLSD& experience_ids); +    void buildExperienceList(); +    void updateExperiencePanel(); +    void requestExperiences(); +    void experienceChanged(); +    void addAssociatedExperience(const LLSD& experience);  private:  	virtual BOOL canClose(); @@ -237,11 +251,7 @@ private:  	virtual void loadAsset();  	void loadAsset(BOOL is_new);  	/*virtual*/ void saveIfNeeded(bool sync = true); -	void uploadAssetViaCaps(const std::string& url, -							const std::string& filename,  -							const LLUUID& task_id, -							const LLUUID& item_id, -							BOOL is_running); +	void uploadAssetViaCaps(const std::string& url, const std::string& filename, const LLUUID& task_id, const LLUUID& item_id, BOOL is_running, const LLUUID& experience_public_id);  	void uploadAssetLegacy(const std::string& filename,  						   LLViewerObject* object,  						   const LLTransactionID& tid, @@ -282,9 +292,14 @@ private:  	S32					mPendingUploads;  	BOOL getIsModifiable() const { return mIsModifiable; } // Evaluated on load assert -	 +  	LLCheckBoxCtrl*	mMonoCheckbox;  	BOOL mIsModifiable; + + +    LLComboBox     	*mExperiences; +    LLCheckBoxCtrl  *mExperienceEnabled; +    LLSD            mExperienceIds;  };  #endif  // LL_LLPREVIEWSCRIPT_H diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index 92c2863ffd..040c4ee72d 100755 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -43,6 +43,8 @@  #include "llviewercontrol.h"  #include "llviewerinventory.h"  #include "llviewerobjectlist.h" +#include "llexperiencecache.h" +#include "llexperienceassociationresponder.h"  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -316,6 +318,15 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  		is_obj_modify = object->permOwnerModify();  	} +    if(item->getInventoryType() == LLInventoryType::IT_LSL) +    { +        getChildView("LabelItemExperienceTitle")->setVisible(TRUE); +        LLTextBox* tb = getChild<LLTextBox>("LabelItemExperience"); +        tb->setText(getString("loading_experience")); +        tb->setVisible(TRUE); +        ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLSidepanelItemInfo::setAssociatedExperience, getDerivedHandle<LLSidepanelItemInfo>(), _1)); +    } +      	//////////////////////  	// ITEM NAME & DESC //  	////////////////////// @@ -666,6 +677,30 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  	}  } + +void LLSidepanelItemInfo::setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience ) +{ +    LLSidepanelItemInfo* info = hInfo.get(); +    if(info) +    { +        LLUUID id; +        if(experience.has(LLExperienceCache::EXPERIENCE_ID) && experience.has(LLExperienceCache::NAME)) +        { +            id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); +        } +        LLTextBox* xpName = info->getChild<LLTextBox>("LabelItemExperience"); +        if(id.isNull()) +        { +            xpName->setText(info->getString("no_experience")); +        } +        else +        { +            xpName->setText(experience[LLExperienceCache::NAME].asString()); +        } +    } +} + +  void LLSidepanelItemInfo::startObjectInventoryObserver()  {  	if (!mObjectInventoryObserver) diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h index 12aaca923e..2e24e58a2a 100755 --- a/indra/newview/llsidepaneliteminfo.h +++ b/indra/newview/llsidepaneliteminfo.h @@ -67,6 +67,8 @@ protected:  	void refreshFromItem(LLViewerInventoryItem* item);  private: +    static void setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience ); +  	void startObjectInventoryObserver();  	void stopObjectInventoryObserver(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index cff3a7e02a..3f8f5e282a 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -48,6 +48,7 @@  #include "llares.h"  #include "llavatarnamecache.h" +#include "llexperiencecache.h"  #include "lllandmark.h"  #include "llcachename.h"  #include "lldir.h" @@ -1401,6 +1402,9 @@ bool idle_startup()  		LLStartUp::initNameCache();  		display_startup(); +		LLStartUp::initExperienceCache(); +		display_startup(); +  		// update the voice settings *after* gCacheName initialization  		// so that we can construct voice UI that relies on the name cache  		LLVoiceClient::getInstance()->updateSettings(); @@ -2814,6 +2818,13 @@ void LLStartUp::initNameCache()  	LLAvatarNameCache::setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames"));  } + +void LLStartUp::initExperienceCache() +{ +	LLAppViewer::instance()->loadExperienceCache(); +	LLExperienceCache::initClass(); +} +  void LLStartUp::cleanupNameCache()  {  	LLAvatarNameCache::cleanupClass(); @@ -3510,3 +3521,4 @@ void transition_back_to_login_panel(const std::string& emsg)  	reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW );  	gSavedSettings.setBOOL("AutoLogin", FALSE);  } + diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 760e38890b..00e03bcda6 100755 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -91,6 +91,7 @@ public:  	static void fontInit();  	static void initNameCache(); +	static void initExperienceCache();  	static void cleanupNameCache(); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index c6b28b9e5e..c6296a20d7 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -58,6 +58,7 @@  #include "llfloatereditsky.h"  #include "llfloatereditwater.h"  #include "llfloaterenvironmentsettings.h" +#include "llfloaterexperiences.h"  #include "llfloaterevent.h"  #include "llfloaterdestinations.h"  #include "llfloaterfonttest.h" @@ -207,7 +208,8 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("env_edit_day_cycle", "floater_edit_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditDayCycle>);  	LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>); -	 +	LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiences>); +  	LLFloaterReg::add("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFontTest>);  	LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGesture>); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 8422708add..adc346529e 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1596,7 +1596,11 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)  	}  	capabilityNames.append("GetDisplayNames"); -	capabilityNames.append("GetMesh"); +	capabilityNames.append("GetExperiences"); +	capabilityNames.append("GetExperienceInfo"); +	capabilityNames.append("GetCreatorExperiences"); +    capabilityNames.append("GetMesh"); +    capabilityNames.append("GetMetadata");  	capabilityNames.append("GetObjectCost");  	capabilityNames.append("GetObjectPhysicsData");  	capabilityNames.append("GetTexture"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 1a050800b4..15d5dd63c7 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -43,6 +43,7 @@  #include "llanimationstates.h"  #include "llavatarnamecache.h"  #include "llavatarpropertiesprocessor.h" +#include "llexperiencecache.h"  #include "llphysicsmotion.h"  #include "llviewercontrol.h"  #include "llcallingcard.h"		// IDEVO for LLAvatarTracker @@ -2037,7 +2038,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)  		idleUpdateBelowWater();	// wind effect uses this  		idleUpdateWindEffect();  	} -	 +		  	idleUpdateNameTag( root_pos_last );  	idleUpdateRenderCost();  } diff --git a/indra/newview/skins/default/xui/en/floater_experiences.xml b/indra/newview/skins/default/xui/en/floater_experiences.xml new file mode 100644 index 0000000000..57541c8b35 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experiences.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> + +<floater +  positioning="cascading" +  can_close="true" +  can_resize="true" +  height="400" +  help_topic="sidebar_experiences" +  min_height="300" +  min_width="300" +  layout="topleft" +  name="floater_experiences" +  save_rect="false" +  single_instance="true" +  reuse_instance="false" +  title="EXPERIENCES" +  width="400"> +  <panel +    top="3" +    left="3" +    layout="topleft" +    right="-3" +    follows="all" +    height="300" +    class="experiences_panel" +    filename="panel_experiences.xml" +    > +  </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml index 5cd7cd196d..d8c2a753a1 100755 --- a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml +++ b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml @@ -1,71 +1,110 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <floater - legacy_header_height="18" - bevel_style="none" - border_style="line" - can_resize="true" - height="580" - layout="topleft" - min_height="271" - min_width="290" - name="script ed float" - help_topic="script_ed_float" - save_rect="true" - title="SCRIPT: NEW SCRIPT" - width="508"> -    <floater.string -     name="not_allowed"> -        You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. -    </floater.string> -    <floater.string -     name="script_running"> -        Running -    </floater.string> -    <floater.string -     name="Title"> -        SCRIPT: [NAME] -    </floater.string> -    <panel -     bevel_style="none" +  legacy_header_height="18" +  bevel_style="none" +  border_style="line" +  can_resize="true" +  height="582" +  layout="topleft" +  min_height="271" +  min_width="328" +  name="script ed float" +  help_topic="script_ed_float" +  save_rect="true" +  title="SCRIPT: NEW SCRIPT" +  width="508"> +  <floater.string +    name="not_allowed"> +    You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. +  </floater.string> +  <floater.string +    name="script_running"> +    Running +  </floater.string> +  <floater.string +    name="Title"> +    SCRIPT: [NAME] +  </floater.string> +  <floater.string +    name="experience_enabled"> +    Uncheck to remove the current experience +  </floater.string> +  <floater.string +    name="no_experiences"> +    You are not authorized for any experiences +  </floater.string> +  <floater.string +    name="add_experiences"> +    Select to add an experience +  </floater.string> +  <panel +    bevel_style="none" -     border_style="line" -     follows="left|top|right|bottom" -     height="522" -     layout="topleft" -     left="10" -     name="script ed panel" -     top="20" -     width="497" /> -    <button -     follows="left|bottom" -     height="23" -     label="Reset" -     label_selected="Reset" -     layout="topleft" -     name="Reset" -     left="10" -     width="85" /> -    <check_box +    border_style="line" +    follows="left|top|right|bottom" +    height="499" +    layout="topleft" +    left="10" +    name="script ed panel" +    top="16" +    width="501" /> +  <button +    follows="left|bottom" +    height="23" +    label="Reset" +    label_selected="Reset" +    layout="topleft" +    name="Reset" +    left="10" +    width="85" /> +  <check_box      left_delta="90"      top_delta="3" -     enabled="false" -     follows="left|bottom" -     font="SansSerif" -     height="18" -     initial_value="true" -     label="Running" -     layout="topleft" -     name="running" -     width="205" /> -    <check_box +    enabled="false" +    follows="left|bottom" +    font="SansSerif" +    height="18" +    initial_value="true" +    label="Running" +    layout="topleft" +    name="running" +    width="205" /> +  <check_box      left_delta="140" -     enabled="true" -     follows="left|bottom" -     font="SansSerif" -     height="18" -     initial_value="true" -     label="Mono" -     layout="topleft" -     name="mono" -     width="100" /> +    enabled="true" +    follows="left|bottom" +    font="SansSerif" +    height="18" +    initial_value="true" +    label="Mono" +    layout="topleft" +    name="mono" +    width="100" /> +  <check_box width="130" +             height="21" +             enabled="false" +             left="9" +             top_pad="10" +             layout="topleft" +             follows="bottom|left" +             label="Use Experience:" +             name="enable_xp"/> +  <combo_box +    label="" +    top_pad="-21" +    left="149" +    right="467" +    layout="topleft" +    follows="left|bottom|right" +    visible="false" +    name="Experiences..."/> +  <button label=">" +          name="view_profile" +          height="23" +          width="23" +          right="496" +          layout="topleft" +          top_pad="-23" +          follows="right" +          visible="false"/>  </floater> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index b01c3067ff..752734d7bd 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -53,6 +53,14 @@           parameter="" />        </menu_item_call>        <menu_item_call +        label="Experiences..." +        name="Experiences" +        shortcut="control|E"> +        <menu_item_call.on_click  +          function="Floater.ToggleOrBringToFront" +          parameter="experiences"/> +      </menu_item_call> +      <menu_item_call         label="Places..."         name="Places">          <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/panel_experience_info.xml b/indra/newview/skins/default/xui/en/panel_experience_info.xml new file mode 100644 index 0000000000..268e7462c1 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experience_info.xml @@ -0,0 +1,377 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel +  background_visible="true" +  follows="all" +  height="570" +  layout="topleft" +  left="5" +  min_height="350" +  top="5" +  width="348" +  name="panel_experience_info"> +  <text +    follows="top|left|right" +    font="SansSerifHugeBold" +    height="26" +    layout="topleft" +    left_pad="4" +    name="title" +    text_color="White" +    top="2" +    value="Experience Profile" +    use_ellipses="true" +    left="3" +    right="-3"/> +  <scroll_container +    color="DkGray2" +    follows="all" +    height="532" +    layout="topleft" +    left="9" +    name="xp_scroll" +    opaque="true" +    top_pad="10" +    width="330"> +    <panel +      bg_alpha_color="DkGray2" +      follows="top|left" +      height="480" +      layout="topleft" +      left="0" +      min_height="480" +      name="scrolling_panel" +      top="0" +      width="315" +      min_width="315"> +      <layout_stack +        follows="all" +        height="480" +        layout="topleft" +        left="0" +        top="0" +        orientation="vertical" +        width="315"> +        <layout_panel +          follows="all" +          height="197" +          layout="topleft" +          left="0" +          top="0" +          auto_resize="false" +          visible="true" +          width="315" +          name="image_panel"> +          <texture_picker +            enabled="false" +            fallback_image="default_land_picture.j2c" +            follows="left|top" +            height="197" +            layout="topleft" +            left="10" +            name="logo" +            top="10" +            width="290" /> +        </layout_panel> +        <layout_panel +          follows="all" +          height="19" +          layout="topleft" +          left="0" +          top="5" +          width="313" +          auto_resize="false" +            > +          <text +            follows="left|top|right" +            font="SansSerifLarge" +            height="14" +            layout="topleft" +            left="10" +            name="experience_title" +            text_color="white" +            top="0" +            use_ellipses="true" +            value="Kyle's Superhero RPG" +            width="288"/> +        </layout_panel> +        <layout_panel +          follows="" +          height="50" +          layout="topleft" +          left="0" +          top="0" +          auto_resize="false" +          width="315" +          name="description panel"> +          <expandable_text +            follows="left|top|right" +            font="SansSerif" +            height="50" +            layout="topleft" +            left="7" +            name="experience_description" +            top="0" +            value="It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. " +            width="293"/> +        </layout_panel> +        <layout_panel +          follows="all" +          height="69" +          layout="topleft" +          left="0" +          top="5" +          width="313" +          visible="true" +          auto_resize="false" +          name="location panel" +            > +          <text +            type="string" +            length="1" +            follows="left|top" +            height="16" +            layout="topleft" +            left="10" +            name="Location" +            width="290"> +            Location: +          </text> +          <text +            type="string" +            length="1" +            follows="left|top|right" +            height="18" +            layout="topleft" +            left="10" +            valign="center" +            name="LocationTextText" +            width="288"> +            someplace +          </text> +          <button +            follows="bottom|left" +            height="23" +            label="Teleport" +            layout="topleft" +            name="teleport_btn" +            width="151" +            left="10"/> +          <button +            follows="bottom|left" +            height="23" +            label="Map" +            layout="topleft" +            name="map_btn" +            top_pad="-23" +            width="101" +            left_pad="5"/> +        </layout_panel> +        <layout_panel +          follows="all" +          height="53" +          layout="topleft" +          left="0" +          top="5" +          width="313" +          visible="true" +          auto_resize="false" +          name="marketplace panel" +           +            > +          <text +            type="string" +            length="1" +            follows="left|top" +            height="16" +            layout="topleft" +            left="10" +            name="Location" +            width="290"> +            Marketplace store: +          </text> +          <text +            type="string" +            length="1" +            follows="left|top|right" +            height="18" +            layout="topleft" +            left="10" +            valign="center" +            name="LocationTextText" +            width="288"> +            someplace +          </text> +        </layout_panel> +        <layout_panel +          follows="left|top|right" +          height="69" +          left="0" +          top="0" +          auto_resize="false" +          width="315" +            > +          <text +            type="string" +            length="1" +            follows="left|top" +            height="16" +            layout="topleft" +            left="10" +            name="ContentRating" +            width="100"> +            Rating: +          </text> +          <text +            type="string" +            length="1" +            follows="left|top|right" +            height="18" +            layout="topleft" +            left_pad="2" +            valign="center" +            name="ContentRatingText" +            top_delta="-2" +            width="188"> +            Adult +          </text> +          <text +            type="string" +            length="1" +            follows="left|top" +            height="16" +            layout="topleft" +            left="10" +            name="Owner" +            width="100"> +            Owner: +          </text> +          <text +            type="string" +            length="1" +            follows="left|top|right" +            height="18" +            layout="topleft" +            left_pad="2" +            valign="center" +            name="OwnerText" +            top_delta="-2" +            width="188"> +            Kyle +          </text> +          <button +            follows="bottom|left" +            height="23" +            label="Edit" +            layout="topleft" +            name="edit_btn" +            top_pad="0" +            width="151" +            left="10"/> +          <!-- +         + +          <expandable_text +            allow_scroll="false" +            bg_visible="false" +            follows="left|top|right" +            h_pad="0" +            height="35" +            width="280" +            layout="topleft" +            font="SansSerifBig" +            font.style="BOLD" +            left="10" +            top_pad="10" +            name="pick_name" +            read_only="false" +            text_color="white" +            v_pad="0" +            use_ellipses="true" +            value="It's an experience" /> + +          <text +            follows="left|top" +            height="16" +            layout="topleft" +            left="10" +            top_pad="10" +            value="Maturity level:" +            width="130" /> +          <icon +            follows="top|left" +            height="16" +            image_name="unknown" +            layout="topleft" +            left_pad="10" +            name="maturity_icon" +            top_pad="-18" +            width="18" /> +          <text +            follows="top|left" +            height="16" +            layout="topleft" +            left_pad="5" +            name="maturity_value" +            top_pad="-14" +            value="unknown" +            width="118" /> +          <panel +            follows="left|top|right" +            name="location_panel" +            left="10" +            width="280" +            visible="false" +            top_pad="10" +            height="42"> +            <text +              follows="left|top|right" +              height="16" +              layout="topleft" +              left="00" +              top="0" +              value="Location:" +              width="280" /> + +            <text +              follows="left|top|right" +              height="16" +              layout="topleft" +              left="0" +              top_pad="10" +              value="Location:" +              width="280" /> +          </panel> + +          <panel +            follows="left|top|right" +            name="location_panel" +            left="10" +            width="280" +            top_pad="10" +            height="42"> +            <text +              follows="left|top|right" +              height="16" +              layout="topleft" +              left="00" +              top="0" +              value="Location:" +              width="280" /> + +            <text +              follows="left|top|right" +              height="16" +              layout="topleft" +              left="0" +              top_pad="10" +              value="Location:" +              width="280" /> +          </panel> + +--> +        </layout_panel> +      </layout_stack> +    </panel> +  </scroll_container> + +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_experiences.xml b/indra/newview/skins/default/xui/en/panel_experiences.xml new file mode 100644 index 0000000000..47a3005aae --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experiences.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + +<panel +  layout="topleft" +  top="3" +  left="3" +  right="-3" +  bottom="-3" +  label="Experiences" +  follows="all"> +  <string +    name="no_experiences" +    value="No experiences."/> + +  <accordion +    fit_parent="true" +    layout="topleft" +    top="0" +    left="3" +    right="-3" +    bottom="-3" +    single_expansion="true" +    follows="all"> +    <accordion_tab +      name="tab_experiences" +      layout="topleft" +      top="0" +      left="0" +      right="-3" +      title="Experiences" +      follows="all"> +      <flat_list_view +        name="experiences_list" +        layout="topleft" +        top="0" +        left="0" +        follows="all"/> +    </accordion_tab> +  </accordion> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index 765b07ed8b..ed99651a78 100755 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -1,207 +1,209 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <panel - bevel_style="none" - border_style="line" - follows="left|top|right|bottom" - height="522" - layout="topleft" - left="0" - name="script panel" - width="497"> -    <panel.string -     name="loading"> -        Loading... -    </panel.string> -    <panel.string -     name="can_not_view"> -        You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. -    </panel.string> -    <panel.string -     name="public_objects_can_not_run"> -        Public Objects cannot run scripts -    </panel.string> -    <panel.string -     name="script_running"> -        Running -    </panel.string> -    <panel.string -     name="Title"> -        Script: [NAME] -    </panel.string> -    <panel.string -     name="external_editor_not_set"> -        Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting. -    </panel.string> -    <menu_bar -     bg_visible="false" -     follows="left|top" -     height="18" -     layout="topleft" -     left="0" -     mouse_opaque="false" -     name="script_menu" -     width="476"> -        <menu -         top="0" -         height="62" -         label="File" -         layout="topleft" -         left="0" -         mouse_opaque="false" -         name="File" -         width="138"> -            <menu_item_call -             label="Save" -             layout="topleft" -             name="Save" /> -          <menu_item_separator -           layout="topleft" /> -          <menu_item_call -           label="Revert All Changes" -           layout="topleft" -           name="Revert All Changes" /> -          <menu_item_separator -           layout="topleft" /> -          <menu_item_call -           label="Load from file..." -           layout="topleft" -           name="LoadFromFile" /> -          <menu_item_call -           label="Save to file..." -           layout="topleft" -           name="SaveToFile" /> -        </menu> -        <menu -         top="0" -         height="198" -         label="Edit" -         layout="topleft" -         mouse_opaque="false" -         name="Edit" -         width="139"> -            <menu_item_call -             enabled="false" -             label="Undo" -             layout="topleft" -             name="Undo" /> -            <menu_item_call -             enabled="false" -             label="Redo" -             layout="topleft" -             name="Redo" /> -            <menu_item_separator -             layout="topleft" /> -            <menu_item_call -             enabled="false" -             label="Cut" -             layout="topleft" -             name="Cut" /> -            <menu_item_call -             enabled="false" -             label="Copy" -             layout="topleft" -             name="Copy" /> -            <menu_item_call -             enabled="false" -             label="Paste" -             layout="topleft" -             name="Paste" /> -            <menu_item_separator -             layout="topleft" -             name="separator2" /> -            <menu_item_call -             label="Select All" -             layout="topleft" -             name="Select All" /> -            <menu_item_call -             enabled="false" -             label="Deselect" -             layout="topleft" -             name="Deselect" /> -            <menu_item_separator -             layout="topleft" -             name="separator3" /> -            <menu_item_call -             label="Search / Replace..." -             layout="topleft" -             name="Search / Replace..." /> -        </menu> -        <menu -         top="0" -         height="34" -         label="Help" -         layout="topleft" -         mouse_opaque="false" -         name="Help" -         width="112"> -            <menu_item_call -             label="Help..." -             layout="topleft" -             name="Help..." /> -            <menu_item_call -             label="Keyword Help..." -             layout="topleft" -             name="Keyword Help..." /> -        </menu> -    </menu_bar> -    <text_editor +  bevel_style="none" +  border_style="line" +  follows="left|top|right|bottom" +  height="515" +  layout="topleft" +  left="0" +  name="script panel" +  width="497"> +  <panel.string +    name="loading"> +    Loading... +  </panel.string> +  <panel.string +    name="can_not_view"> +    You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. +  </panel.string> +  <panel.string +    name="public_objects_can_not_run"> +    Public Objects cannot run scripts +  </panel.string> +  <panel.string +    name="script_running"> +    Running +  </panel.string> +  <panel.string +    name="Title"> +    Script: [NAME] +  </panel.string> +  <panel.string +    name="external_editor_not_set"> +    Select an editor by setting the environment variable LL_SCRIPT_EDITOR or the ExternalEditor setting. +  </panel.string> +  <menu_bar +    bg_visible="false" +    follows="left|top" +    height="18" +    layout="topleft"      left="0" -     type="string" -     length="1" -     follows="left|top|right|bottom" -     font="Monospace" -     height="376" -     ignore_tab="false" -     layout="topleft" -     max_length="65536" -     name="Script Editor" -     text_readonly_color="DkGray" -     width="487" -     show_line_numbers="true"  -     enable_tooltip_paste="true" -     word_wrap="true"> -        Loading... -    </text_editor> -    <scroll_list +    mouse_opaque="false" +    name="script_menu" +    width="476"> +    <menu +      top="0" +      height="62" +      label="File" +      layout="topleft" +      left="0" +      mouse_opaque="false" +      name="File" +      width="138"> +      <menu_item_call +        label="Save" +        layout="topleft" +        name="Save" /> +      <menu_item_separator +        layout="topleft" /> +      <menu_item_call +        label="Revert All Changes" +        layout="topleft" +        name="Revert All Changes" /> +      <menu_item_separator +        layout="topleft" /> +      <menu_item_call +        label="Load from file..." +        layout="topleft" +        name="LoadFromFile" /> +      <menu_item_call +        label="Save to file..." +        layout="topleft" +        name="SaveToFile" /> +    </menu> +    <menu +      top="0" +      height="198" +      label="Edit" +      layout="topleft" +      mouse_opaque="false" +      name="Edit" +      width="139"> +      <menu_item_call +        enabled="false" +        label="Undo" +        layout="topleft" +        name="Undo" /> +      <menu_item_call +        enabled="false" +        label="Redo" +        layout="topleft" +        name="Redo" /> +      <menu_item_separator +        layout="topleft" /> +      <menu_item_call +        enabled="false" +        label="Cut" +        layout="topleft" +        name="Cut" /> +      <menu_item_call +        enabled="false" +        label="Copy" +        layout="topleft" +        name="Copy" /> +      <menu_item_call +        enabled="false" +        label="Paste" +        layout="topleft" +        name="Paste" /> +      <menu_item_separator +        layout="topleft" +        name="separator2" /> +      <menu_item_call +        label="Select All" +        layout="topleft" +        name="Select All" /> +      <menu_item_call +        enabled="false" +        label="Deselect" +        layout="topleft" +        name="Deselect" /> +      <menu_item_separator +        layout="topleft" +        name="separator3" /> +      <menu_item_call +        label="Search / Replace..." +        layout="topleft" +        name="Search / Replace..." /> +    </menu> +    <menu +      top="0" +      height="34" +      label="Help" +      layout="topleft" +      mouse_opaque="false" +      name="Help" +      width="112"> +      <menu_item_call +        label="Help..." +        layout="topleft" +        name="Help..." /> +      <menu_item_call +        label="Keyword Help..." +        layout="topleft" +        name="Keyword Help..." /> +    </menu> +  </menu_bar> +  <text_editor +    left="0" +    type="string" +    length="1" +    follows="left|top|right|bottom" +    font="Monospace" +    height="369" +    ignore_tab="false" +    layout="topleft" +    max_length="65536" +    name="Script Editor" +    text_readonly_color="DkGray" +    width="482" +    show_line_numbers="true" +    enable_tooltip_paste="true" +    word_wrap="true"> +    Loading... +  </text_editor> +  <scroll_list      top_pad="10"      left="0" -     follows="left|right|bottom" -     height="60" -     layout="topleft" -     name="lsl errors" -     width="487" /> -    <text -     follows="left|bottom" -     height="12" -     layout="topleft" -     left="0" -     name="line_col" -     width="128" /> -    <combo_box -     follows="left|bottom" -     height="23" -     label="Insert..." -     layout="topleft" -     name="Insert..." -     width="128" /> -    <button -     follows="right|bottom" -     height="23" -     label="Save" -     label_selected="Save" -     layout="topleft" -     top_pad="-35" -     right="487" -     name="Save_btn" -     width="81" /> -    <button -     enabled="false" -     follows="right|bottom" -     height="23" -     label="Edit..." -     layout="topleft" -     top_pad="-23" -     right="400" -     name="Edit_btn" -     width="81" /> +    follows="left|right|bottom" +    height="60" +    layout="topleft" +    name="lsl errors" +    width="482" /> +  <text +    follows="left|bottom" +    height="12" +    layout="topleft" +    left="11" +    name="line_col" +    width="128" /> +  <combo_box +    follows="left|bottom" +    height="23" +    label="Insert..." +    layout="topleft" +    name="Insert..." +    width="128" +    left="0"/> +  <button +    follows="right|bottom" +    height="23" +    label="Save" +    label_selected="Save" +    layout="topleft" +    top_pad="-35" +    right="482" +    name="Save_btn" +    width="81" /> +  <button +    enabled="false" +    follows="right|bottom" +    height="23" +    label="Edit..." +    layout="topleft" +    top_pad="-23" +    right="396" +    name="Edit_btn" +    width="81" /> +  </panel> diff --git a/indra/newview/skins/default/xui/en/panel_script_experience.xml b/indra/newview/skins/default/xui/en/panel_script_experience.xml new file mode 100644 index 0000000000..e798638751 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_script_experience.xml @@ -0,0 +1,97 @@ +<panel +  name="script_experience" +  title="EXPERIENCE" +  width="400" +  follows="top|left|right" +  top="0" +  left="0" +  layout="topleft"> +  <!-- <floater.string name="EXPERIENCE">EXPERIENCE!!!!</floater.string> +  +  <floater.string name="Script:">Script:</floater.string> +  <floater.string name="Associated with:">Associated with:</floater.string> +  <floater.string name="You can contribute:">You can contribute:</floater.string> +  <floater.string name="Associate with:">Associate with:</floater.string> +  <floater.string name="Yes">Yes</floater.string> +  <floater.string name="No">No</floater.string> +  <floater.string name="(none)"></floater.string> +  <floater.string name="Choose Experience...">Choose Experience...</floater.string> +  <floater.string name="You are not a contributor to any experiences.">You are not a contributor to any experiences.</floater.string> +  --> +   +  <button name="Expand Experience" width="200" +          height="35" +          is_toggle="true" +          tab_stop="false" +          pad_left="35" +          top="0" +          left="3" +          label="Experience" +          halign="left" +          handle_right_mouse="false" +          follows="top|left|right" +          image_unselected="MarketplaceBtn_Off" +          image_selected="MarketplaceBtn_Selected"> +  </button> +  <check_box +	  follows="top|right" height="25" label="Uses Experience" left="215" width="0" top="0" name="enable_xp" +	  /> +  <layout_stack +    follows="top|left|right" +     +    width="384" +    height="140" +    name="xp_details" +    left="4" +    top="45" +    orientation="horizontal" +    layout="topleft" +    visible="false"> +    <layout_panel  width="120" +                   height="140"> +      <text > +        Script: +      </text> +      <text bottom_delta="25"> +        Associated with: +      </text> +      <text bottom_delta="25"> +        You can contribute: +      </text> +      <text bottom_delta="25"> +        Associate with: +      </text> +    </layout_panel> + +    <layout_panel width="250" +                  height="140"> +      <text > +        EasySit Animator 1.2.4 +      </text> +      <text bottom_delta="25" +           text_color="HTMLLinkColor" font.style="UNDERLINE"> +        Kyle's Superhero RPG +      </text> +      <text bottom_delta="25"> +        Yes +      </text> +      <combo_box left="0" bottom_delta="33" +                 label="Choose Experience..." +                 name="Experiences..." +                 follows="top|left|right" +        /> +    </layout_panel> +  </layout_stack> +  <text +    follows="top|left|right" +	  width="400" +	  height="15" +	  bottom_delta="-12" +	  left="0" +    halign="center" +    name="No Experiences" +    visible="false" +    text_color="AlertCautionTextColor"> +    You are not a contributor to any experiences. +  </text> +</panel> diff --git a/indra/newview/skins/default/xui/en/role_actions.xml b/indra/newview/skins/default/xui/en/role_actions.xml index 0eeccbeac5..9e429234d3 100755 --- a/indra/newview/skins/default/xui/en/role_actions.xml +++ b/indra/newview/skins/default/xui/en/role_actions.xml @@ -187,4 +187,16 @@  		     longdescription="Members in a Role with this Ability can control access and participation in group voice and text chat sessions."  		     name="moderate group chat" value="37" />  	</action_set> +  <action_set +    description="These Abilities include power to modify experiences owned by this group." +    name="experience_tools_experience"> +    <action description="Experience Admin" +            longdescription="Members in a role with this ability can edit the meta-data for an experience." +            name="experience admin" +            value ="49" /> +    <action description="Experience Creator" +            longdescription="Members in a role with this ability can create scripts for an experience." +            name="experience creator" +            value ="50" /> +  </action_set>  </role_actions> diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml index c5dfb703e5..f1f8843a9d 100755 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -1,440 +1,477 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <panel -     follows="all" -	 height="570" -	 layout="topleft" -	 name="item properties" -	 help_topic="item_properties" -	 title="Item Profile" -	 width="333"> -	<panel.string -		 name="unknown"> -        (unknown) -	</panel.string> -    <panel.string -         name="unknown_multiple"> -        (unknown / multiple) -    </panel.string> -	<panel.string -		 name="public"> -        (public) -    </panel.string> -	<panel.string -    	 name="you_can"> -        You can: -    </panel.string> -	<panel.string -    	 name="owner_can"> -        Owner can: -    </panel.string> -	<panel.string -    	 name="acquiredDate"> -        [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] -	</panel.string> -	<panel.string -		 name="origin_inventory"> -        (Inventory) -	</panel.string> -	<panel.string -		 name="origin_inworld"> -        (Inworld) -	</panel.string> -	<icon -     	 follows="top|right" -     	 height="18" -     	 image_name="Lock" -	     layout="topleft" -		 right="-15" -	     mouse_opaque="true" -	     name="IconLocked" -	     top="8" -	     width="18" /> -    <button -     follows="top|left" -     height="24" -     image_hover_unselected="BackButton_Over" -     image_pressed="BackButton_Press" -     image_unselected="BackButton_Off" -     layout="topleft" -     left="12" -     name="back_btn" -     tab_stop="false" -     top="2" -     width="30" -     use_draw_context_alpha="false" /> -    <text -     follows="top|left|right" -     font="SansSerifHugeBold" -     height="26" -     layout="topleft" -     left_pad="3" -     name="title" -     text_color="LtGray" -     top="2" -     use_ellipses="true" -     value="Item Profile" -     width="275" /> -    	    <text -     follows="top|left|right" -     height="13" -     layout="topleft" -     left="45" -     name="origin" -     text_color="LtGray_50" -     use_ellipses="true" -     value="(Inventory)" -     width="275" /> -    <scroll_container -     color="DkGray2" -     follows="all" -     layout="topleft" -     left="9" -     name="item_profile_scroll" -     opaque="true" -     height="493" -     width="313" -     top="45"> -        <panel -             follows="left|top|right" -             height="390" -             help_topic="" -             label="" -             layout="topleft" -             left="0" -             name="item_profile" -             top="0" -             width="295"> -            <text -                 type="string" -                 length="1" -                 follows="left|top" -                 height="10" -                 layout="topleft" -                 left="5" -                 name="LabelItemNameTitle" -                 top="10" -                 width="78"> -                Name: -            </text> -            <line_editor -                 border_style="line" -                 border_thickness="1" -                 follows="left|top|right" -                 height="20" -                 layout="topleft" -                 left_delta="78" -                 max_length_bytes="63" -                 name="LabelItemName" -                 top_delta="0" -                 width="210" /> -            <text -                 type="string" -                 length="1" -                 follows="left|top" -                 height="10" -                 layout="topleft" -                 left="5" -                 name="LabelItemDescTitle" -                 top_pad="10" -                 width="78"> -                Description: -            </text> -            <line_editor -                 border_style="line" -                 border_thickness="1" -                 follows="left|top|right" -                 height="23" -                 layout="topleft" -                 left_delta="78" -                 max_length_bytes="127" -                 name="LabelItemDesc" -                 top_delta="-5" -                 width="210" /> -            <text -                 type="string" -                 length="1" -                 follows="left|top" -                 height="23" -                 layout="topleft" -                 left="5" -                 name="LabelCreatorTitle" -              top_pad="10" -                 width="78"> -                Creator: -            </text> -                <avatar_icon -         follows="top|left" -         height="20" -         default_icon_name="Generic_Person" -         layout="topleft" -         left_pad="0" -         top_delta="-6" -         mouse_opaque="true" -         width="20" /> -            <text -                 type="string" -         follows="left|right|top" -         font="SansSerifSmall" -         height="15" -         layout="topleft" -         left_pad="5" -                 name="LabelCreatorName" -       top_delta="6" -       use_ellipses="true" -       width="165"> -          </text> -          <button -          follows="top|right" -          height="16" +  follows="all" +  height="570" +  layout="topleft" +  name="item properties" +  help_topic="item_properties" +  title="Item Profile" +  width="333"> +  <panel.string +    name="no_experience"> +    (none) +  </panel.string> +  <panel.string +    name="loading_experience"> +    (loading) +  </panel.string> +  <panel.string +    name="unknown"> +    (unknown) +  </panel.string> +  <panel.string +    name="unknown_multiple"> +    (unknown / multiple) +  </panel.string> +  <panel.string +    name="public"> +    (public) +  </panel.string> +  <panel.string +    name="you_can"> +    You can: +  </panel.string> +  <panel.string +    name="owner_can"> +    Owner can: +  </panel.string> +  <panel.string +    name="acquiredDate"> +    [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] +  </panel.string> +  <panel.string +    name="origin_inventory"> +    (Inventory) +  </panel.string> +  <panel.string +    name="origin_inworld"> +    (Inworld) +  </panel.string> +  <icon +    follows="top|right" +    height="18" +    image_name="Lock" +    layout="topleft" +    right="-15" +    mouse_opaque="true" +    name="IconLocked" +    top="8" +    width="18" /> +  <button +    follows="top|left" +    height="24" +    image_hover_unselected="BackButton_Over" +    image_pressed="BackButton_Press" +    image_unselected="BackButton_Off" +    layout="topleft" +    left="12" +    name="back_btn" +    tab_stop="false" +    top="2" +    width="30" +    use_draw_context_alpha="false" /> +  <text +    follows="top|left|right" +    font="SansSerifHugeBold" +    height="26" +    layout="topleft" +    left_pad="3" +    name="title" +    text_color="LtGray" +    top="2" +    use_ellipses="true" +    value="Item Profile" +    width="275" /> +  <text +    follows="top|left|right" +    height="13" +    layout="topleft" +    left="45" +    name="origin" +    text_color="LtGray_50" +    use_ellipses="true" +    value="(Inventory)" +    width="275" /> +  <scroll_container +    color="DkGray2" +    follows="all" +    layout="topleft" +    left="9" +    name="item_profile_scroll" +    opaque="true" +    height="493" +    width="313" +    top="45"> +    <panel +      follows="left|top|right" +      height="390" +      help_topic="" +      label="" +      layout="topleft" +      left="0" +      name="item_profile" +      top="0" +      width="295"> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="10" +        layout="topleft" +        left="5" +        name="LabelItemNameTitle" +        top="10" +        width="78"> +        Name: +      </text> +      <line_editor +        border_style="line" +        border_thickness="1" +        follows="left|top|right" +        height="20" +        layout="topleft" +        left_delta="78" +        max_length_bytes="63" +        name="LabelItemName" +        top_delta="0" +        width="210" /> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="10" +        layout="topleft" +        left="5" +        name="LabelItemDescTitle" +        top_pad="10" +        width="78"> +        Description: +      </text> +      <line_editor +        border_style="line" +        border_thickness="1" +        follows="left|top|right" +        height="23" +        layout="topleft" +        left_delta="78" +        max_length_bytes="127" +        name="LabelItemDesc" +        top_delta="-5" +        width="210" /> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="23" +        layout="topleft" +        left="5" +        name="LabelCreatorTitle" +        top_pad="10" +        width="78"> +        Creator: +      </text> +      <avatar_icon +        follows="top|left" +        height="20" +        default_icon_name="Generic_Person" +        layout="topleft" +        left_pad="0" +        top_delta="-6" +        mouse_opaque="true" +        width="20" /> +      <text +        type="string" +        follows="left|right|top" +        font="SansSerifSmall" +        height="15" +        layout="topleft" +        left_pad="5" +        name="LabelCreatorName" +        top_delta="6" +        use_ellipses="true" +        width="165"> +      </text> +      <button +        follows="top|right" +        height="16" +        image_selected="Inspector_I" +        image_unselected="Inspector_I" +        layout="topleft" +        right="-5" +        name="BtnCreator" +        top_delta="-6" +        width="16" /> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="23" +        layout="topleft" +        left="5" +        name="LabelOwnerTitle" +        top_pad="10" +        width="78"> +        Owner: +      </text> +      <avatar_icon +        follows="top|left" +        height="20" +        default_icon_name="Generic_Person" +        layout="topleft" +        left_pad="0" +        top_delta="-6" +        mouse_opaque="true" +        width="20" /> +      <text +        type="string" +        follows="left|right|top" +        font="SansSerifSmall" +        height="15" +        layout="topleft" +        left_pad="5" +        name="LabelOwnerName" +        top_delta="6" +        use_ellipses="true" +        width="165"> +      </text> +      <button +        follows="top|right" +        height="16"          image_selected="Inspector_I"          image_unselected="Inspector_I" +        layout="topleft" +        right="-5" +        name="BtnOwner" +        top_delta="-3" +        width="16" /> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="23" +        layout="topleft" +        left="5" +        name="LabelAcquiredTitle" +        top_pad="10" +        width="78"> +        Acquired: +      </text> +      <text +        type="string" +        length="1" +        follows="left|top|right" +        height="23" +        layout="topleft" +        left_delta="78" +        name="LabelAcquiredDate" +        top_delta="0" +        width="210"> +      </text> +      <panel +        border="false" +        follows="left|top|right" +        layout="topleft" +        mouse_opaque="false" +        name="perms_inv" +        left="0" +        top_pad="25" +        height="155" +        width="313"> +        <text +          type="string" +          length="1" +          left="10" +          top_pad="13" +          text_color="EmphasisColor" +          height="15" +          follows="left|top|right" +          layout="topleft" +          name="perm_modify" +          width="200"> +          You can: +        </text> +        <check_box +          height="18" +          label="Modify" +          layout="topleft" +          left="20" +          name="CheckOwnerModify" +          top_pad="0" +          width="90" /> +        <check_box +          height="18" +          label="Copy" +          layout="topleft" +          left_pad="0" +          name="CheckOwnerCopy" +          width="90" /> +        <check_box +          height="18" +          label="Transfer" +          layout="topleft" +          left_pad="0" +          name="CheckOwnerTransfer" +          width="106" /> +        <text +          type="string" +          length="1" +          follows="left|top" +          height="16" +          layout="topleft" +          left="10" +          name="AnyoneLabel" +          top_pad="8" +          width="100"> +          Anyone: +        </text> +        <check_box +          height="18" +          label="Copy"            layout="topleft" -          right="-5" -          name="BtnCreator" -          top_delta="-6" -          width="16" /> -          <text +          left_pad="0" +          name="CheckEveryoneCopy" +          top_delta="-2" +          width="150" /> +        <text            type="string"            length="1"            follows="left|top" -          height="23" +          height="16"            layout="topleft" -            left="5" -          name="LabelOwnerTitle" -            top_pad="10" -          width="78"> -            Owner: -          </text> -          <avatar_icon -            follows="top|left" -            height="20" -            default_icon_name="Generic_Person" -            layout="topleft" -            left_pad="0" -            top_delta="-6" -            mouse_opaque="true" -            width="20" /> -          <text +          left="10" +          name="GroupLabel" +          top_pad="8" +          width="100"> +          Group: +        </text> +        <check_box +          height="18" +          label="Share" +          layout="topleft" +          left_pad="0" +          top_delta="-2" +          name="CheckShareWithGroup" +          tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions." +          width="150" /> +        <text            type="string" -            follows="left|right|top" -            font="SansSerifSmall" -            height="15" -            layout="topleft" -            left_pad="5" -          name="LabelOwnerName" -          top_delta="6" -       use_ellipses="true" -			 width="165"> -          </text> -             <button -                 follows="top|right" -                 height="16" -         image_selected="Inspector_I" -         image_unselected="Inspector_I" -                 layout="topleft" -                 right="-5" -                 name="BtnOwner" -                 top_delta="-3" -                 width="16" /> -             <text -                 type="string" -                 length="1" -                 follows="left|top" -                 height="23" -                 layout="topleft" -                 left="5" -                 name="LabelAcquiredTitle" -    top_pad="10" -                 width="78"> -                Acquired: -             </text> -             <text -                 type="string" -                 length="1" -                 follows="left|top|right" -                 height="23" -                 layout="topleft" -                 left_delta="78" -                 name="LabelAcquiredDate" -                 top_delta="0" -                 width="210"> -          </text> -         <panel -             border="false" -             follows="left|top|right" -             layout="topleft" -             mouse_opaque="false" -             name="perms_inv" -             left="0" -             top_pad="25" -             height="155" -             width="313"> -          <text -                 type="string" -                 length="1" -                 left="10" -                 top_pad="13" -                 text_color="EmphasisColor" -             height="15" -                 follows="left|top|right" -                 layout="topleft" -                 name="perm_modify" -                 width="200"> -                    You can: -                </text> -            <check_box -                 height="18" -                 label="Modify" -                 layout="topleft" -                 left="20" -                 name="CheckOwnerModify" -                 top_pad="0" -                 width="90" /> -            <check_box -                 height="18" -                 label="Copy" -                 layout="topleft" -                 left_pad="0" -                 name="CheckOwnerCopy" -                 width="90" /> -            <check_box -                 height="18" -                 label="Transfer" -                 layout="topleft" -                 left_pad="0" -                 name="CheckOwnerTransfer" -                 width="106" /> -            <text -                 type="string" -                 length="1" -                 follows="left|top" -                 height="16" -                 layout="topleft" -                 left="10" -                 name="AnyoneLabel" -                 top_pad="8" -                 width="100"> -                Anyone: -            </text> -            <check_box -                 height="18" -                 label="Copy" -                 layout="topleft" -                 left_pad="0" -                 name="CheckEveryoneCopy" -                 top_delta="-2" -                 width="150" /> -            <text -                 type="string" -                 length="1" -                 follows="left|top" -                 height="16" -                 layout="topleft" -                 left="10" -                 name="GroupLabel" -                 top_pad="8" -                 width="100"> -                Group: -            </text> -            <check_box -                 height="18" -                 label="Share" -                 layout="topleft" -                 left_pad="0" -                 top_delta="-2" -                 name="CheckShareWithGroup" -                 tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions." -                 width="150" /> -            <text -                 type="string" -                 length="1" -                 follows="left|top" -                 height="16" -                 layout="topleft" -                 left="10" -                 name="NextOwnerLabel" -                 top_pad="8" -                 width="200" -                 word_wrap="true"> -                Next owner: -            </text> -            <check_box -                 height="18" -                 label="Modify" -                 layout="topleft" -                 left="20" -                 top_pad="0" -                 name="CheckNextOwnerModify" -                 width="90" /> -            <check_box -                 height="18" -                 label="Copy" -                 layout="topleft" -                 left_pad="0" -                 name="CheckNextOwnerCopy" -                 width="90" /> -            <check_box -                 height="18" -                 label="Transfer" -                 layout="topleft" -                 left_pad="0" -                 name="CheckNextOwnerTransfer" -                 tool_tip="Next owner can give away or resell this object" -                 width="106" /> -            </panel> +          length="1" +          follows="left|top" +          height="16" +          layout="topleft" +          left="10" +          name="NextOwnerLabel" +          top_pad="8" +          width="200" +          word_wrap="true"> +          Next owner: +        </text>          <check_box -                 height="18" -                 label="For Sale" -                 layout="topleft" -                 left="20" -                 name="CheckPurchase" -                 top_pad="20" -                 width="100" /> -            <combo_box -                 height="23" -                 left_pad="0" -                 layout="topleft" -                 follows="left|top" -                 name="combobox sale copy" -                 width="170"> -                <combo_box.item -                     label="Copy" -                     name="Copy" -                     value="Copy" /> -                <combo_box.item -                     label="Original" -                     name="Original" -                     value="Original" /> -            </combo_box> -            <spinner -                    follows="left|top" -                    decimal_digits="0" -                    increment="1" -                    control_name="Edit Cost" -                    name="Edit Cost" -                    label="Price: L$" -                    label_width="75" -                    left="120" -                    width="170" -                    min_val="0" -                    height="23" -                    max_val="999999999" -                    top_pad="10"/> -        </panel> -    </scroll_container> -    <panel -		 height="30" -		 layout="topleft" -		 name="button_panel" -		 left="5" -		 top_pad="0" -		 width="313"> -	    <button -		     height="23" -		     label="Cancel" -		     layout="topleft" -		     name="cancel_btn" -		     right="-1" -		     width="100" /> -	    </panel> -	</panel> +          height="18" +          label="Modify" +          layout="topleft" +          left="20" +          top_pad="0" +          name="CheckNextOwnerModify" +          width="90" /> +        <check_box +          height="18" +          label="Copy" +          layout="topleft" +          left_pad="0" +          name="CheckNextOwnerCopy" +          width="90" /> +        <check_box +          height="18" +          label="Transfer" +          layout="topleft" +          left_pad="0" +          name="CheckNextOwnerTransfer" +          tool_tip="Next owner can give away or resell this object" +          width="106" /> +      </panel> +      <check_box +        height="18" +        label="For Sale" +        layout="topleft" +        left="20" +        name="CheckPurchase" +        top_pad="20" +        width="100" /> +      <combo_box +        height="23" +        left_pad="0" +        layout="topleft" +        follows="left|top" +        name="combobox sale copy" +        width="170"> +        <combo_box.item +          label="Copy" +          name="Copy" +          value="Copy" /> +        <combo_box.item +          label="Original" +          name="Original" +          value="Original" /> +      </combo_box> +      <spinner +        follows="left|top" +        decimal_digits="0" +        increment="1" +        control_name="Edit Cost" +        name="Edit Cost" +        label="Price: L$" +        label_width="75" +        left="120" +        width="170" +        min_val="0" +        height="23" +        max_val="999999999" +        top_pad="10"/> + +      <text +        type="string" +        length="1" +        follows="left|top" +        height="10" +        layout="topleft" +        left="5" +        name="LabelItemExperienceTitle" +        top_pad="10" +        width="78" +        visible="false"> +        Experience: +      </text> +      <text +        type="string" +        length="1" +        follows="left|top|right" +        height="10" +        layout="topleft" +        left_delta="78" +        name="LabelItemExperience" +        top_delta="0" +        width="210" +        visible="false" +        ></text> +    </panel> + +  </scroll_container> +  <panel +    height="30" +    layout="topleft" +    name="button_panel" +    left="5" +    top_pad="0" +    width="313" +    follows="top|right|left"> +    <button +      follows="top|right" +      height="23" +      label="Cancel" +      layout="topleft" +      name="cancel_btn" +      right="-1" +      width="100" /> +  </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index d964ccaf79..baeab47a4e 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3919,6 +3919,10 @@ Try enclosing path to the editor with double quotes.    <!-- Spell check settings floater -->    <string name="UserDictionary">[User]</string> +  <!-- Experience Tools strings --> +  <string name="experience_tools_experience">Experience</string> + +    <!-- Conversation log messages -->    <string name="logging_calls_disabled_log_empty">      Conversations are not being logged. To begin keeping a log, choose "Save: Log only" or "Save: Log and transcripts" under Preferences > Chat. diff --git a/indra/tools/vstool/DispatchUtility.cs b/indra/tools/vstool/DispatchUtility.cs new file mode 100644 index 0000000000..6056ac55a1 --- /dev/null +++ b/indra/tools/vstool/DispatchUtility.cs @@ -0,0 +1,271 @@ +#region Using Directives
 +
 +using System;
 +using System.Collections.Generic;
 +using System.Text;
 +using System.Runtime.InteropServices;
 +using System.Reflection;
 +using System.Security.Permissions;
 +
 +#endregion
 +
 +namespace TestDispatchUtility
 +{
 +	/// <summary>
 +	/// Provides helper methods for working with COM IDispatch objects that have a registered type library.
 +	/// </summary>
 +	public static class DispatchUtility
 +	{
 +		#region Private Constants
 +
 +		private const int S_OK = 0; //From WinError.h
 +		private const int LOCALE_SYSTEM_DEFAULT = 2 << 10; //From WinNT.h == 2048 == 0x800
 +
 +		#endregion
 +
 +		#region Public Methods
 +
 +		/// <summary>
 +		/// Gets whether the specified object implements IDispatch.
 +		/// </summary>
 +		/// <param name="obj">An object to check.</param>
 +		/// <returns>True if the object implements IDispatch.  False otherwise.</returns>
 +		public static bool ImplementsIDispatch(object obj)
 +		{
 +			bool result = obj is IDispatchInfo;
 +			return result;
 +		}
 +
 +		/// <summary>
 +		/// Gets a Type that can be used with reflection.
 +		/// </summary>
 +		/// <param name="obj">An object that implements IDispatch.</param>
 +		/// <param name="throwIfNotFound">Whether an exception should be thrown if a Type can't be obtained.</param>
 +		/// <returns>A .NET Type that can be used with reflection.</returns>
 +		/// <exception cref="InvalidCastException">If <paramref name="obj"/> doesn't implement IDispatch.</exception>
 +		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
 +		public static Type GetType(object obj, bool throwIfNotFound)
 +		{
 +			RequireReference(obj, "obj");
 +			Type result = GetType((IDispatchInfo)obj, throwIfNotFound);
 +			return result;
 +		}
 +
 +		/// <summary>
 +		/// Tries to get the DISPID for the requested member name.
 +		/// </summary>
 +		/// <param name="obj">An object that implements IDispatch.</param>
 +		/// <param name="name">The name of a member to lookup.</param>
 +		/// <param name="dispId">If the method returns true, this holds the DISPID on output.
 +		/// If the method returns false, this value should be ignored.</param>
 +		/// <returns>True if the member was found and resolved to a DISPID.  False otherwise.</returns>
 +		/// <exception cref="InvalidCastException">If <paramref name="obj"/> doesn't implement IDispatch.</exception>
 +		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
 +		public static bool TryGetDispId(object obj, string name, out int dispId)
 +		{
 +			RequireReference(obj, "obj");
 +			bool result = TryGetDispId((IDispatchInfo)obj, name, out dispId);
 +			return result;
 +		}
 +
 +		/// <summary>
 +		/// Invokes a member by DISPID.
 +		/// </summary>
 +		/// <param name="obj">An object that implements IDispatch.</param>
 +		/// <param name="dispId">The DISPID of a member.  This can be obtained using
 +		/// <see cref="TryGetDispId(object, string, out int)"/>.</param>
 +		/// <param name="args">The arguments to pass to the member.</param>
 +		/// <returns>The member's return value.</returns>
 +		/// <remarks>
 +		/// This can invoke a method or a property get accessor.
 +		/// </remarks>
 +		public static object Invoke(object obj, int dispId, object[] args)
 +		{
 +			string memberName = "[DispId=" + dispId + "]";
 +			object result = Invoke(obj, memberName, args);
 +			return result;
 +		}
 +
 +		/// <summary>
 +		/// Invokes a member by name.
 +		/// </summary>
 +		/// <param name="obj">An object.</param>
 +		/// <param name="memberName">The name of the member to invoke.</param>
 +		/// <param name="args">The arguments to pass to the member.</param>
 +		/// <returns>The member's return value.</returns>
 +		/// <remarks>
 +		/// This can invoke a method or a property get accessor.
 +		/// </remarks>
 +		public static object Invoke(object obj, string memberName, object[] args)
 +		{
 +			RequireReference(obj, "obj");
 +			Type type = obj.GetType();
 +			object result = type.InvokeMember(memberName, BindingFlags.InvokeMethod | BindingFlags.GetProperty,
 +				null, obj, args, null);
 +			return result;
 +		}
 +
 +		#endregion
 +
 +		#region Private Methods
 +
 +		/// <summary>
 +		/// Requires that the value is non-null.
 +		/// </summary>
 +		/// <typeparam name="T">The type of the value.</typeparam>
 +		/// <param name="value">The value to check.</param>
 +		/// <param name="name">The name of the value.</param>
 +		private static void RequireReference<T>(T value, string name) where T : class
 +		{
 +			if (value == null)
 +			{
 +				throw new ArgumentNullException(name);
 +			}
 +		}
 +
 +		/// <summary>
 +		/// Gets a Type that can be used with reflection.
 +		/// </summary>
 +		/// <param name="dispatch">An object that implements IDispatch.</param>
 +		/// <param name="throwIfNotFound">Whether an exception should be thrown if a Type can't be obtained.</param>
 +		/// <returns>A .NET Type that can be used with reflection.</returns>
 +		private static Type GetType(IDispatchInfo dispatch, bool throwIfNotFound)
 +		{
 +			RequireReference(dispatch, "dispatch");
 +
 +			Type result = null;
 +			int typeInfoCount;
 +			int hr = dispatch.GetTypeInfoCount(out typeInfoCount);
 +			if (hr == S_OK && typeInfoCount > 0)
 +			{
 +				// Type info isn't usually culture-aware for IDispatch, so we might as well pass
 +				// the default locale instead of looking up the current thread's LCID each time
 +				// (via CultureInfo.CurrentCulture.LCID).
 +				dispatch.GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, out result);
 +			}
 +
 +			if (result == null && throwIfNotFound)
 +			{
 +				// If the GetTypeInfoCount called failed, throw an exception for that.
 +				Marshal.ThrowExceptionForHR(hr);
 +
 +				// Otherwise, throw the same exception that Type.GetType would throw.
 +				throw new TypeLoadException();
 +			}
 +
 +			return result;
 +		}
 +
 +		/// <summary>
 +		/// Tries to get the DISPID for the requested member name.
 +		/// </summary>
 +		/// <param name="dispatch">An object that implements IDispatch.</param>
 +		/// <param name="name">The name of a member to lookup.</param>
 +		/// <param name="dispId">If the method returns true, this holds the DISPID on output.
 +		/// If the method returns false, this value should be ignored.</param>
 +		/// <returns>True if the member was found and resolved to a DISPID.  False otherwise.</returns>
 +		private static bool TryGetDispId(IDispatchInfo dispatch, string name, out int dispId)
 +		{
 +			RequireReference(dispatch, "dispatch");
 +			RequireReference(name, "name");
 +
 +			bool result = false;
 +
 +			// Members names aren't usually culture-aware for IDispatch, so we might as well
 +			// pass the default locale instead of looking up the current thread's LCID each time
 +			// (via CultureInfo.CurrentCulture.LCID).
 +			Guid iidNull = Guid.Empty;
 +			int hr = dispatch.GetDispId(ref iidNull, ref name, 1, LOCALE_SYSTEM_DEFAULT, out dispId);
 +
 +			const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); //From WinError.h
 +			const int DISPID_UNKNOWN = -1; //From OAIdl.idl
 +			if (hr == S_OK)
 +			{
 +				result = true;
 +			}
 +			else if (hr == DISP_E_UNKNOWNNAME && dispId == DISPID_UNKNOWN)
 +			{
 +				// This is the only supported "error" case because it means IDispatch
 +				// is saying it doesn't know the member we asked about.
 +				result = false;
 +			}
 +			else
 +			{
 +				// The other documented result codes are all errors.
 +				Marshal.ThrowExceptionForHR(hr);
 +			}
 +
 +			return result;
 +		}
 +
 +		#endregion
 +
 +		#region Private Interfaces
 +
 +		/// <summary>
 +		/// A partial declaration of IDispatch used to lookup Type information and DISPIDs.
 +		/// </summary>
 +		/// <remarks>
 +		/// This interface only declares the first three methods of IDispatch.  It omits the
 +		/// fourth method (Invoke) because there are already plenty of ways to do dynamic
 +		/// invocation in .NET.  But the first three methods provide dynamic type metadata
 +		/// discovery, which .NET doesn't provide normally if you have a System.__ComObject
 +		/// RCW instead of a strongly-typed RCW.
 +		/// <para/>
 +		/// Note: The original declaration of IDispatch is in OAIdl.idl.
 +		/// </remarks>
 +		[ComImport]
 +		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 +		[Guid("00020400-0000-0000-C000-000000000046")]
 +		private interface IDispatchInfo
 +		{
 +			/// <summary>
 +			/// Gets the number of Types that the object provides (0 or 1).
 +			/// </summary>
 +			/// <param name="typeInfoCount">Returns 0 or 1 for the number of Types provided by <see cref="GetTypeInfo"/>.</param>
 +			/// <remarks>
 +			/// http://msdn.microsoft.com/en-us/library/da876d53-cb8a-465c-a43e-c0eb272e2a12(VS.85)
 +			/// </remarks>
 +			[PreserveSig]
 +			int GetTypeInfoCount(out int typeInfoCount);
 +
 +			/// <summary>
 +			/// Gets the Type information for an object if <see cref="GetTypeInfoCount"/> returned 1.
 +			/// </summary>
 +			/// <param name="typeInfoIndex">Must be 0.</param>
 +			/// <param name="lcid">Typically, LOCALE_SYSTEM_DEFAULT (2048).</param>
 +			/// <param name="typeInfo">Returns the object's Type information.</param>
 +			/// <remarks>
 +			/// http://msdn.microsoft.com/en-us/library/cc1ec9aa-6c40-4e70-819c-a7c6dd6b8c99(VS.85)
 +			/// </remarks>
 +			void GetTypeInfo(int typeInfoIndex, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler,
 +				MarshalTypeRef = typeof(System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler))] out Type typeInfo);
 +
 +			/// <summary>
 +			/// Gets the DISPID of the specified member name.
 +			/// </summary>
 +			/// <param name="riid">Must be IID_NULL.  Pass a copy of Guid.Empty.</param>
 +			/// <param name="name">The name of the member to look up.</param>
 +			/// <param name="nameCount">Must be 1.</param>
 +			/// <param name="lcid">Typically, LOCALE_SYSTEM_DEFAULT (2048).</param>
 +			/// <param name="dispId">If a member with the requested <paramref name="name"/>
 +			/// is found, this returns its DISPID and the method's return value is 0.
 +			/// If the method returns a non-zero value, then this parameter's output value is
 +			/// undefined.</param>
 +			/// <returns>Zero for success. Non-zero for failure.</returns>
 +			/// <remarks>
 +			/// http://msdn.microsoft.com/en-us/library/6f6cf233-3481-436e-8d6a-51f93bf91619(VS.85)
 +			/// </remarks>
 +			[PreserveSig]
 +			int GetDispId(ref Guid riid, ref string name, int nameCount, int lcid, out int dispId);
 +
 +			// NOTE: The real IDispatch also has an Invoke method next, but we don't need it.
 +			// We can invoke methods using .NET's Type.InvokeMember method with the special
 +			// [DISPID=n] syntax for member "names", or we can get a .NET Type using GetTypeInfo
 +			// and invoke methods on that through reflection.
 +			// Type.InvokeMember: http://msdn.microsoft.com/en-us/library/de3dhzwy.aspx
 +		}
 +
 +		#endregion
 +	}
 +}
 diff --git a/indra/tools/vstool/app.config b/indra/tools/vstool/app.config new file mode 100644 index 0000000000..8494f728ff --- /dev/null +++ b/indra/tools/vstool/app.config @@ -0,0 +1,3 @@ +<?xml version="1.0"?>
 +<configuration>
 +<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
 | 
