diff options
Diffstat (limited to 'indra')
67 files changed, 8088 insertions, 793 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..86121667b7 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..c8deaac1ef --- /dev/null +++ b/indra/llmessage/llexperiencecache.cpp @@ -0,0 +1,647 @@ +/**  + * @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> KeyMap; +    KeyMap privateToPublicKeyMap; + +    void mapKeys(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()); +		} + +		//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) +	{ +        mapKeys(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_DEBUGS("ExperienceCache") << "importFile() 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_DEBUGS("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[MISSING]=true; +                exp[QUOTA] = DEFAULT_QUOTA; + +				processExperience(id, exp); +				LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL ; +			} + +			LL_DEBUGS("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 = get(it->first); +                //leave the properties alone if we already have a cache entry for this xp +                if(exp.isUndefined()) +                { +                    exp[PROPERTIES]=PROPERTY_INVALID; +                } +				exp[EXPIRES]=retry_timestamp; +				exp[EXPERIENCE_ID] = it->first; +				exp["key_type"] = it->second; +				exp["uuid"] = it->first; +				exp["error"] = (LLSD::Integer)status; +                exp[QUOTA] = DEFAULT_QUOTA; + + 				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; +		const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH; + +		std::ostringstream ostr; + +		ask_queue_t keys; + +		ostr << sLookupURL << "?page_size=" << PAGE_SIZE; + + +		int request_count = 0; +		while(!sAskQueue.empty() && request_count < sMaximumLookups) +		{ +			ask_queue_t::iterator it = sAskQueue.begin(); +			const LLUUID& key = it->first; +			const std::string& key_type = it->second; + +			ostr << '&' << key_type << '=' << key.asString() ; +		 +			keys[key]=key_type; +			request_count++; + +			sPendingQueue[key] = now; +			 +			if(ostr.tellp() > EXP_URL_SEND_THRESHOLD) +			{ +				LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query: " << ostr.str() << LL_ENDL; +				LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); +				ostr.clear(); +				ostr.str(sLookupURL); +				ostr << "?page_size=" << PAGE_SIZE; +				keys.clear(); +			} +			sAskQueue.erase(it); +		} + +		if(ostr.tellp() > sLookupURL.size()) +		{ +			LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query 2: " << ostr.str() << LL_ENDL; +			LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); +		} +	} + +	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)) +				{ +                    LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ; +                    sCache.erase(cur); +					} +                else +                { +                    LLUUID id = exp[EXPERIENCE_ID].asUUID(); +                    LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null; +                    if(private_key.notNull() || !exp.has("DoesNotExist")) +					{ +						fetch(id, true); +					} +					else +					{ +                        LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ; +						sCache.erase(cur); +					} +				} +			} +		} +	} + +	 +	bool fetch( const LLUUID& key, bool refresh/* = true*/ )  +	{ +		if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end())) +		{ +			LL_DEBUGS("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)) +		{ +            processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data); +		} +		else +		{ +			LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; +		} +	} +	static LLSD empty; +	const LLSD& get(const LLUUID& key) +	{ +		if(key.isNull()) return empty; +		cache_t::const_iterator it = sCache.find(key); + +		if (it != sCache.end()) +		{ +			return it->second; +		} + +		fetch(key); + +		return empty; +	} +	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::mapKeys( 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)) +		{ +            privateToPublicKeyMap[(*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; + +    KeyMap::const_iterator it=privateToPublicKeyMap.find(private_key); +    if(it == privateToPublicKeyMap.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..e669ee888e --- /dev/null +++ b/indra/llmessage/llexperiencecache.h @@ -0,0 +1,104 @@ +/**  + * @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 MISSING       = "DoesNotExist"; + +    const std::string AGENT_ID      = "agent_id"; +    const std::string GROUP_ID      = "group_id"; +	const std::string EXPERIENCE_ID	= "public_id"; +	const std::string NAME			= "name"; +	const std::string PROPERTIES	= "properties"; +	const std::string EXPIRES		= "expiration";   +    const std::string DESCRIPTION	= "description"; +    const std::string QUOTA         = "quota"; +    const std::string MATURITY      = "maturity"; +    const std::string METADATA      = "extended_metadata"; +    const std::string SLURL         = "slurl"; + + +	// 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; + + +	// default values +	const static F64 DEFAULT_EXPIRATION = 600.0; +	const static S32 DEFAULT_QUOTA = 128; // this is megabytes + +	// 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(const LLSD& experience_data); +	const LLSD& get(const LLUUID& key); + +	// If name information is in cache, callback will be called immediately. +	void get(const LLUUID& key, callback_slot_t slot); + +	const cache_t& getCached(); + +	// maps an experience private key to the experience id +	LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false); + +}; + +#endif // LL_LLEXPERIENCECACHE_H diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 39cfb6019e..e0f69dc2d3 100755 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1383,3 +1383,5 @@ char const* const _PREHASH_GroupAVSounds = LLMessageStringTable::getInstance()->  char const* const _PREHASH_AppearanceData = LLMessageStringTable::getInstance()->getString("AppearanceData");  char const* const _PREHASH_AppearanceVersion = LLMessageStringTable::getInstance()->getString("AppearanceVersion");  char const* const _PREHASH_CofVersion = LLMessageStringTable::getInstance()->getString("CofVersion"); +char const* const _PREHASH_Experience = LLMessageStringTable::getInstance()->getString("Experience"); +char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->getString("ExperienceID"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 573e10dc0b..fb8dcc17fb 100755 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1383,4 +1383,6 @@ extern char const* const _PREHASH_GroupAVSounds;  extern char const* const _PREHASH_AppearanceData;  extern char const* const _PREHASH_AppearanceVersion;  extern char const* const _PREHASH_CofVersion; +extern char const* const _PREHASH_Experience; +extern char const* const _PREHASH_ExperienceID;  #endif diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 2b9286f663..129cb7b4e6 100755 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -2073,3 +2073,8 @@ void LLTabContainer::commitHoveredButton(S32 x, S32 y)  		}  	}  } + +S32 LLTabContainer::getTotalTabWidth() const +{ +    return mTotalTabWidth; +} diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 57862fc626..057809dc42 100755 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -182,7 +182,8 @@ public:  	LLPanel*	getPanelByIndex(S32 index);  	S32			getIndexForPanel(LLPanel* panel);  	S32			getPanelIndexByTitle(const std::string& title); -	LLPanel*	getPanelByName(const std::string& name); +    LLPanel*	getPanelByName(const std::string& name); +    S32         getTotalTabWidth() const;  	void		setCurrentTabName(const std::string& name);  	void		selectFirstTab(); @@ -287,7 +288,7 @@ private:  	S32								mMaxTabWidth;  	S32								mTotalTabWidth; -	S32								mTabHeight; +    S32								mTabHeight;  	// Padding under the text labels of tab buttons  	S32								mLabelPadBottom; diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 840f67968d..0af2cd42aa 100755 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -37,6 +37,7 @@  #include "lltrans.h"  #include "lluicolortable.h"  #include "message.h" +#include "llexperiencecache.h"  #define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" @@ -1201,3 +1202,50 @@ std::string LLUrlEntryIcon::getIcon(const std::string &url)  	LLStringUtil::trim(mIcon);  	return mIcon;  } + +LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile() +{ +    mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/\\w+\\S*", +        boost::regex::perl|boost::regex::icase); +    mIcon = "Generic_Experience"; +} + +std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const LLUrlLabelCallback &cb ) +{ +    if (!gCacheName) +    { +        // probably at the login screen, use short string for layout +        return LLTrans::getString("LoadingData"); +    } + +    std::string experience_id_string = getIDStringFromUrl(url); +    if (experience_id_string.empty()) +    { +        // something went wrong, just give raw url +        return unescapeUrl(url); +    } + +    LLUUID experience_id(experience_id_string); +    if (experience_id.isNull()) +    { +        return LLTrans::getString("ExperienceNameNull"); +    } + +    const LLSD& experience_details = LLExperienceCache::get(experience_id); +    if(!experience_details.isUndefined()) +    { +        return experience_details[LLExperienceCache::NAME].asString(); +    } + +    addObserver(experience_id_string, url, cb); +    LLExperienceCache::get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1)); +    return LLTrans::getString("LoadingData"); + +} + +void LLUrlEntryExperienceProfile::onExperienceDetails( const LLSD& experience_details ) +{ +    callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), experience_details[LLExperienceCache::NAME].asString(), LLStringUtil::null); +} + + diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 8c6c32178a..48b4a1d531 100755 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -258,6 +258,20 @@ private:  };  /// +/// LLUrlEntryExperienceProfile Describes a Second Life experience profile Url, e.g., +/// secondlife:///app/experience/0e346d8b-4433-4d66-a6b0-fd37083abc4c/profile +/// that displays the experience name +class LLUrlEntryExperienceProfile : public LLUrlEntryBase +{ +public: +    LLUrlEntryExperienceProfile(); +    /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +private: +    void onExperienceDetails(const LLSD& experience_details); +}; + + +///  /// LLUrlEntryGroup Describes a Second Life group Url, e.g.,  /// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about  /// diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 523ee5d78c..480f05bd72 100755 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -60,6 +60,7 @@ LLUrlRegistry::LLUrlRegistry()  	registerUrl(new LLUrlEntryPlace());  	registerUrl(new LLUrlEntryInventory());  	registerUrl(new LLUrlEntryObjectIM()); +    registerUrl(new LLUrlEntryExperienceProfile());  	//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,   	//so it should be registered in the end of list  	registerUrl(new LLUrlEntrySL()); diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index c3f0e92cb0..15f2354552 100755 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -32,9 +32,23 @@  #include "lltut.h"  #include "../lluicolortable.h"  #include "../llrender/lluiimage.h" +#include "../llmessage/llexperiencecache.h"  #include <boost/regex.hpp> + +namespace LLExperienceCache +{ +    const LLSD& get( const LLUUID& key) +    { +		static LLSD boo; +        return boo; +    } + +    void get( const LLUUID& key, callback_slot_t slot ){} + +} +  typedef std::map<std::string, LLControlGroup*> settings_map_t;  settings_map_t LLUI::sSettingGroups; diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l index 88dfc2e9f3..643d14ae43 100755 --- a/indra/lscript/lscript_compile/indra.l +++ b/indra/lscript/lscript_compile/indra.l @@ -737,6 +737,23 @@ int yyerror(const char *fmt, ...);  "STATUS_INTERNAL_ERROR"              { count(); yylval.ival = LSL_STATUS_INTERNAL_ERROR; return(INTEGER_CONSTANT); }  "STATUS_WHITELIST_FAILED"            { count(); yylval.ival = LSL_STATUS_WHITELIST_FAILED; return(INTEGER_CONSTANT); } +"XP_ERROR_NONE"                      { const char* sval= "no error";                                yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_THROTTLED"                 { const char* sval= "exceeded throttle";                       yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_EXPERIENCES_DISABLED"      { const char* sval= "experiences are disabled";                yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_INVALID_PARAMETERS"        { const char* sval= "invalid parameters";                      yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_NOT_PERMITTED"             { const char* sval= "operation not permitted";                 yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_NO_EXPERIENCE"             { const char* sval= "script not associated with an experience";yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_NOT_FOUND"                 { const char* sval= "not found";                               yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_INVALID_EXPERIENCE"        { const char* sval= "invalid experience";                      yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_EXPERIENCE_DISABLED"       { const char* sval= "experience is disabled";                  yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_EXPERIENCE_SUSPENDED"      { const char* sval= "experience is suspended";                 yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_UNKNOWN_ERROR"             { const char* sval= "unknown error";                           yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_QUOTA_EXCEEDED"            { const char* sval= "experience data quota exceeded";          yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_STORE_DISABLED"            { const char* sval= "key-value store is disabled";             yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_STORAGE_EXCEPTION"         { const char* sval= "key-value store communication failed";    yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_KEY_NOT_FOUND"             { const char* sval= "key doesn't exist";                       yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +"XP_ERROR_RETRY_UPDATE"              { const char* sval= "retry update";                            yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); } +  {L}({L}|{N})*		{ count(); yylval.sval = new char[strlen(yytext) + 1]; strcpy(yylval.sval, yytext); return(IDENTIFIER); }  {N}+{E}					{ count(); yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 17e340d136..3a18133193 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -189,6 +189,8 @@ set(viewer_SOURCE_FILES      lleventnotifier.cpp      lleventpoll.cpp      llexpandabletextbox.cpp +    llexperienceassociationresponder.cpp +    llexperiencelog.cpp      llexternaleditor.cpp      llface.cpp      llfacebookconnect.cpp @@ -228,6 +230,9 @@ set(viewer_SOURCE_FILES      llfloatereditwater.cpp      llfloaterenvironmentsettings.cpp      llfloaterevent.cpp +    llfloaterexperiencepicker.cpp +    llfloaterexperienceprofile.cpp +    llfloaterexperiences.cpp      llfloaterfonttest.cpp      llfloatergesture.cpp      llfloatergodtools.cpp @@ -400,6 +405,9 @@ set(viewer_SOURCE_FILES      llpanelclassified.cpp      llpanelcontents.cpp      llpaneleditwearable.cpp +    llpanelexperiencelisteditor.cpp +    llpanelexperiencelog.cpp +    llpanelexperiences.cpp      llpanelface.cpp      llpanelgenerictip.cpp      llpanelgroup.cpp @@ -778,6 +786,8 @@ set(viewer_HEADER_FILES      lleventnotifier.h      lleventpoll.h      llexpandabletextbox.h +    llexperienceassociationresponder.h +    llexperiencelog.h      llexternaleditor.h      llface.h      llfacebookconnect.h @@ -817,6 +827,9 @@ set(viewer_HEADER_FILES      llfloatereditwater.h      llfloaterenvironmentsettings.h      llfloaterevent.h +    llfloaterexperiencepicker.h +    llfloaterexperienceprofile.h +    llfloaterexperiences.h      llfloaterfonttest.h      llfloatergesture.h      llfloatergodtools.h @@ -982,6 +995,9 @@ set(viewer_HEADER_FILES      llpanelclassified.h      llpanelcontents.h      llpaneleditwearable.h +    llpanelexperiencelisteditor.h +    llpanelexperiencelog.h +    llpanelexperiences.h      llpanelface.h      llpanelgenerictip.h      llpanelgroup.h diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini index ad843bca14..3bcb4d666d 100755 --- a/indra/newview/app_settings/keywords.ini +++ b/indra/newview/app_settings/keywords.ini @@ -52,6 +52,7 @@ remote_data     remote_data(integer event_type, key channel, key message_id, str  http_response   http_response(key request_id, integer status, list metadata, string body):Triggered when task receives a response to one of its llHTTPRequests  http_request	http_request(key id, string method, string body):Triggered when task receives an http request against a public URL +  # integer constants  [word .1, .1, .5]  TRUE			Integer constant for Boolean operations @@ -713,6 +714,25 @@ TEXTURE_TRANSPARENT		UUID for the "White - Transparent" texture  URL_REQUEST_GRANTED		Used with http_request when a public URL is successfully granted  URL_REQUEST_DENIED		Used with http_request when a public URL is not available +XP_ERROR_NONE					No error was detected +XP_ERROR_THROTTLED				The call failed due to too many recent calls. +XP_ERROR_EXPERIENCES_DISABLED	The region currently has experiences disabled. +XP_ERROR_INVALID_PARAMETERS		One of the string arguments was too big to fit in the key-value store. +XP_ERROR_NOT_PERMITTED			This experience is not allowed to run on the current region. +XP_ERROR_NO_EXPERIENCE			This script is not associated with an experience. +XP_ERROR_NOT_FOUND				The sim was unable to verify the validity of the experience. Retrying after a short wait is advised. +XP_ERROR_INVALID_EXPERIENCE		The script is associated with an experience that no longer exists. +XP_ERROR_EXPERIENCE_DISABLED	The experience owner has temporarily disabled the experience. +XP_ERROR_EXPERIENCE_SUSPENDED	The experience has been suspended by Linden Customer Support. +XP_ERROR_QUOTA_EXCEEDED			An attempted write data to the key-value store failed due to the data quota being met. +XP_ERROR_STORE_DISABLED			The key-value store is currently disabled on this region. +XP_ERROR_STORAGE_EXCEPTION		Unable to communicate with the key-value store. +XP_ERROR_KEY_NOT_FOUND			The requested key does not exist. +XP_ERROR_RETRY_UPDATE			A checked update failed due to an out of date request. +XP_ERROR_MATURITY_EXCEEDED		The request failed due to agent content preferences. +XP_ERROR_UNKNOWN_ERROR			Other unknown error. + +  # float constants  [word .3, .1, .5]  PI					3.1415926535897932384626433832795 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e3c89f1a5f..68ce15ef0e 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -103,6 +103,7 @@  // Linden library includes  #include "llavatarnamecache.h"  #include "lldiriterator.h" +#include "llexperiencecache.h"  #include "llimagej2c.h"  #include "llmemory.h"  #include "llprimitive.h" @@ -4600,7 +4601,7 @@ void LLAppViewer::loadNameCache()  }  void LLAppViewer::saveNameCache() -	{ +{  	// display names cache  	std::string filename =  		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); @@ -4608,7 +4609,7 @@ void LLAppViewer::saveNameCache()  	if(name_cache_stream.is_open())  	{  		LLAvatarNameCache::exportFile(name_cache_stream); -} +	}  	if (!gCacheName) return; @@ -4621,6 +4622,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. @@ -4815,7 +4842,7 @@ void LLAppViewer::idle()  	    // floating throughout the various object lists.  	    //  		idleNameCache(); -     +		idleExperienceCache();  		idleNetwork(); @@ -5238,6 +5265,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  // @@ -5400,6 +5443,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 05326c2baf..7f33bf58ac 100755 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -123,6 +123,10 @@ public:      void loadNameCache();      void saveNameCache(); +	void loadExperienceCache(); +	void saveExperienceCache(); + +  	void removeMarkerFile(bool leave_logout_marker = false);      // LLAppViewer testing helpers. @@ -228,6 +232,7 @@ private:      void idle();       void idleShutdown();  	// update avatar SLID and display name caches +	void idleExperienceCache();  	void idleNameCache();      void idleNetwork(); 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/llexperiencelog.cpp b/indra/newview/llexperiencelog.cpp new file mode 100644 index 0000000000..5f9fab306b --- /dev/null +++ b/indra/newview/llexperiencelog.cpp @@ -0,0 +1,273 @@ +/**  + * @file llexperiencelog.cpp + * @brief llexperiencelog implementation + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llexperiencelog.h" + +#include "lldispatcher.h" +#include "llsdserialize.h" +#include "llviewergenericmessage.h" +#include "llnotificationsutil.h" +#include "lltrans.h" +#include "llerror.h" +#include "lldate.h" + + +class LLExperienceLogDispatchHandler : public LLDispatchHandler +{ +public: +	virtual bool operator()( +		const LLDispatcher* dispatcher, +		const std::string& key, +		const LLUUID& invoice, +		const sparam_t& strings) +	{ +		LLSD message; + +		sparam_t::const_iterator it = strings.begin(); +		if(it != strings.end()){ +			const std::string& llsdRaw = *it++; +			std::istringstream llsdData(llsdRaw); +			if (!LLSDSerialize::deserialize(message, llsdData, llsdRaw.length())) +			{ +				llwarns << "LLExperienceLogDispatchHandler: Attempted to read parameter data into LLSD but failed:" << llsdRaw << llendl; +			} +		} +		message["public_id"] = invoice; + +		// Object Name +		if(it != strings.end()) +		{ +			message["ObjectName"] = *it++; +		} + +		// parcel Name +		if(it != strings.end()) +		{ +			message["ParcelName"] = *it++; +		} +		message["Count"] = 1; + +		LLExperienceLog::instance().handleExperienceMessage(message); +		return true; +	} +}; + +static LLExperienceLogDispatchHandler experience_log_dispatch_handler; + +void LLExperienceLog::handleExperienceMessage(LLSD& message) +{ +	time_t now; +	time(&now); +	char daybuf[16];/* Flawfinder: ignore */ +	char time_of_day[16];/* Flawfinder: ignore */ +	strftime(daybuf, 16, "%Y-%m-%d", localtime(&now)); +	strftime(time_of_day, 16, " %H:%M:%S", localtime(&now)); +	message["Time"] = time_of_day; + +	std::string day = daybuf; + +	if(!mEvents.has(day)) +	{ +		mEvents[day] = LLSD::emptyArray(); +	} +	LLSD& dayEvents = mEvents[day]; +	if(dayEvents.size() > 0) +	{ +		LLSD& last = *(dayEvents.rbeginArray()); +		if( last["public_id"].asUUID() == message["public_id"].asUUID()  +			&& last["ObjectName"].asString() == message["ObjectName"].asString()  +			&& last["OwnerID"].asUUID() == message["OwnerID"].asUUID() +			&& last["ParcelName"].asString() == message["ParcelName"].asString() +			&& last["Permission"].asInteger() == message["Permission"].asInteger()) +		{ +			last["Count"] = last["Count"].asInteger() + 1; +			last["Time"] = time_of_day; +			mSignals(last); +			return; +		} +	} +	message["Time"] = time_of_day; +	mEvents[day].append(message); +	mSignals(message); +} + +LLExperienceLog::LLExperienceLog() +	: mMaxDays(7) +	, mPageSize(25) +	, mNotifyNewEvent(false) +{ +} + +void LLExperienceLog::initialize() +{ +	loadEvents(); +	if(!gGenericDispatcher.isHandlerPresent("ExperienceEvent")) +	{ +		gGenericDispatcher.addHandler("ExperienceEvent", &experience_log_dispatch_handler); +	} +} + +std::string LLExperienceLog::getFilename() +{ +	return gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "experience_events.xml"); +} + + +std::string LLExperienceLog::getPermissionString( const LLSD& message, const std::string& base ) +{ +	std::ostringstream buf; +	if(message.has("Permission")) +	{ +		buf << base << message["Permission"].asInteger(); +		std::string entry; +		if(LLTrans::findString(entry, buf.str())) +		{ +			buf.str(entry); +		} +		else +		{ +			buf.str(); +		} +	} + +	if(buf.str().empty()) +	{ +		buf << base << "Unknown"; + +		buf.str(LLTrans::getString(buf.str(), message)); +	} + +	return buf.str(); +} + +void LLExperienceLog::notify( LLSD& message ) +{ +	message["EventType"] = getPermissionString(message, "ExperiencePermission"); +	if(message.has("IsAttachment") && message["IsAttachment"].asBoolean()) +	{ +		LLNotificationsUtil::add("ExperienceEventAttachment", message); +	} +	else +	{ +		LLNotificationsUtil::add("ExperienceEvent", message); +	} +	message.erase("EventType"); +} + +void LLExperienceLog::saveEvents() +{ +	eraseExpired(); +	std::string filename = getFilename(); +	LLSD settings = LLSD::emptyMap().with("Events", mEvents); + +	settings["MaxDays"] = (int)mMaxDays; +	settings["Notify"] = mNotifyNewEvent; +	settings["PageSize"] = (int)mPageSize; + +	llofstream stream(filename); +	LLSDSerialize::toPrettyXML(settings, stream); +} + + +void LLExperienceLog::loadEvents() +{ +	LLSD settings = LLSD::emptyMap(); + +	std::string filename = getFilename(); +	llifstream stream(filename); +	LLSDSerialize::fromXMLDocument(settings, stream); + +	if(settings.has("MaxDays")) +	{ +		setMaxDays((U32)settings["MaxDays"].asInteger()); +	} +	if(settings.has("Notify")) +	{ +		setNotifyNewEvent(settings["Notify"].asBoolean()); +	} +	if(settings.has("PageSize")) +	{ +		setPageSize((U32)settings["PageSize"].asInteger()); +	} +	mEvents.clear(); +	if(mMaxDays > 0 && settings.has("Events")) +	{ +		mEvents = settings["Events"]; +	} + +	eraseExpired(); +} + +LLExperienceLog::~LLExperienceLog() +{ +	saveEvents(); +} + +void LLExperienceLog::eraseExpired() +{ +	while(mEvents.size() > mMaxDays && mMaxDays > 0) +	{ +		mEvents.erase(mEvents.beginMap()->first); +	} +} + +const LLSD& LLExperienceLog::getEvents() const +{ +	return mEvents; +} + +void LLExperienceLog::clear() +{ +	mEvents.clear(); +} + +void LLExperienceLog::setMaxDays( U32 val ) +{ +	mMaxDays = val; +	if(mMaxDays > 0) +	{ +		eraseExpired(); +	} +} + +LLExperienceLog::callback_connection_t LLExperienceLog::addUpdateSignal( callback_slot_t cb ) +{ +	return mSignals.connect(cb); +} + +void LLExperienceLog::setNotifyNewEvent( bool val ) +{ +	mNotifyNewEvent = val; +	if(!val && mNotifyConnection.connected()) +	{ +		mNotifyConnection.disconnect(); +	} +	else if( val && !mNotifyConnection.connected()) +	{ +		mNotifyConnection = addUpdateSignal(notify); +	} +} diff --git a/indra/newview/llexperiencelog.h b/indra/newview/llexperiencelog.h new file mode 100644 index 0000000000..5bc47c4e8d --- /dev/null +++ b/indra/newview/llexperiencelog.h @@ -0,0 +1,85 @@ +/**  + * @file llexperiencelog.h + * @brief llexperiencelog and related class definitions + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + + + +#ifndef LL_LLEXPERIENCELOG_H +#define LL_LLEXPERIENCELOG_H + +#include "llsingleton.h" + +class LLExperienceLog : public LLSingleton<LLExperienceLog> +{ +public: +	typedef boost::signals2::signal<void(LLSD&)>  +		callback_signal_t; +	typedef callback_signal_t::slot_type callback_slot_t; +	typedef boost::signals2::connection callback_connection_t; +	callback_connection_t addUpdateSignal(callback_slot_t cb); + +	void initialize(); + +	U32 getMaxDays() const { return mMaxDays; } +	void setMaxDays(U32 val); + +	bool getNotifyNewEvent() const { return mNotifyNewEvent; } +	void setNotifyNewEvent(bool val); + +	U32 getPageSize() const { return mPageSize; } +	void setPageSize(U32 val) { mPageSize = val; } + +	const LLSD& getEvents()const; +	void clear(); + +	virtual ~LLExperienceLog(); + +	static void notify(LLSD& message); +	static std::string getFilename(); +	static std::string getPermissionString(const LLSD& message, const std::string& base); +protected: +	LLExperienceLog(); +	void handleExperienceMessage(LLSD& message); + + +	void loadEvents(); +	void saveEvents(); +	void eraseExpired(); + +	LLSD mEvents; +	callback_signal_t mSignals; +	callback_connection_t mNotifyConnection; +	U32 mMaxDays; +	U32 mPageSize; +	bool mNotifyNewEvent; + +	friend class LLExperienceLogDispatchHandler; +	friend class LLSingleton<LLExperienceLog>; +}; + + + + +#endif // LL_LLEXPERIENCELOG_H diff --git a/indra/newview/llfloaterexperiencepicker.cpp b/indra/newview/llfloaterexperiencepicker.cpp new file mode 100644 index 0000000000..b82257ee56 --- /dev/null +++ b/indra/newview/llfloaterexperiencepicker.cpp @@ -0,0 +1,502 @@ +/**  +* @file llfloaterexperiencepicker.cpp +* @brief Implementation of llfloaterexperiencepicker +* @author dolphin@lindenlab.com +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterexperiencepicker.h" + + +#include "lllineeditor.h" +#include "llfloaterreg.h" +#include "llscrolllistctrl.h" +#include "llviewerregion.h" +#include "llagent.h" +#include "llexperiencecache.h" +#include "llslurl.h" +#include "llavatarnamecache.h" +#include "llfloaterexperienceprofile.h" +#include "llcombobox.h" +#include "llviewercontrol.h" +#include "lldraghandle.h" + +#define BTN_FIND		"find" +#define BTN_OK			"ok_btn" +#define BTN_CANCEL		"cancel_btn" +#define BTN_PROFILE		"profile_btn" +#define TEXT_EDIT		"edit" +#define TEXT_MATURITY	"maturity" +#define LIST_RESULTS	"search_results" +#define PANEL_SEARCH	"search_panel" + +const static std::string columnSpace = " "; + +class LLExperiencePickerResponder : public LLHTTPClient::Responder +{ +public: +	LLUUID mQueryID; +	LLHandle<LLFloaterExperiencePicker> mParent; + +	LLExperiencePickerResponder(const LLUUID& id, const LLHandle<LLFloaterExperiencePicker>& parent) : mQueryID(id), mParent(parent) { } + +	void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			if(mParent.isDead()) +				return; + +			LLFloaterExperiencePicker* floater =mParent.get(); +			if (floater) +			{ +				floater->processResponse(mQueryID, content); +			} +		} +		else +		{ +			llwarns << "avatar picker failed [status:" << status << "]: " << content << llendl; + +		} +	} +}; +LLFloaterExperiencePicker* LLFloaterExperiencePicker::show( select_callback_t callback, const LLUUID& key, BOOL allow_multiple, BOOL closeOnSelect, LLView * frustumOrigin ) +{ +	LLFloaterExperiencePicker* floater = +		LLFloaterReg::showTypedInstance<LLFloaterExperiencePicker>("experience_search", key); +	if (!floater) +	{ +		llwarns << "Cannot instantiate experience picker" << llendl; +		return NULL; +	} + +	floater->mSelectionCallback = callback; +	floater->mCloseOnSelect = closeOnSelect; +	floater->setAllowMultiple(allow_multiple); + +	if(frustumOrigin) +	{ +		floater->mFrustumOrigin = frustumOrigin->getHandle(); +	} + +	return floater; +} + + +void LLFloaterExperiencePicker::drawFrustum() +{ +	if(mFrustumOrigin.get()) +	{ +		LLView * frustumOrigin = mFrustumOrigin.get(); +		LLRect origin_rect; +		frustumOrigin->localRectToOtherView(frustumOrigin->getLocalRect(), &origin_rect, this); +		// draw context cone connecting color picker with color swatch in parent floater +		LLRect local_rect = getLocalRect(); +		if (hasFocus() && frustumOrigin->isInVisibleChain() && mContextConeOpacity > 0.001f) +		{ +			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +			LLGLEnable(GL_CULL_FACE); +			gGL.begin(LLRender::QUADS); +			{ +				gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); +				gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop); +				gGL.vertex2i(origin_rect.mRight, origin_rect.mTop); +				gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); +				gGL.vertex2i(local_rect.mRight, local_rect.mTop); +				gGL.vertex2i(local_rect.mLeft, local_rect.mTop); + +				gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); +				gGL.vertex2i(local_rect.mLeft, local_rect.mTop); +				gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); +				gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); +				gGL.vertex2i(origin_rect.mLeft, origin_rect.mBottom); +				gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop); + +				gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); +				gGL.vertex2i(local_rect.mRight, local_rect.mBottom); +				gGL.vertex2i(local_rect.mRight, local_rect.mTop); +				gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); +				gGL.vertex2i(origin_rect.mRight, origin_rect.mTop); +				gGL.vertex2i(origin_rect.mRight, origin_rect.mBottom); + +				gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); +				gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); +				gGL.vertex2i(local_rect.mRight, local_rect.mBottom); +				gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); +				gGL.vertex2i(origin_rect.mRight, origin_rect.mBottom); +				gGL.vertex2i(origin_rect.mLeft, origin_rect.mBottom); +			} +			gGL.end(); +		} + +		if (gFocusMgr.childHasMouseCapture(getDragHandle())) +		{ +			mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), LLCriticalDamp::getInterpolant(mContextConeFadeTime)); +		} +		else +		{ +			mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(mContextConeFadeTime)); +		} +	} +} + +void LLFloaterExperiencePicker::draw() +{ +	drawFrustum(); +	LLFloater::draw(); +} + +LLFloaterExperiencePicker::LLFloaterExperiencePicker( const LLSD& key ) +	:LLFloater(key) +	,mContextConeOpacity	(0.f) +	,mContextConeInAlpha(0.f) +	,mContextConeOutAlpha(0.f) +	,mContextConeFadeTime(0.f) +{ +	setDefaultFilters(); + +	mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha"); +	mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha"); +	mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime"); +} + +LLFloaterExperiencePicker::~LLFloaterExperiencePicker() +{ +	gFocusMgr.releaseFocusIfNeeded( this ); +} + + + +BOOL LLFloaterExperiencePicker::postBuild() +{ +	getChild<LLLineEditor>(TEXT_EDIT)->setKeystrokeCallback( boost::bind(&LLFloaterExperiencePicker::editKeystroke, this, _1, _2),NULL); + +	childSetAction(BTN_FIND, boost::bind(&LLFloaterExperiencePicker::onBtnFind, this)); +	getChildView(BTN_FIND)->setEnabled(FALSE); + +	LLScrollListCtrl* searchresults = getChild<LLScrollListCtrl>(LIST_RESULTS); +	searchresults->setDoubleClickCallback( boost::bind(&LLFloaterExperiencePicker::onBtnSelect, this)); +	searchresults->setCommitCallback(boost::bind(&LLFloaterExperiencePicker::onList, this)); +	getChildView(LIST_RESULTS)->setEnabled(FALSE); +	getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("no_results")); + +	childSetAction(BTN_OK, boost::bind(&LLFloaterExperiencePicker::onBtnSelect, this)); +	getChildView(BTN_OK)->setEnabled(FALSE); +	childSetAction(BTN_CANCEL, boost::bind(&LLFloaterExperiencePicker::onBtnClose, this)); +	childSetAction(BTN_PROFILE, boost::bind(&LLFloaterExperiencePicker::onBtnProfile, this)); +	getChildView(BTN_PROFILE)->setEnabled(FALSE); + +	getChild<LLComboBox>(TEXT_MATURITY)->setCurrentByIndex(2); +	getChild<LLComboBox>(TEXT_MATURITY)->setCommitCallback(boost::bind(&LLFloaterExperiencePicker::onMaturity, this)); +	getChild<LLUICtrl>(TEXT_EDIT)->setFocus(TRUE); + +	LLPanel* search_panel = getChild<LLPanel>(PANEL_SEARCH); +	if (search_panel) +	{ +		// Start searching when Return is pressed in the line editor. +		search_panel->setDefaultBtn(BTN_FIND); +	} +	return TRUE; +} + +void LLFloaterExperiencePicker::editKeystroke( class LLLineEditor* caller, void* user_data ) +{ +	getChildView(BTN_FIND)->setEnabled(caller->getText().size() > 0); +} + +void LLFloaterExperiencePicker::onBtnFind() +{ +	find(); +} + +void LLFloaterExperiencePicker::onBtnSelect() +{ +	if(!isSelectButtonEnabled()) +	{ +		return; +	} + +	if(mSelectionCallback) +	{ +		const LLScrollListCtrl* results = getChild<LLScrollListCtrl>(LIST_RESULTS); +		uuid_vec_t experience_ids; +		 +		getSelectedExperienceIds(results, experience_ids); +		mSelectionCallback(experience_ids); +		getChild<LLScrollListCtrl>(LIST_RESULTS)->deselectAllItems(TRUE); +		if(mCloseOnSelect) +		{ +			mCloseOnSelect = FALSE; +			closeFloater(); +		} +	} +	else  +	{ +		onBtnProfile(); +	} +} + +void LLFloaterExperiencePicker::onList() +{ +	bool enabled = isSelectButtonEnabled(); +	getChildView(BTN_OK)->setEnabled(enabled); + +	enabled = enabled && getChild<LLScrollListCtrl>(LIST_RESULTS)->getNumSelected() == 1; +	getChildView(BTN_PROFILE)->setEnabled(enabled); +} + + +void LLFloaterExperiencePicker::onBtnClose() +{ +	closeFloater(); +} + + +void LLFloaterExperiencePicker::find() +{ +	std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString(); +	mQueryID.generate(); +	std::string url; +	url.reserve(128+text.size()); + +	LLViewerRegion* region = gAgent.getRegion(); +	url = region->getCapability("FindExperienceByName"); +	if (!url.empty()) +	{ +		url+="?query="; +		url+=LLURI::escape(text); +		LLHTTPClient::get(url, new LLExperiencePickerResponder(mQueryID, getDerivedHandle<LLFloaterExperiencePicker>())); + +	} + + +	getChild<LLScrollListCtrl>(LIST_RESULTS)->deleteAllItems(); +	getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("searching")); + +	getChildView(BTN_OK)->setEnabled(FALSE); +	getChildView(BTN_PROFILE)->setEnabled(FALSE); +} + + +bool LLFloaterExperiencePicker::isSelectButtonEnabled() +{ +	LLScrollListCtrl* list=getChild<LLScrollListCtrl>(LIST_RESULTS); +	return list->getFirstSelectedIndex() >=0; +} + +void LLFloaterExperiencePicker::getSelectedExperienceIds( const LLScrollListCtrl* results, uuid_vec_t &experience_ids ) +{ +	std::vector<LLScrollListItem*> items = results->getAllSelected(); +	for(std::vector<LLScrollListItem*>::iterator it = items.begin(); it != items.end(); ++it) +	{ +		LLScrollListItem* item = *it; +		if (item->getUUID().notNull()) +		{ +			experience_ids.push_back(item->getUUID()); +		} +	} +} + +void LLFloaterExperiencePicker::setAllowMultiple( bool allow_multiple ) +{ +	getChild<LLScrollListCtrl>(LIST_RESULTS)->setAllowMultipleSelection(allow_multiple); +} + + +void name_callback(const LLHandle<LLFloaterExperiencePicker>& floater, const LLUUID& experience_id, const LLUUID& agent_id, const LLAvatarName& av_name) +{ +	if(floater.isDead()) +		return; +	LLFloaterExperiencePicker* picker = floater.get(); +	LLScrollListCtrl* search_results = picker->getChild<LLScrollListCtrl>(LIST_RESULTS); + +	LLScrollListItem* item = search_results->getItem(experience_id); +	if(!item) +		return; + +	item->getColumn(2)->setValue(columnSpace+av_name.getDisplayName()); + +} + +void LLFloaterExperiencePicker::processResponse( const LLUUID& query_id, const LLSD& content ) +{ +	if(query_id != mQueryID) +	{ +		return; +	} + +	mResponse = content; + +	const LLSD& experiences=mResponse["experience_keys"]; +	LLSD::array_const_iterator it = experiences.beginArray(); +	for ( ; it != experiences.endArray(); ++it) +	{ +		LLExperienceCache::insert(*it); +	} + +	filterContent(); + +} + +void LLFloaterExperiencePicker::onBtnProfile() +{ +	LLScrollListItem* item = getChild<LLScrollListCtrl>(LIST_RESULTS)->getFirstSelected(); +	if(item) +	{ +		LLFloaterReg::showInstance("experience_profile", item->getUUID(), true); +	} +} + +std::string LLFloaterExperiencePicker::getMaturityString(int maturity) +{ +	if(maturity <= SIM_ACCESS_PG) +	{ +		return getString("maturity_icon_general"); +	} +	else if(maturity <= SIM_ACCESS_MATURE) +	{ +		return getString("maturity_icon_moderate"); +	} +	return getString("maturity_icon_adult"); +} + +void LLFloaterExperiencePicker::filterContent() +{ +	LLScrollListCtrl* search_results = getChild<LLScrollListCtrl>(LIST_RESULTS); + +	const LLSD& experiences=mResponse["experience_keys"]; + +	search_results->deleteAllItems(); + +	LLSD item; +	LLSD::array_const_iterator it = experiences.beginArray(); +	for ( ; it != experiences.endArray(); ++it) +	{ +		const LLSD& experience = *it; + +		if(isExperienceHidden(experience)) +			continue; + +		item["id"]=experience[LLExperienceCache::EXPERIENCE_ID]; +		LLSD& columns = item["columns"]; +		columns[0]["column"] = "maturity"; +		columns[0]["value"] = getMaturityString(experience[LLExperienceCache::MATURITY].asInteger()); +		columns[0]["type"]="icon"; +		columns[0]["halign"]="right"; +		columns[1]["column"] = "experience_name"; +		columns[1]["value"] = columnSpace+experience[LLExperienceCache::NAME].asString(); +		columns[2]["column"] = "owner"; +		columns[2]["value"] = columnSpace+getString("loading"); +		search_results->addElement(item); +		LLAvatarNameCache::get(experience[LLExperienceCache::AGENT_ID], boost::bind(name_callback, getDerivedHandle<LLFloaterExperiencePicker>(), experience[LLExperienceCache::EXPERIENCE_ID], _1, _2)); +	} + +	if (search_results->isEmpty()) +	{ +		LLStringUtil::format_map_t map; +		map["[TEXT]"] = childGetText(TEXT_EDIT); +		getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("not_found", map)); +		search_results->setEnabled(false); +		getChildView(BTN_OK)->setEnabled(false); +		getChildView(BTN_PROFILE)->setEnabled(false); +	} +	else +	{ +		getChildView(BTN_OK)->setEnabled(true); +		search_results->setEnabled(true); +		search_results->sortByColumnIndex(1, TRUE); +		std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString(); +		if (!search_results->selectItemByLabel(text, TRUE, 1)) +		{ +			search_results->selectFirstItem(); +		}			 +		onList(); +		search_results->setFocus(TRUE); +	} +} + +void LLFloaterExperiencePicker::onMaturity() +{ +	if(mResponse.has("experience_keys") && mResponse["experience_keys"].beginArray() != mResponse["experience_keys"].endArray()) +	{ +		filterContent(); +	} +} + +bool LLFloaterExperiencePicker::isExperienceHidden( const LLSD& experience) const +{ +	bool hide=false; +	filter_list::const_iterator it = mFilters.begin(); +	for(/**/;it != mFilters.end(); ++it) +	{ +		if((*it)(experience)){ +			return true; +		} +	} + +	return hide; +} + +bool LLFloaterExperiencePicker::FilterOverRating( const LLSD& experience ) +{ +	int maturity = getChild<LLComboBox>(TEXT_MATURITY)->getSelectedValue().asInteger(); +	return experience[LLExperienceCache::MATURITY].asInteger() > maturity; +} + +bool LLFloaterExperiencePicker::FilterWithProperty( const LLSD& experience, S32 prop) +{ +	return (experience[LLExperienceCache::PROPERTIES].asInteger() & prop) != 0; +} + +bool LLFloaterExperiencePicker::FilterWithoutProperty( const LLSD& experience, S32 prop ) +{ +	return (experience[LLExperienceCache::PROPERTIES].asInteger() & prop) == 0; +} + +void LLFloaterExperiencePicker::setDefaultFilters() +{ +	mFilters.clear(); +	addFilter(boost::bind(&LLFloaterExperiencePicker::FilterOverRating, this, _1)); +} + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/llfloaterexperiencepicker.h b/indra/newview/llfloaterexperiencepicker.h new file mode 100644 index 0000000000..ea0240d2ac --- /dev/null +++ b/indra/newview/llfloaterexperiencepicker.h @@ -0,0 +1,103 @@ +/**  +* @file   llfloaterexperiencepicker.h +* @brief  Header file for llfloaterexperiencepicker +* @author dolphin@lindenlab.com +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLFLOATEREXPERIENCEPICKER_H +#define LL_LLFLOATEREXPERIENCEPICKER_H + +#include "llfloater.h" + +class LLScrollListCtrl; +class LLLineEditor; + + +class LLFloaterExperiencePicker : public LLFloater +{ +public: +	friend class LLExperiencePickerResponder; + +	// The callback function will be called with an avatar name and UUID. +	typedef boost::function<void (const uuid_vec_t&)> select_callback_t; +	// filter function for experiences, return true if the experience should be hidden. +	typedef boost::function<bool (const LLSD&)> filter_function; +	typedef std::vector<filter_function> filter_list; + +	static LLFloaterExperiencePicker* show( select_callback_t callback, const LLUUID& key, BOOL allow_multiple, BOOL closeOnSelect, LLView * frustumOrigin); + +	LLFloaterExperiencePicker(const LLSD& key); +	virtual ~LLFloaterExperiencePicker(); + +	BOOL postBuild(); + +	void addFilter(filter_function func){mFilters.push_back(func);} +	template <class IT> +	void addFilters(IT begin, IT end){mFilters.insert(mFilters.end(), begin, end);} +	void setDefaultFilters(); + +	static bool FilterWithProperty(const LLSD& experience, S32 prop); +	static bool FilterWithoutProperty(const LLSD& experience, S32 prop); +	bool FilterOverRating(const LLSD& experience); + +	virtual void	draw(); +private: +	void editKeystroke(LLLineEditor* caller, void* user_data); + +	void onBtnFind(); +	void onBtnSelect(); +	void onBtnProfile(); +	void onBtnClose(); +	void onList(); +	void onMaturity(); + +	void getSelectedExperienceIds( const LLScrollListCtrl* results, uuid_vec_t &experience_ids ); +	void setAllowMultiple(bool allow_multiple); + + +	void find(); +	bool isSelectButtonEnabled(); +	void processResponse( const LLUUID& query_id, const LLSD& content ); + +	void filterContent(); +	bool isExperienceHidden(const LLSD& experience) const ; +	std::string getMaturityString(int maturity); + + +	select_callback_t	mSelectionCallback; +	filter_list			mFilters; +	LLUUID				mQueryID; +	LLSD				mResponse; +	bool				mCloseOnSelect; + + +	void drawFrustum(); +	LLHandle <LLView>   mFrustumOrigin; +	F32		            mContextConeOpacity; +	F32                 mContextConeInAlpha; +	F32                 mContextConeOutAlpha; +	F32                 mContextConeFadeTime; +}; + +#endif // LL_LLFLOATEREXPERIENCEPICKER_H + diff --git a/indra/newview/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp new file mode 100644 index 0000000000..f8b4978a3d --- /dev/null +++ b/indra/newview/llfloaterexperienceprofile.cpp @@ -0,0 +1,992 @@ +/**  + * @file llfloaterexperienceprofile.cpp + * @brief llfloaterexperienceprofile 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$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llfloaterexperienceprofile.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llcommandhandler.h" +#include "llexpandabletextbox.h" +#include "llexperiencecache.h" +#include "llfloaterreg.h" +#include "llhttpclient.h" +#include "lllayoutstack.h" +#include "lllineeditor.h" +#include "llnotificationsutil.h" +#include "llsdserialize.h" +#include "llslurl.h" +#include "lltabcontainer.h" +#include "lltextbox.h" +#include "lltexturectrl.h" +#include "lltrans.h" +#include "llviewerregion.h" +#include "llevents.h" +#include "llfloatergroups.h" +#include "llnotifications.h" +#include "llfloaterreporter.h" + +#define XML_PANEL_EXPERIENCE_PROFILE "floater_experienceprofile.xml" +#define TF_NAME "experience_title" +#define TF_DESC "experience_description" +#define TF_SLURL "LocationTextText" +#define TF_MRKT "marketplace" +#define TF_MATURITY "ContentRatingText" +#define TF_OWNER "OwnerText" +#define TF_GROUP "GroupText" +#define TF_GRID_WIDE "grid_wide" +#define TF_PRIVILEGED "privileged" +#define EDIT "edit_" + +#define IMG_LOGO "logo" + +#define PNL_TOP "top panel" +#define PNL_IMAGE "image_panel" +#define PNL_DESC "description panel" +#define PNL_LOC "location panel" +#define PNL_MRKT "marketplace panel" +#define PNL_GROUP "group_panel" +#define PNL_PERMS "perm panel" + +#define BTN_ALLOW "allow_btn" +#define BTN_BLOCK "block_btn" +#define BTN_CANCEL "cancel_btn" +#define BTN_CLEAR_LOCATION "clear_btn" +#define BTN_EDIT "edit_btn" +#define BTN_ENABLE "enable_btn" +#define BTN_FORGET "forget_btn" +#define BTN_PRIVATE "private_btn" +#define BTN_REPORT "report_btn" +#define BTN_SAVE "save_btn" +#define BTN_SET_GROUP "Group_btn" +#define BTN_SET_LOCATION "location_btn" + + +class LLExperienceHandler : public LLCommandHandler +{ +public: +    LLExperienceHandler() : LLCommandHandler("experience", UNTRUSTED_THROTTLE) { } + +    bool handle(const LLSD& params, const LLSD& query_map, +        LLMediaCtrl* web) +    { +        if(params.size() != 2 || params[1].asString() != "profile") +            return false; + +        LLExperienceCache::get(params[0].asUUID(), boost::bind(&LLExperienceHandler::experienceCallback, this, _1)); +        return true; +    } + +    void experienceCallback(const LLSD& experienceDetails) +    { +        if(!experienceDetails.has(LLExperienceCache::MISSING)) +        { +            LLFloaterReg::showInstance("experience_profile", experienceDetails[LLExperienceCache::EXPERIENCE_ID].asUUID(), true); +        } +    } +}; + +LLExperienceHandler gExperienceHandler; + + +LLFloaterExperienceProfile::LLFloaterExperienceProfile(const LLSD& data) +    : LLFloater(data) +    , mExperienceId(data.asUUID()) +    , mSaveCompleteAction(NOTHING) +    , mDirty(false) +    , mForceClose(false) +{ + +} + + +LLFloaterExperienceProfile::~LLFloaterExperienceProfile() +{ + +} + +template<class T>  +class HandleResponder : public LLHTTPClient::Responder +{ +public: +    HandleResponder(const LLHandle<T>& parent):mParent(parent){} +    LLHandle<T> mParent; + +    virtual void error(U32 status, const std::string& reason) +    { +        llwarns << "HandleResponder failed with code: " << status<< ", reason: " << reason << llendl; +    } +}; + +class ExperienceUpdateResponder : public HandleResponder<LLFloaterExperienceProfile> +{ +public: +    ExperienceUpdateResponder(const LLHandle<LLFloaterExperienceProfile>& parent):HandleResponder<LLFloaterExperienceProfile>(parent) +    { +    } + +    virtual void result(const LLSD& content) +    { +        LLFloaterExperienceProfile* parent=mParent.get(); +        if(parent) +        { +            parent->onSaveComplete(content); +        } +    } +}; + + + +class ExperiencePreferencesResponder : public LLHTTPClient::Responder +{ +public: +    ExperiencePreferencesResponder(const LLUUID& single = LLUUID::null):mId(single) +    { +    } + +    bool sendSingle(const LLSD& content, const LLSD& permission, const char* name) +    { +        if(!content.has(name)) +            return false; + +        LLEventPump& pump = LLEventPumps::instance().obtain("experience_permission"); +        const LLSD& list = content[name]; +        LLSD::array_const_iterator it = list.beginArray(); +        while(it != list.endArray()) +        { +            if(it->asUUID() == mId) +            { +                LLSD message; +                message[it->asString()] = permission; +                message["experience"] = mId; +                pump.post(message); +                return true; +            } +            ++it; +        } +        return false; +    } + +    bool hasPermission(const LLSD& content, const char* name) +    { +        if(!content.has(name)) +            return false; + +        const LLSD& list = content[name]; +        LLSD::array_const_iterator it = list.beginArray(); +        while(it != list.endArray()) +        { +            if(it->asUUID() == mId) +            { +                return true; +            } +            ++it; +        } +        return false; +    } + +    const char* getPermission(const LLSD& content) +    { +        if(hasPermission(content, "experiences")) +        { +            return "Allow"; +        } +        else if(hasPermission(content, "blocked")) +        { +            return "Block"; +        } +        return "Forget"; +    } + + +    virtual void result(const LLSD& content) +    { +        if(mId.notNull()) +        { +            post(getPermission(content)); +            return; +        } +        LLEventPumps::instance().obtain("experience_permission").post(content); +    } + +    void post( const char* perm ) +    { +        LLSD experience; +        LLSD message; +        experience["permission"]=perm; +        message["experience"] = mId; +        message[mId.asString()] = experience; +        LLEventPumps::instance().obtain("experience_permission").post(message); +    } + +private: +    LLUUID mId; +}; + + +class IsAdminResponder : public HandleResponder<LLFloaterExperienceProfile> +{ +public: +    IsAdminResponder(const LLHandle<LLFloaterExperienceProfile>& parent):HandleResponder<LLFloaterExperienceProfile>(parent) +    { +    } +     +    virtual void result(const LLSD& content) +    { +        LLFloaterExperienceProfile* parent = mParent.get(); +        if(!parent) +            return; + +        bool enabled = true; +        LLViewerRegion* region = gAgent.getRegion(); +        if (!region) +        { +            enabled = false; +        } +        else +        { +            std::string url=region->getCapability("UpdateExperience");  +            if(url.empty()) +                enabled = false; +        } +        if(enabled && content["status"].asBoolean()) +        { +            parent->getChild<LLLayoutPanel>(PNL_TOP)->setVisible(TRUE); +            parent->getChild<LLButton>(BTN_EDIT)->setVisible(TRUE); +        } +    } +}; + +BOOL LLFloaterExperienceProfile::postBuild() +{ + +    if (mExperienceId.notNull()) +    { +        LLExperienceCache::fetch(mExperienceId, true); +        LLExperienceCache::get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback,  +            getDerivedHandle<LLFloaterExperienceProfile>(), _1));  +         +        LLViewerRegion* region = gAgent.getRegion(); +        if (region) +        { +            std::string lookup_url=region->getCapability("IsExperienceAdmin");  +            if(!lookup_url.empty()) +            { +                LLHTTPClient::get(lookup_url+"?experience_id="+mExperienceId.asString(), new IsAdminResponder(getDerivedHandle<LLFloaterExperienceProfile>())); +            } +        } +    } + +    childSetAction(BTN_EDIT, boost::bind(&LLFloaterExperienceProfile::onClickEdit, this)); +    childSetAction(BTN_ALLOW, boost::bind(&LLFloaterExperienceProfile::onClickPermission, this, "Allow")); +    childSetAction(BTN_FORGET, boost::bind(&LLFloaterExperienceProfile::onClickForget, this)); +    childSetAction(BTN_BLOCK, boost::bind(&LLFloaterExperienceProfile::onClickPermission, this, "Block")); +    childSetAction(BTN_CANCEL, boost::bind(&LLFloaterExperienceProfile::onClickCancel, this)); +	childSetAction(BTN_SAVE, boost::bind(&LLFloaterExperienceProfile::onClickSave, this)); +	childSetAction(BTN_SET_LOCATION, boost::bind(&LLFloaterExperienceProfile::onClickLocation, this)); +	childSetAction(BTN_CLEAR_LOCATION, boost::bind(&LLFloaterExperienceProfile::onClickClear, this)); +	childSetAction(BTN_SET_GROUP, boost::bind(&LLFloaterExperienceProfile::onPickGroup, this)); +	childSetAction(BTN_REPORT, boost::bind(&LLFloaterExperienceProfile::onReportExperience, this)); + +    getChild<LLTextEditor>(EDIT TF_DESC)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); +    getChild<LLUICtrl>(EDIT TF_MATURITY)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); +    getChild<LLLineEditor>(EDIT TF_MRKT)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL); +    getChild<LLLineEditor>(EDIT TF_NAME)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL); +     +    childSetCommitCallback(EDIT BTN_ENABLE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL); +    childSetCommitCallback(EDIT BTN_PRIVATE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL); + +    childSetCommitCallback(EDIT IMG_LOGO, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), NULL); + +    getChild<LLTextEditor>(EDIT TF_DESC)->setCommitOnFocusLost(TRUE); + + +    LLEventPumps::instance().obtain("experience_permission").listen(mExperienceId.asString()+"-profile",  +        boost::bind(&LLFloaterExperienceProfile::experiencePermission, getDerivedHandle<LLFloaterExperienceProfile>(this), _1)); +     +    return TRUE; +} + +void LLFloaterExperienceProfile::experienceCallback(LLHandle<LLFloaterExperienceProfile> handle,  const LLSD& experience ) +{ +    LLFloaterExperienceProfile* pllpep = handle.get(); +    if(pllpep) +    { +        pllpep->refreshExperience(experience); +    } +} + + +bool LLFloaterExperienceProfile::experiencePermission( LLHandle<LLFloaterExperienceProfile> handle, const LLSD& permission ) +{ +    LLFloaterExperienceProfile* pllpep = handle.get(); +    if(pllpep) +    { +        pllpep->updatePermission(permission); +    } +    return false; +} + + +void LLFloaterExperienceProfile::onClickEdit() +{ +    LLTabContainer* tabs = getChild<LLTabContainer>("tab_container"); + +    tabs->selectTabByName("edit_panel_experience_info"); +} + + +void LLFloaterExperienceProfile::onClickCancel() +{ +    changeToView(); +} + +void LLFloaterExperienceProfile::onClickSave() +{ +    doSave(NOTHING); +} + + +void LLFloaterExperienceProfile::onClickPermission(const char* perm) +{ +    LLViewerRegion* region = gAgent.getRegion(); +    if (!region) +        return; +    +    std::string lookup_url=region->getCapability("ExperiencePreferences");  +    if(lookup_url.empty()) +        return; +    LLSD permission; +    LLSD data; +    permission["permission"]=perm; + +    data[mExperienceId.asString()]=permission; +    LLHTTPClient::put(lookup_url, data, new ExperiencePreferencesResponder(mExperienceId)); +    +} + + +void LLFloaterExperienceProfile::onClickForget() +{ +    LLViewerRegion* region = gAgent.getRegion(); +    if (!region) +        return; + +    std::string lookup_url=region->getCapability("ExperiencePreferences");  +    if(lookup_url.empty()) +        return; + +    LLHTTPClient::del(lookup_url+"?"+mExperienceId.asString(), new ExperiencePreferencesResponder(mExperienceId)); +} + +bool LLFloaterExperienceProfile::setMaturityString( U8 maturity, LLTextBox* child, LLComboBox* combo ) +{ +    LLStyle::Params style; +    std::string access; +    if(maturity <= SIM_ACCESS_PG) +    { +        style.image(LLUI::getUIImage(getString("maturity_icon_general"))); +        access = LLTrans::getString("SIM_ACCESS_PG"); +        combo->setCurrentByIndex(2); +    } +    else if(maturity <= SIM_ACCESS_MATURE) +    { +        style.image(LLUI::getUIImage(getString("maturity_icon_moderate"))); +        access = LLTrans::getString("SIM_ACCESS_MATURE"); +        combo->setCurrentByIndex(1); +    } +    else if(maturity <= SIM_ACCESS_ADULT) +    { +        style.image(LLUI::getUIImage(getString("maturity_icon_adult"))); +        access = LLTrans::getString("SIM_ACCESS_ADULT"); +        combo->setCurrentByIndex(0); +    } +    else +    { +        return false; +    } + +    child->setText(LLStringUtil::null); + +    child->appendImageSegment(style); + +    child->appendText(access, false); +     +    return true; +} + + +void LLFloaterExperienceProfile::refreshExperience( const LLSD& experience ) +{ +    mExperienceDetails = experience; +    mPackage = experience; + + +    LLLayoutPanel* imagePanel = getChild<LLLayoutPanel>(PNL_IMAGE); +    LLLayoutPanel* descriptionPanel = getChild<LLLayoutPanel>(PNL_DESC); +    LLLayoutPanel* locationPanel = getChild<LLLayoutPanel>(PNL_LOC); +    LLLayoutPanel* marketplacePanel = getChild<LLLayoutPanel>(PNL_MRKT);   +    LLLayoutPanel* topPanel = getChild<LLLayoutPanel>(PNL_TOP);   + + +    imagePanel->setVisible(FALSE); +    descriptionPanel->setVisible(FALSE); +    locationPanel->setVisible(FALSE); +    marketplacePanel->setVisible(FALSE); +    topPanel->setVisible(FALSE); + + +    LLTextBox* child = getChild<LLTextBox>(TF_NAME); +    child->setText(experience[LLExperienceCache::NAME].asString()); +     +    LLLineEditor* linechild = getChild<LLLineEditor>(EDIT TF_NAME); +    linechild->setText(experience[LLExperienceCache::NAME].asString()); +     +    std::string value = experience[LLExperienceCache::DESCRIPTION].asString(); +    LLExpandableTextBox* exchild = getChild<LLExpandableTextBox>(TF_DESC); +    exchild->setText(value); +    descriptionPanel->setVisible(value.length()>0); + +    LLTextEditor* edit_child = getChild<LLTextEditor>(EDIT TF_DESC); +    edit_child->setText(value); +    +    value = experience[LLExperienceCache::SLURL].asString(); +    child = getChild<LLTextBox>(TF_SLURL); +    bool has_value = value.length()>0; +    locationPanel->setVisible(has_value); +    value = LLSLURL(value).getSLURLString(); +    child->setText(value); + + +    child = getChild<LLTextBox>(EDIT TF_SLURL); +    if(has_value) +    { +        child->setText(value); +    } +    else +    { +        child->setText(getString("empty_slurl")); +    } +     +    setMaturityString((U8)(experience[LLExperienceCache::MATURITY].asInteger()), getChild<LLTextBox>(TF_MATURITY), getChild<LLComboBox>(EDIT TF_MATURITY)); + +    LLUUID id = experience[LLExperienceCache::AGENT_ID].asUUID(); +    child = getChild<LLTextBox>(TF_OWNER); +    value = LLSLURL("agent", id, "inspect").getSLURLString(); +    child->setText(value); + + +    id = experience[LLExperienceCache::GROUP_ID].asUUID(); +	bool id_null = id.isNull(); +    child = getChild<LLTextBox>(TF_GROUP); +    value = LLSLURL("group", id, "inspect").getSLURLString(); +    child->setText(value); +    getChild<LLLayoutPanel>(PNL_GROUP)->setVisible(!id_null); + +	setEditGroup(id); + +	getChild<LLButton>(BTN_SET_GROUP)->setEnabled(experience[LLExperienceCache::AGENT_ID].asUUID() == gAgent.getID()); +     +    LLCheckBoxCtrl* enable = getChild<LLCheckBoxCtrl>(EDIT BTN_ENABLE); +    S32 properties = mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger(); +    enable->set(!(properties & LLExperienceCache::PROPERTY_DISABLED));  + +    enable = getChild<LLCheckBoxCtrl>(EDIT BTN_PRIVATE); +    enable->set(properties & LLExperienceCache::PROPERTY_PRIVATE);   +    if(properties & LLExperienceCache::PROPERTY_GRID) +    { +        topPanel->setVisible(TRUE); +        child=getChild<LLTextBox>(TF_GRID_WIDE); +        child->setVisible(TRUE); +        child->setText(LLTrans::getString("GRID_WIDE")); +    } + +	if(getChild<LLButton>(BTN_EDIT)->getVisible()) +	{ +		topPanel->setVisible(TRUE); +	} + +    if(properties & LLExperienceCache::PROPERTY_PRIVILEGED) +    { +        child = getChild<LLTextBox>(TF_PRIVILEGED); +        child->setVisible(TRUE); +    } +    else +    { +        LLViewerRegion* region = gAgent.getRegion(); +        if (region) +        { +            std::string lookup_url=region->getCapability("ExperiencePreferences");  +            if(!lookup_url.empty()) +            { +                LLHTTPClient::get(lookup_url+"?"+mExperienceId.asString(), new ExperiencePreferencesResponder(mExperienceId)); +            } +        } +    } +             +    value=experience[LLExperienceCache::METADATA].asString(); +    if(value.empty()) +        return; +     +    LLPointer<LLSDParser> parser = new LLSDXMLParser(); + +    LLSD data; + +    std::istringstream is(value); +    if(LLSDParser::PARSE_FAILURE != parser->parse(is, data, value.size())) +    { +        value=""; +        if(data.has(TF_MRKT)) +        { +            value=data[TF_MRKT].asString(); + +            child = getChild<LLTextBox>(TF_MRKT); +            child->setText(value); +            if(value.size()) +            { +                marketplacePanel->setVisible(TRUE); +            } +            else +            { +                marketplacePanel->setVisible(FALSE); +            } +        } +        else +        { +            marketplacePanel->setVisible(FALSE); +        } +         +        linechild = getChild<LLLineEditor>(EDIT TF_MRKT); +        linechild->setText(value); + +        if(data.has(IMG_LOGO)) +        { +            LLTextureCtrl* logo = getChild<LLTextureCtrl>(IMG_LOGO); + +            LLUUID id = data[IMG_LOGO].asUUID(); +            logo->setImageAssetID(id); +            imagePanel->setVisible(TRUE); + +            logo = getChild<LLTextureCtrl>(EDIT IMG_LOGO); +            logo->setImageAssetID(data[IMG_LOGO].asUUID()); + +            imagePanel->setVisible(id.notNull()); +        } +    } +    else +    { +        marketplacePanel->setVisible(FALSE); +        imagePanel->setVisible(FALSE); +    } + +    mDirty=false; +    mForceClose = false; +    getChild<LLButton>(BTN_SAVE)->setEnabled(mDirty); +} + +void LLFloaterExperienceProfile::setPreferences( const LLSD& content ) +{ +    S32 properties = mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger(); +    if(properties & LLExperienceCache::PROPERTY_PRIVILEGED) +    { +        return; +    } + +    const LLSD& experiences = content["experiences"]; +    const LLSD& blocked = content["blocked"]; + + +    for(LLSD::array_const_iterator it = experiences.beginArray(); it != experiences.endArray() ; ++it) +    { +        if(it->asUUID()==mExperienceId) +        { +            experienceAllowed(); +            return; +        } +    } + +    for(LLSD::array_const_iterator it = blocked.beginArray(); it != blocked.endArray() ; ++it) +    { +        if(it->asUUID()==mExperienceId) +        { +            experienceBlocked(); +            return; +        } +    } + +    experienceForgotten(); +} + +void LLFloaterExperienceProfile::onFieldChanged() +{ +    updatePackage(); + +    LLSD::map_const_iterator st = mExperienceDetails.beginMap(); +    LLSD::map_const_iterator dt = mPackage.beginMap(); + +    mDirty = false; +    while( !mDirty && st != mExperienceDetails.endMap() && dt != mPackage.endMap()) +    { +        mDirty = st->first != dt->first || st->second.asString() != dt->second.asString(); +        ++st;++dt; +    } + +    if(!mDirty && (st != mExperienceDetails.endMap() || dt != mPackage.endMap())) +    { +        mDirty = true; +    } + +    getChild<LLButton>(BTN_SAVE)->setEnabled(mDirty); +} + + +BOOL LLFloaterExperienceProfile::canClose() +{ +    if(mForceClose || !mDirty) +    { +        return TRUE; +    } +    else +    { +        // Bring up view-modal dialog: Save changes? Yes, No, Cancel +        LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLFloaterExperienceProfile::handleSaveChangesDialog, this, _1, _2, CLOSE)); +        return FALSE; +    } +} + +bool LLFloaterExperienceProfile::handleSaveChangesDialog( const LLSD& notification, const LLSD& response, PostSaveAction action ) +{ +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +    switch( option ) +    { +    case 0:  // "Yes" +        // close after saving +        doSave( action ); +        break; + +    case 1:  // "No" +        if(action != NOTHING) +        { +            mForceClose = TRUE; +            if(action==CLOSE) +            { +                closeFloater(); +            } +            else +            { +                changeToView(); +            } +        } +        break; + +    case 2: // "Cancel" +    default: +        // If we were quitting, we didn't really mean it. +        LLAppViewer::instance()->abortQuit(); +        break; +    } +    return false; +} + +void LLFloaterExperienceProfile::doSave( int success_action ) +{ +    mSaveCompleteAction=success_action; + +    LLViewerRegion* region = gAgent.getRegion(); +    if (!region) +        return; + +    std::string url=region->getCapability("UpdateExperience");  +    if(url.empty()) +        return; + +	mPackage.erase(LLExperienceCache::QUOTA); +	mPackage.erase(LLExperienceCache::EXPIRES); +	mPackage.erase(LLExperienceCache::AGENT_ID); +     +    LLHTTPClient::post(url, mPackage, new ExperienceUpdateResponder(getDerivedHandle<LLFloaterExperienceProfile>())); +} + +void LLFloaterExperienceProfile::onSaveComplete( const LLSD& content ) +{ +    LLUUID id = getExperienceId(); + +	if(content.has("removed")) +	{ +		const LLSD& removed = content["removed"]; +		LLSD::map_const_iterator it = removed.beginMap(); +		for(/**/; it != removed.endMap(); ++it) +		{ +			const std::string& field = it->first; +			if(field == LLExperienceCache::EXPERIENCE_ID) +			{ +				//this message should be removed by the experience api +				continue; +			} +			const LLSD& data = it->second; +			std::string error_tag = data["error_tag"].asString()+ "ExperienceProfileMessage"; +			LLSD fields; +			if( LLNotifications::instance().getTemplate(error_tag)) +			{ +				fields["field"] = field; +				fields["extra_info"] = data["extra_info"]; +				LLNotificationsUtil::add(error_tag, fields); +			} +			else +			{ +				fields["MESSAGE"]=data["en"]; +				LLNotificationsUtil::add("GenericAlert", fields); +			} +		}		 +	} + +    if(!content.has("experience_keys")) +    { +        llwarns << "LLFloaterExperienceProfile::onSaveComplete called with bad content" << llendl; +        return; +    } + +    const LLSD& experiences = content["experience_keys"]; + +    LLSD::array_const_iterator it = experiences.beginArray(); +    if(it == experiences.endArray()) +    { +        llwarns << "LLFloaterExperienceProfile::onSaveComplete called with empty content" << llendl; +        return; +    } + +    if(!it->has(LLExperienceCache::EXPERIENCE_ID) || ((*it)[LLExperienceCache::EXPERIENCE_ID].asUUID() != id)) +    { +        llwarns << "LLFloaterExperienceProfile::onSaveComplete called with unexpected experience id" << llendl; +        return; +    } +  +    refreshExperience(*it); +    LLExperienceCache::insert(*it); +    LLExperienceCache::fetch(id, true); + +    if(mSaveCompleteAction==VIEW) +    { +        LLTabContainer* tabs = getChild<LLTabContainer>("tab_container"); +        tabs->selectTabByName("panel_experience_info"); +    } +    else if(mSaveCompleteAction == CLOSE) +    { +        closeFloater(); +    }    +} + +void LLFloaterExperienceProfile::changeToView() +{ +    if(mForceClose || !mDirty) +    { +        refreshExperience(mExperienceDetails); +        LLTabContainer* tabs = getChild<LLTabContainer>("tab_container"); + +        tabs->selectTabByName("panel_experience_info"); +    } +    else +    { +        // Bring up view-modal dialog: Save changes? Yes, No, Cancel +        LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLFloaterExperienceProfile::handleSaveChangesDialog, this, _1, _2, VIEW)); +    } +} + +void LLFloaterExperienceProfile::onClickLocation() +{ +    LLViewerRegion* region = gAgent.getRegion(); +    if(region) +    { +        LLTextBox* child = getChild<LLTextBox>(EDIT TF_SLURL); +        child->setText(LLSLURL(region->getName(), gAgent.getPositionGlobal()).getSLURLString()); +        onFieldChanged(); +    } +} + +void LLFloaterExperienceProfile::onClickClear() +{ +    LLTextBox* child = getChild<LLTextBox>(EDIT TF_SLURL); +    child->setText(getString("empty_slurl")); +    onFieldChanged(); +} + +void LLFloaterExperienceProfile::updatePermission( const LLSD& permission ) +{ +    if(permission.has("experience")) +    { +        if(permission["experience"].asUUID() != mExperienceId) +        { +            return; +        } + +        std::string str = permission[mExperienceId.asString()]["permission"].asString(); +        if(str == "Allow") +        { +            experienceAllowed(); +        } +        else if(str == "Block") +        { +            experienceBlocked(); +        } +        else if(str == "Forget") +        { +            experienceForgotten(); +        } +    } +    else +    { +        setPreferences(permission); +    } +} + +void LLFloaterExperienceProfile::experienceAllowed() +{ +    LLButton* button=getChild<LLButton>(BTN_ALLOW); +    button->setEnabled(FALSE); + +    button=getChild<LLButton>(BTN_FORGET); +    button->setEnabled(TRUE); + +    button=getChild<LLButton>(BTN_BLOCK); +    button->setEnabled(TRUE); +} + +void LLFloaterExperienceProfile::experienceForgotten() +{ +    LLButton* button=getChild<LLButton>(BTN_ALLOW); +    button->setEnabled(TRUE); + +    button=getChild<LLButton>(BTN_FORGET); +    button->setEnabled(FALSE); + +    button=getChild<LLButton>(BTN_BLOCK); +    button->setEnabled(TRUE); +} + +void LLFloaterExperienceProfile::experienceBlocked() +{ +    LLButton* button=getChild<LLButton>(BTN_ALLOW); +    button->setEnabled(TRUE); + +    button=getChild<LLButton>(BTN_FORGET); +    button->setEnabled(TRUE); + +    button=getChild<LLButton>(BTN_BLOCK); +    button->setEnabled(FALSE); +} + +void LLFloaterExperienceProfile::onClose( bool app_quitting ) +{ +    LLEventPumps::instance().obtain("experience_permission").stopListening(mExperienceId.asString()+"-profile"); +    LLFloater::onClose(app_quitting); +} + +void LLFloaterExperienceProfile::updatePackage() +{ +    mPackage[LLExperienceCache::NAME] = getChild<LLLineEditor>(EDIT TF_NAME)->getText(); +    mPackage[LLExperienceCache::DESCRIPTION] = getChild<LLTextEditor>(EDIT TF_DESC)->getText(); +    std::string slurl = getChild<LLTextBox>(EDIT TF_SLURL)->getText(); +    if(slurl == getString("empty_slurl")) +    { +        mPackage[LLExperienceCache::SLURL] = LLStringUtil::null; +    } +    else +    { +        mPackage[LLExperienceCache::SLURL] = slurl; +    } + +    mPackage[LLExperienceCache::MATURITY] = getChild<LLComboBox>(EDIT TF_MATURITY)->getSelectedValue().asInteger(); + +    LLSD metadata; + +    metadata[TF_MRKT] = getChild<LLLineEditor>(EDIT TF_MRKT)->getText(); +    metadata[IMG_LOGO] = getChild<LLTextureCtrl>(EDIT IMG_LOGO)->getImageAssetID(); + +    LLPointer<LLSDXMLFormatter> formatter = new LLSDXMLFormatter(); + +    std::ostringstream os; +    if(formatter->format(metadata, os)) +    { +        mPackage[LLExperienceCache::METADATA]=os.str(); +    } + +    int properties = mPackage[LLExperienceCache::PROPERTIES].asInteger(); +    LLCheckBoxCtrl* enable = getChild<LLCheckBoxCtrl>(EDIT BTN_ENABLE); +    if(enable->get()) +    { +        properties &= ~LLExperienceCache::PROPERTY_DISABLED; +    } +    else +    { +        properties |= LLExperienceCache::PROPERTY_DISABLED; +    } + +    enable = getChild<LLCheckBoxCtrl>(EDIT BTN_PRIVATE); +    if(enable->get()) +    { +        properties |= LLExperienceCache::PROPERTY_PRIVATE; +    } +    else +    { +        properties &= ~LLExperienceCache::PROPERTY_PRIVATE; +    } + +    mPackage[LLExperienceCache::PROPERTIES] = properties; +} + +void LLFloaterExperienceProfile::onPickGroup() +{ +	LLFloater* parent_floater = gFloaterView->getParentFloater(this); + +	LLFloaterGroupPicker* widget = LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(gAgent.getID())); +	if (widget) +	{ +		widget->setSelectGroupCallback(boost::bind(&LLFloaterExperienceProfile::setEditGroup, this, _1)); +		if (parent_floater) +		{ +			LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, widget); +			widget->setOrigin(new_rect.mLeft, new_rect.mBottom); +			parent_floater->addDependentFloater(widget); +		} +	} +} + +void LLFloaterExperienceProfile::setEditGroup( LLUUID group_id ) +{ +	LLTextBox* child = getChild<LLTextBox>(EDIT TF_GROUP); +	std::string value = LLSLURL("group", group_id, "inspect").getSLURLString(); +	child->setText(value); +	mPackage[LLExperienceCache::GROUP_ID] = group_id; +	onFieldChanged(); +} + +void LLFloaterExperienceProfile::onReportExperience() +{ +	LLFloaterReporter::showFromExperience(mExperienceId); +} diff --git a/indra/newview/llfloaterexperienceprofile.h b/indra/newview/llfloaterexperienceprofile.h new file mode 100644 index 0000000000..b66df49a8a --- /dev/null +++ b/indra/newview/llfloaterexperienceprofile.h @@ -0,0 +1,103 @@ +/**  + * @file llfloaterexperienceprofile.h + * @brief llfloaterexperienceprofile 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_LLFLOATEREXPERIENCEPROFILE_H +#define LL_LLFLOATEREXPERIENCEPROFILE_H + +#include "llfloater.h" +#include "lluuid.h" +#include "llsd.h" + +class LLLayoutPanel; +class LLTextBox; +class LLComboBox; + +class LLFloaterExperienceProfile : public LLFloater +{ +    LOG_CLASS(LLFloaterExperienceProfile); +public: +    enum PostSaveAction +    { +        NOTHING, +        CLOSE, +        VIEW, +    }; + + +    LLFloaterExperienceProfile(const LLSD& data); +    virtual ~LLFloaterExperienceProfile(); + +    LLUUID getExperienceId() const { return mExperienceId; } +    void setPreferences( const LLSD& content ); + + +    void refreshExperience(const LLSD& experience); +    void onSaveComplete( const LLSD& content ); +    virtual BOOL canClose(); + +    virtual void onClose(bool app_quitting); +protected: +    void onClickEdit(); +    void onClickPermission(const char* permission); +    void onClickForget(); +    void onClickCancel(); +    void onClickSave(); +    void onClickLocation(); +	void onClickClear(); +	void onPickGroup(); +	void onFieldChanged(); +	void onReportExperience(); + +	void setEditGroup(LLUUID group_id); + +    void changeToView(); + +    void experienceForgotten(); +    void experienceBlocked(); +    void experienceAllowed(); + +    static void experienceCallback(LLHandle<LLFloaterExperienceProfile> handle, const LLSD& experience); +    static bool experiencePermission(LLHandle<LLFloaterExperienceProfile> handle, const LLSD& permission); + +    BOOL postBuild(); +    bool setMaturityString(U8 maturity, LLTextBox* child, LLComboBox* combo); +    bool handleSaveChangesDialog(const LLSD& notification, const LLSD& response, PostSaveAction action); +    void doSave( int success_action ); + +    void updatePackage(); + +    void updatePermission( const LLSD& permission ); +    LLUUID mExperienceId; +    LLSD mExperienceDetails; +    LLSD mPackage; +    int mSaveCompleteAction; +    bool mDirty; +    bool mForceClose; +}; + +#endif // LL_LLFLOATEREXPERIENCEPROFILE_H diff --git a/indra/newview/llfloaterexperiences.cpp b/indra/newview/llfloaterexperiences.cpp new file mode 100644 index 0000000000..1654419826 --- /dev/null +++ b/indra/newview/llfloaterexperiences.cpp @@ -0,0 +1,310 @@ +/**  + * @file llfloaterexperiences.cpp + * @brief LLFloaterExperiences class implementation + * + * $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 "llviewerprecompiledheaders.h" + +#include "llpanelexperiences.h" +#include "llfloaterexperiences.h" +#include "llagent.h" +#include "llfloaterregioninfo.h" +#include "lltabcontainer.h" +#include "lltrans.h" +#include "llexperiencecache.h" +#include "llevents.h" +#include "llnotificationsutil.h" +#include "llpanelexperiencelog.h" + + + +#define SHOW_RECENT_TAB (0) + +class LLExperienceListResponder : public LLHTTPClient::Responder +{ +public: +    typedef std::map<std::string, std::string> NameMap; +    LLExperienceListResponder(const LLHandle<LLFloaterExperiences>& parent, NameMap& nameMap, const std::string& errorMessage="ErrorMessage"):mParent(parent),mErrorMessage(errorMessage) +    { +        mNameMap.swap(nameMap); +    } + +    LLHandle<LLFloaterExperiences> mParent; +    NameMap mNameMap; +	const std::string mErrorMessage; +    virtual void result(const LLSD& content) +    { +        if(mParent.isDead()) +            return; + +        LLFloaterExperiences* parent=mParent.get(); +        LLTabContainer* tabs = parent->getChild<LLTabContainer>("xp_tabs"); +  +        NameMap::iterator it = mNameMap.begin(); +        while(it != mNameMap.end()) +        { +            if(content.has(it->first)) +            { +                LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName(it->second); +                if(tab) +                { +                    const LLSD& ids = content[it->first]; +                    tab->setExperienceList(ids); +					tab->enableButton(ids.beginArray() == ids.endArray()); +                } +            } +            ++it; +        } +    } + +	virtual void error(U32 status, const std::string& reason) +	{ +		LLSD subs; +		subs["ERROR_MESSAGE"] = reason; +		LLNotificationsUtil::add(mErrorMessage, subs); +	} +}; + + + +LLFloaterExperiences::LLFloaterExperiences(const LLSD& data) +	:LLFloater(data) +{ +} + +LLPanelExperiences* LLFloaterExperiences::addTab(const std::string& name, bool select) +{ +	LLPanelExperiences* newPanel = LLPanelExperiences::create(name); +    getChild<LLTabContainer>("xp_tabs")->addTabPanel(LLTabContainer::TabPanelParams(). +        panel(newPanel). +        label(LLTrans::getString(name)). +        select_tab(select)); + +	return newPanel; +} + +BOOL LLFloaterExperiences::postBuild() +{ +    addTab("Allowed_Experiences_Tab", true); +    addTab("Blocked_Experiences_Tab", false); +    addTab("Admin_Experiences_Tab", false); +    addTab("Contrib_Experiences_Tab", false); +	LLPanelExperiences* owned = addTab("Owned_Experiences_Tab", false); +	owned->setButtonAction("acquire", boost::bind(&LLFloaterExperiences::sendPurchaseRequest, this)); +	owned->enableButton(false); +#if SHOW_RECENT_TAB +	addTab("Recent_Experiences_Tab", false); +#endif //SHOW_RECENT_TAB +	getChild<LLTabContainer>("xp_tabs")->addTabPanel(new LLPanelExperienceLog()); +    resizeToTabs(); + +    +    LLEventPumps::instance().obtain("experience_permission").listen("LLFloaterExperiences",  +        boost::bind(&LLFloaterExperiences::updatePermissions, this, _1)); +      +   	return TRUE; +} + + +void LLFloaterExperiences::clearFromRecent(const LLSD& ids) +{ +#if SHOW_RECENT_TAB +    LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs"); + +    LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Recent_Experiences_Tab"); +    if(!tab) +        return; + +    tab->removeExperiences(ids); +#endif // SHOW_RECENT_TAB +} + +void LLFloaterExperiences::setupRecentTabs() +{ +#if SHOW_RECENT_TAB +    LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs"); + +    LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Recent_Experiences_Tab"); +    if(!tab) +        return; + +    LLSD recent; + +    const LLExperienceCache::cache_t& experiences = LLExperienceCache::getCached(); + +    LLExperienceCache::cache_t::const_iterator it = experiences.begin(); +    while( it != experiences.end() ) +    { +        if(!it->second.has(LLExperienceCache::MISSING)) +        { +            recent.append(it->first); +        } +        ++it; +    } + +    tab->setExperienceList(recent); +#endif // SHOW_RECENT_TAB +} + + +void LLFloaterExperiences::resizeToTabs() +{ +    const S32 TAB_WIDTH_PADDING = 16; + +    LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs"); +    LLRect rect = getRect(); +    if(rect.getWidth() < tabs->getTotalTabWidth() + TAB_WIDTH_PADDING) +    { +        rect.mRight = rect.mLeft + tabs->getTotalTabWidth() + TAB_WIDTH_PADDING; +    } +    reshape(rect.getWidth(), rect.getHeight(), FALSE); +} + +void LLFloaterExperiences::refreshContents() +{ +    setupRecentTabs(); + +    LLViewerRegion* region = gAgent.getRegion(); + +    if (region) +    { +        LLExperienceListResponder::NameMap nameMap; +        std::string lookup_url=region->getCapability("GetExperiences");  +        if(!lookup_url.empty()) +        { +            nameMap["experiences"]="Allowed_Experiences_Tab"; +            nameMap["blocked"]="Blocked_Experiences_Tab"; +            LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap)); +        } + +        lookup_url = region->getCapability("GetAdminExperiences");  +        if(!lookup_url.empty()) +        { +            nameMap["experience_ids"]="Admin_Experiences_Tab"; +            LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap)); +        } + +        lookup_url = region->getCapability("GetCreatorExperiences");  +        if(!lookup_url.empty()) +        { +            nameMap["experience_ids"]="Contrib_Experiences_Tab"; +            LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap)); +        } + +		lookup_url = region->getCapability("AgentExperiences");  +		if(!lookup_url.empty()) +		{ +			nameMap["experience_ids"]="Owned_Experiences_Tab"; +			LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap)); +		} +    } +} + +void LLFloaterExperiences::onOpen( const LLSD& key ) +{ +    LLViewerRegion* region = gAgent.getRegion(); +    if(region) +    { +        if(region->capabilitiesReceived()) +        { +            refreshContents(); +            return; +        } +        region->setCapabilitiesReceivedCallback(boost::bind(&LLFloaterExperiences::refreshContents, this)); +        return; +    } +} + +bool LLFloaterExperiences::updatePermissions( const LLSD& permission ) +{ +    LLTabContainer* tabs = getChild<LLTabContainer>("xp_tabs"); +    LLUUID experience; +    std::string permission_string; +    if(permission.has("experience")) +    { +        experience = permission["experience"].asUUID(); +        permission_string = permission[experience.asString()]["permission"].asString(); + +    } +    LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Allowed_Experiences_Tab"); +    if(tab) +    { +        if(permission.has("experiences")) +        { +            tab->setExperienceList(permission["experiences"]); +        } +        else if(experience.notNull()) +        { +            if(permission_string != "Allow") +            { +                tab->removeExperience(experience); +            } +            else +            { +                tab->addExperience(experience); +            } +        } +    } +     +    tab = (LLPanelExperiences*)tabs->getPanelByName("Blocked_Experiences_Tab"); +    if(tab) +    { +        if(permission.has("blocked")) +        { +            tab->setExperienceList(permission["blocked"]); +        } +        else if(experience.notNull()) +        { +            if(permission_string != "Block") +            { +                tab->removeExperience(experience); +            } +            else +            { +                tab->addExperience(experience); +            } +        } +    } +    return false; +} + +void LLFloaterExperiences::onClose( bool app_quitting ) +{ +    LLEventPumps::instance().obtain("experience_permission").stopListening("LLFloaterExperiences"); +    LLFloater::onClose(app_quitting); +} + +void LLFloaterExperiences::sendPurchaseRequest() const +{ +	LLViewerRegion* region = gAgent.getRegion(); +	std::string url = region->getCapability("AgentExperiences"); +	if(!url.empty()) +	{ +		LLSD content; + +		LLExperienceListResponder::NameMap nameMap; +		nameMap["experience_ids"]="Owned_Experiences_Tab"; +		LLHTTPClient::post(url, content, new LLExperienceListResponder(getDerivedHandle<LLFloaterExperiences>(), nameMap, "ExperienceAcquireFailed")); +	} +} diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h new file mode 100644 index 0000000000..bb2de2f0db --- /dev/null +++ b/indra/newview/llfloaterexperiences.h @@ -0,0 +1,57 @@ +/**  + * @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 LLPanelExperiences; + +class LLFloaterExperiences : +	public LLFloater +{ +public: +    LLFloaterExperiences(const LLSD& data); +    virtual void onClose(bool app_quitting); + +    virtual void onOpen(const LLSD& key); +protected: +    void clearFromRecent(const LLSD& ids); +    void resizeToTabs(); +	/*virtual*/ BOOL	postBuild(); +    void refreshContents(); +    void setupRecentTabs(); +    LLPanelExperiences* addTab(const std::string& name, bool select); + +    bool updatePermissions(const LLSD& permission); +	void sendPurchaseRequest() const; + +private: + +}; + +#endif //LL_LLFLOATEREXPERIENCES_H diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 73c0963a1d..027815c593 100755 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -92,6 +92,11 @@  #include "llagentui.h"  #include "llmeshrepository.h"  #include "llfloaterregionrestarting.h" +#include "llpanelexperiencelisteditor.h" +#include <boost/function.hpp> +#include "llfloaterexperiencepicker.h" +#include "llexperiencecache.h" +#include "llpanelexperiences.h"  const S32 TERRAIN_TEXTURE_COUNT = 4;  const S32 CORNER_COUNT = 4; @@ -215,6 +220,14 @@ BOOL LLFloaterRegionInfo::postBuild()  	panel->buildFromFile("panel_region_debug.xml");  	mTab->addTabPanel(panel); +	if(!gAgent.getRegion()->getCapability("RegionExperiences").empty()) +	{ +		panel = new LLPanelRegionExperiences; +		mInfoPanels.push_back(panel); +		panel->buildFromFile("panel_region_experiences.xml"); +		mTab->addTabPanel(panel); +	} +	  	gMessageSystem->setHandlerFunc(  		"EstateOwnerMessage",   		&processEstateOwnerRequest); @@ -3464,3 +3477,159 @@ void LLPanelEnvironmentInfo::onRegionSettingsApplied(bool ok)  		LLEnvManagerNew::instance().requestRegionSettings();  	}  } + +BOOL LLPanelRegionExperiences::postBuild() +{ +	mAllowed = setupList("panel_allowed"); +	mTrusted = setupList("panel_trusted"); +	mBlocked = setupList("panel_blocked"); +	return LLPanelRegionInfo::postBuild(); +} + +LLPanelExperienceListEditor* LLPanelRegionExperiences::setupList( const char* control_name ) +{ +	LLPanelExperienceListEditor* child = findChild<LLPanelExperienceListEditor>(control_name); +	if(child) +	{ +		child->getChild<LLTextBox>("text_name")->setText(getString(control_name)); +	} + +	return child; +} + + + + +boost::signals2::connection LLPanelRegionExperiences::processResponse( LLPanelExperienceListEditor* panel, boost::signals2::connection& conn, const LLSD& content ) +{ +	if(conn.connected()) +	{ +		conn.disconnect(); +	} +	panel->setExperienceIds(content); +	 +	return panel->setChangedCallback(boost::bind(&LLPanelRegionExperiences::listChanged, this)); +} + + + +void LLPanelRegionExperiences::processResponse( const LLSD& content ) +{ +	mAllowedConnection=processResponse(mAllowed, mAllowedConnection, content["allowed"]); +	mBlockedConnection=processResponse(mBlocked, mBlockedConnection, content["blocked"]); +	mTrustedConnection=processResponse(mTrusted, mTrustedConnection, content["trusted"]); +	disableButton("apply_btn"); +} + + +class LLRegionExperienceResponder : public LLHTTPClient::Responder +{ +public: +	typedef boost::function<void (const LLSD&)> callback_t; + +	callback_t mCallback; + +	LLRegionExperienceResponder(callback_t callback) : mCallback(callback) { } + +	void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			mCallback(content); +		} +		else +		{ +			llwarns << "experience responder failed [status:" << status << "]: " << content << llendl; +		} +	} +}; + + +void LLPanelRegionExperiences::infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content) +{	 +	if(handle.isDead()) +		return; + +	LLPanelRegionExperiences* floater = handle.get(); +	if (floater) +	{ +		floater->processResponse(content); +	} +} + +bool LLPanelRegionExperiences::FilterExisting(const LLSD& experience) +{ +	LLUUID id = experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); +	return mAllowed->getExperienceIds().find(id) != mAllowed->getExperienceIds().end() || +		mBlocked->getExperienceIds().find(id) != mBlocked->getExperienceIds().end() || +		mTrusted->getExperienceIds().find(id) != mTrusted->getExperienceIds().end() ; +} + + +bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) +{ +	BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); + +	mAllowed->loading(); +	mAllowed->setReadonly(!allow_modify); +	// remove grid-wide experiences +	mAllowed->addFilter(boost::bind(LLFloaterExperiencePicker::FilterWithProperty, _1, LLExperienceCache::PROPERTY_GRID)); +	// and stuff only in another list +	mAllowed->addFilter(boost::bind(&LLPanelRegionExperiences::FilterExisting, this, _1)); + +	mBlocked->loading(); +	mBlocked->setReadonly(!allow_modify); +	// only grid-wide experiences +	mBlocked->addFilter(boost::bind(LLFloaterExperiencePicker::FilterWithoutProperty, _1, LLExperienceCache::PROPERTY_GRID)); +	// but not privileged ones +	mBlocked->addFilter(boost::bind(LLFloaterExperiencePicker::FilterWithProperty, _1, LLExperienceCache::PROPERTY_PRIVILEGED)); +	mBlocked->addFilter(boost::bind(&LLPanelRegionExperiences::FilterExisting, this, _1)); + +	mTrusted->loading(); +	mTrusted->setReadonly(!allow_modify); +	mTrusted->addFilter(boost::bind(&LLPanelRegionExperiences::FilterExisting, this, _1)); + +	std::string url = region->getCapability("RegionExperiences"); +	if (!url.empty()) +	{ +		LLHTTPClient::get(url, new LLRegionExperienceResponder(boost::bind(&LLPanelRegionExperiences::infoCallback,  +			getDerivedHandle<LLPanelRegionExperiences>(), _1))); +	} +	return LLPanelRegionInfo::refreshFromRegion(region); +} + +LLSD LLPanelRegionExperiences::addIds(LLPanelExperienceListEditor* panel) +{ +	LLSD ids; +	const uuid_list_t& id_list = panel->getExperienceIds(); +	for(uuid_list_t::const_iterator it = id_list.begin(); it != id_list.end(); ++it) +	{ +		ids.append(*it); +	} +	return ids; +} + + +BOOL LLPanelRegionExperiences::sendUpdate() +{ +	LLViewerRegion* region = gAgent.getRegion(); +	std::string url = region->getCapability("RegionExperiences"); +	if (!url.empty()) +	{ +		LLSD content; + +		content["allowed"]=addIds(mAllowed); +		content["blocked"]=addIds(mBlocked); +		content["trusted"]=addIds(mTrusted); + +		LLHTTPClient::post(url, content, new LLRegionExperienceResponder(boost::bind(&LLPanelRegionExperiences::infoCallback,  +			getDerivedHandle<LLPanelRegionExperiences>(), _1))); +	} + +	return TRUE; +} + +void LLPanelRegionExperiences::listChanged() +{ +	onChangeAnything(); +} diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index f0499f1903..d2bbcbea61 100755 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -60,6 +60,8 @@ class LLPanelRegionDebugInfo;  class LLPanelRegionTerrainInfo;  class LLPanelEstateInfo;  class LLPanelEstateCovenant; +class LLPanelExperienceListEditor; +class LLPanelExperiences;  class LLEventTimer;  class LLEnvironmentSettings; @@ -450,4 +452,35 @@ private:  	LLComboBox*		mDayCyclePresetCombo;  }; +class LLPanelRegionExperiences : public LLPanelRegionInfo +{ +	LOG_CLASS(LLPanelEnvironmentInfo); + +public: +	LLPanelRegionExperiences(){} +	/*virtual*/ BOOL postBuild(); +	virtual BOOL sendUpdate(); + +	static void infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content); +	void listChanged(); +	bool refreshFromRegion(LLViewerRegion* region); +	void sendPurchaseRequest()const; +private: +	void processResponse( const LLSD& content ); +	boost::signals2::connection processResponse( LLPanelExperienceListEditor* panel, boost::signals2::connection& connection, const LLSD& content); +	void refreshRegionExperiences(); + +	LLPanelExperienceListEditor* setupList(const char* control_name); +	static LLSD addIds( LLPanelExperienceListEditor* panel ); +	bool FilterExisting(const LLSD& experience ); + + +	LLPanelExperienceListEditor* mTrusted; +	boost::signals2::connection  mTrustedConnection; +	LLPanelExperienceListEditor* mAllowed; +	boost::signals2::connection  mAllowedConnection; +	LLPanelExperienceListEditor* mBlocked; +	boost::signals2::connection  mBlockedConnection; +}; +  #endif diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 35b63c5480..27e26d4fda 100755 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -81,6 +81,7 @@  #include "llagentui.h"  #include "lltrans.h" +#include "llexperiencecache.h"  const U32 INCLUDE_SCREENSHOT  = 0x01 << 0; @@ -227,6 +228,30 @@ void LLFloaterReporter::enableControls(BOOL enable)  	getChildView("cancel_btn")->setEnabled(enable);  } +void LLFloaterReporter::getExperienceInfo(const LLUUID& experience_id) +{ +	mExperienceID = experience_id; + +	if (LLUUID::null != mExperienceID) +	{ +		const LLSD& experience = LLExperienceCache::get(mExperienceID); +		std::stringstream desc; + +		if(experience.isDefined()) +		{ +			setFromAvatarID(experience[LLExperienceCache::AGENT_ID]); +			desc << "\nExperience id: " << mExperienceID; +		} +		else +		{ +			desc << "Unable to retrieve details for id: "<< mExperienceID; +		} +		 +		LLUICtrl* details = getChild<LLUICtrl>("details_edit"); +		details->setValue(details->getValue().asString()+desc.str()); +	} +} +  void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)  {  	// TODO --  @@ -472,7 +497,7 @@ void LLFloaterReporter::showFromMenu(EReportType report_type)  }  // static -void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_name) +void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_name, const LLUUID& experience_id)  {  	LLFloaterReporter* f = LLFloaterReg::showTypedInstance<LLFloaterReporter>("reporter"); @@ -485,6 +510,23 @@ void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_  	{  		f->setFromAvatarID(object_id);  	} +	if(experience_id.notNull()) +	{ +		f->getExperienceInfo(experience_id); +	} + +	// Need to deselect on close +	f->mDeselectOnClose = TRUE; + +	f->openFloater(); +} + + + +void LLFloaterReporter::showFromExperience( const LLUUID& experience_id ) +{ +	LLFloaterReporter* f = LLFloaterReg::showTypedInstance<LLFloaterReporter>("reporter"); +	f->getExperienceInfo(experience_id);  	// Need to deselect on close  	f->mDeselectOnClose = TRUE; @@ -494,9 +536,9 @@ void LLFloaterReporter::show(const LLUUID& object_id, const std::string& avatar_  // static -void LLFloaterReporter::showFromObject(const LLUUID& object_id) +void LLFloaterReporter::showFromObject(const LLUUID& object_id, const LLUUID& experience_id)  { -	show(object_id); +	show(object_id, LLStringUtil::null, experience_id);  }  // static @@ -854,6 +896,7 @@ void LLFloaterReporter::setPosBox(const LLVector3d &pos)  	getChild<LLUICtrl>("pos_field")->setValue(pos_string);  } +  // void LLFloaterReporter::setDescription(const std::string& description, LLMeanCollisionData *mcd)  // {  // 	LLFloaterReporter *self = LLFloaterReg::findTypedInstance<LLFloaterReporter>("reporter"); diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index d54e7f6ab0..5eb5c20665 100755 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -88,8 +88,9 @@ public:  	// Enables all buttons  	static void showFromMenu(EReportType report_type); -	static void showFromObject(const LLUUID& object_id); +	static void showFromObject(const LLUUID& object_id, const LLUUID& experience_id = LLUUID::null);  	static void showFromAvatar(const LLUUID& avatar_id, const std::string avatar_name); +	static void showFromExperience(const LLUUID& experience_id);  	static void onClickSend			(void *userdata);  	static void onClickCancel		(void *userdata); @@ -106,7 +107,7 @@ public:  	void setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id);  private: -	static void show(const LLUUID& object_id, const std::string& avatar_name = LLStringUtil::null); +	static void show(const LLUUID& object_id, const std::string& avatar_name = LLStringUtil::null, const LLUUID& experience_id = LLUUID::null);  	void takeScreenshot();  	void sendReportViaCaps(std::string url); @@ -118,6 +119,7 @@ private:  	void sendReportViaCaps(std::string url, std::string sshot_url, const LLSD & report);  	void setPosBox(const LLVector3d &pos);  	void enableControls(BOOL own_avatar); +	void getExperienceInfo(const LLUUID& object_id);  	void getObjectInfo(const LLUUID& object_id);  	void callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names);  	void setFromAvatarID(const LLUUID& avatar_id); @@ -128,6 +130,7 @@ private:  	LLUUID 			mObjectID;  	LLUUID			mScreenID;  	LLUUID			mAbuserID; +	LLUUID			mExperienceID;  	// Store the real name, not the link, for upstream reporting  	std::string		mOwnerName;  	BOOL			mDeselectOnClose; diff --git a/indra/newview/llpanelexperiencelisteditor.cpp b/indra/newview/llpanelexperiencelisteditor.cpp new file mode 100644 index 0000000000..37394936ee --- /dev/null +++ b/indra/newview/llpanelexperiencelisteditor.cpp @@ -0,0 +1,213 @@ +/** + * @file llpanelexperiencelisteditor.cpp + * @brief Editor for building a list of experiences + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llpanelexperiencelisteditor.h" + +#include "llbutton.h" +#include "llexperiencecache.h" +#include "llfloaterexperiencepicker.h" +#include "llfloaterreg.h" +#include "llhandle.h" +#include "llscrolllistctrl.h" +#include "llviewerregion.h" +#include "llagent.h" + + +static LLPanelInjector<LLPanelExperienceListEditor> t_panel_experience_list_editor("panel_experience_list_editor"); + + +LLPanelExperienceListEditor::LLPanelExperienceListEditor() +	:mItems(NULL) +	,mProfile(NULL) +	,mRemove(NULL) +	,mChangedCallback(NULL) +	,mReadonly(false) +{ +} + +BOOL LLPanelExperienceListEditor::postBuild() +{ +	mItems = getChild<LLScrollListCtrl>("experience_list"); +	mAdd = getChild<LLButton>("btn_add"); +	mRemove = getChild<LLButton>("btn_remove"); +	mProfile = getChild<LLButton>("btn_profile"); + +	childSetAction("btn_add", boost::bind(&LLPanelExperienceListEditor::onAdd, this)); +	childSetAction("btn_remove", boost::bind(&LLPanelExperienceListEditor::onRemove, this)); +	childSetAction("btn_profile", boost::bind(&LLPanelExperienceListEditor::onProfile, this)); + +	mItems->setCommitCallback(boost::bind(&LLPanelExperienceListEditor::checkButtonsEnabled, this)); +	 +	checkButtonsEnabled(); +	return TRUE; +} + +const uuid_list_t& LLPanelExperienceListEditor::getExperienceIds() const +{ +	return mExperienceIds; +} + +void LLPanelExperienceListEditor::addExperienceIds( const uuid_vec_t& experience_ids ) +{ +	mExperienceIds.insert(experience_ids.begin(), experience_ids.end()); +	onItems(); +} + + +void LLPanelExperienceListEditor::setExperienceIds( const LLSD& experience_ids ) +{ +	mExperienceIds.clear(); +	mExperienceIds.insert(experience_ids.beginArray(), experience_ids.endArray()); +	onItems(); +} + +void LLPanelExperienceListEditor::addExperience( const LLUUID& id ) +{ +	mExperienceIds.insert(id); +	onItems(); +} +void LLPanelExperienceListEditor::onAdd() +{ +	if(!mPicker.isDead()) +	{ +		mPicker.get()->setFrontmost(TRUE); +	} +	else +	{ +		mKey.generateNewID(); + +		LLFloaterExperiencePicker* picker=LLFloaterExperiencePicker::show(boost::bind(&LLPanelExperienceListEditor::addExperienceIds, this, _1), mKey, FALSE, TRUE, mAdd); +		picker->addFilters(mFilters.begin(), mFilters.end()); + +		mPicker = picker->getDerivedHandle<LLFloaterExperiencePicker>(); +	} +} + + +void LLPanelExperienceListEditor::onRemove() +{ +	std::vector<LLScrollListItem*> items= mItems->getAllSelected(); +	std::vector<LLScrollListItem*>::iterator it = items.begin(); +	for(/**/; it != items.end(); ++it) +	{ +		if((*it) != NULL) +		{ +			mExperienceIds.erase((*it)->getValue()); +		} +	} +	onItems(); +} + +void LLPanelExperienceListEditor::onProfile() +{ +	LLScrollListItem* item = mItems->getFirstSelected(); +	if(item) +	{ +		LLFloaterReg::showInstance("experience_profile", item->getUUID(), true); +	} +} + +void LLPanelExperienceListEditor::checkButtonsEnabled() +{ +	mAdd->setEnabled(!mReadonly); +	int selected = mItems->getNumSelected(); +	mRemove->setEnabled(!mReadonly && selected>0); +	mProfile->setEnabled(selected==1); +} + +void LLPanelExperienceListEditor::onItems() +{ +	mItems->deleteAllItems(); + +	LLSD item; +	uuid_list_t::iterator it = mExperienceIds.begin(); +	for(/**/; it != mExperienceIds.end(); ++it) +	{ +		const LLUUID& experience = *it; +		item["id"]=experience; +		LLSD& columns = item["columns"]; +		columns[0]["column"] = "experience_name"; +		columns[0]["value"] = getString("loading"); +		mItems->addElement(item); + +		LLExperienceCache::get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback,  +			getDerivedHandle<LLPanelExperienceListEditor>(), _1)); +	} + + +	if(mItems->getItemCount() == 0) +	{ +		mItems->setCommentText(getString("no_results")); +	} + + +	checkButtonsEnabled(); +	if(mChangedCallback) +	{ +		(*mChangedCallback)(); +	} +} + +void LLPanelExperienceListEditor::experienceDetailsCallback( LLHandle<LLPanelExperienceListEditor> panel, const LLSD& experience ) +{ +	if(!panel.isDead()) +	{ +		panel.get()->onExperienceDetails(experience); +	} +} + +void LLPanelExperienceListEditor::onExperienceDetails( const LLSD& experience ) +{ +	LLScrollListItem* item = mItems->getItem(experience[LLExperienceCache::EXPERIENCE_ID]); +	if(!item) +		return; + +	item->getColumn(0)->setValue(experience[LLExperienceCache::NAME]); +} + +LLPanelExperienceListEditor::~LLPanelExperienceListEditor() +{ +	if(!mPicker.isDead()) +	{ +		mPicker.get()->closeFloater(); +	} +	delete mChangedCallback; +} + +void LLPanelExperienceListEditor::loading() +{ +	mItems->clear(); +	mItems->setCommentText( getString("loading")); +} + +void LLPanelExperienceListEditor::setReadonly( bool val ) +{ +	mReadonly = val; +	setCtrlsEnabled(!mReadonly); +	checkButtonsEnabled(); +} diff --git a/indra/newview/llpanelexperiencelisteditor.h b/indra/newview/llpanelexperiencelisteditor.h new file mode 100644 index 0000000000..4135f399ce --- /dev/null +++ b/indra/newview/llpanelexperiencelisteditor.h @@ -0,0 +1,96 @@ +/** +* @file llpanelexperiencelisteditor.cpp +* @brief Editor for building a list of experiences +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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_LLPANELEXPERIENCELISTEDITOR_H +#define LL_LLPANELEXPERIENCELISTEDITOR_H + +#include "llpanel.h" +#include "lluuid.h" +#include <set> + +class LLScrollListCtrl; +class LLButton; +class LLFloaterExperiencePicker; + +class LLPanelExperienceListEditor : public LLPanel +{ +public: + +	typedef boost::signals2::signal<void () > list_changed_signal_t; +	// filter function for experiences, return true if the experience should be hidden. +	typedef boost::function<bool (const LLSD&)> filter_function; +	typedef std::vector<filter_function> filter_list; +	typedef LLHandle<LLFloaterExperiencePicker> PickerHandle; +	LLPanelExperienceListEditor(); +	~LLPanelExperienceListEditor(); +	BOOL postBuild(); + +	void loading(); + +	const uuid_list_t& getExperienceIds()const; +	void setExperienceIds(const LLSD& experience_ids); +	void addExperienceIds(const uuid_vec_t& experience_ids); + +	void addExperience(const LLUUID& id); +	 +	boost::signals2::connection setChangedCallback(list_changed_signal_t::slot_type cb ) +	{ +		if (!mChangedCallback) mChangedCallback = new list_changed_signal_t(); +		return mChangedCallback->connect(cb); +	} + +	bool getReadonly() const { return mReadonly; } +	void setReadonly(bool val); + + +	void addFilter(filter_function func){mFilters.push_back(func);} +private: + +	void onItems(); +	void onRemove(); +	void onAdd(); +	void onProfile(); + +	void checkButtonsEnabled(); +	static void experienceDetailsCallback( LLHandle<LLPanelExperienceListEditor> panel, const LLSD& experience ); +	void onExperienceDetails( const LLSD& experience ); +	void processResponse( const LLSD& content ); +	uuid_list_t mExperienceIds; + + +	LLScrollListCtrl*			mItems; +	filter_list					mFilters; +	LLButton*					mAdd; +	LLButton*					mRemove; +	LLButton*					mProfile; +	PickerHandle				mPicker; +	list_changed_signal_t*		mChangedCallback; +	LLUUID						mKey; +	bool						mReadonly; + +}; + +#endif //LL_LLPANELEXPERIENCELISTEDITOR_H diff --git a/indra/newview/llpanelexperiencelog.cpp b/indra/newview/llpanelexperiencelog.cpp new file mode 100644 index 0000000000..eeabab4c44 --- /dev/null +++ b/indra/newview/llpanelexperiencelog.cpp @@ -0,0 +1,259 @@ +/**  + * @file llpanelexperiencelog.cpp + * @brief llpanelexperiencelog + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llpanelexperiencelog.h" + +#include "llexperiencelog.h" +#include "llexperiencecache.h" +#include "llbutton.h" +#include "llscrolllistctrl.h" +#include "llcombobox.h" +#include "llspinctrl.h" +#include "llcheckboxctrl.h" +#include "llfloaterreg.h" +#include "llfloaterreporter.h" +#include "llinventoryfunctions.h" + + +#define BTN_PROFILE_XP "btn_profile_xp" +#define BTN_REPORT_XP "btn_report_xp" + +static LLPanelInjector<LLPanelExperienceLog> register_experiences_panel("experience_log"); + + +LLPanelExperienceLog::LLPanelExperienceLog(  ) +	: mEventList(NULL) +	, mPageSize(25) +	, mCurrentPage(0) +{ +	buildFromFile("panel_experience_log.xml"); +} + + +BOOL LLPanelExperienceLog::postBuild( void ) +{ +	LLExperienceLog* log = LLExperienceLog::getInstance(); +	mEventList = getChild<LLScrollListCtrl>("experience_log_list"); +	mEventList->setCommitCallback(boost::bind(&LLPanelExperienceLog::onSelectionChanged, this)); +	mEventList->setDoubleClickCallback( boost::bind(&LLPanelExperienceLog::onProfileExperience, this)); + +	getChild<LLButton>("btn_clear")->setCommitCallback(boost::bind(&LLExperienceLog::clear, log)); +	getChild<LLButton>("btn_clear")->setCommitCallback(boost::bind(&LLPanelExperienceLog::refresh, this)); + +	getChild<LLButton>(BTN_PROFILE_XP)->setCommitCallback(boost::bind(&LLPanelExperienceLog::onProfileExperience, this)); +	getChild<LLButton>(BTN_REPORT_XP )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onReportExperience, this)); +	getChild<LLButton>("btn_notify"  )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onNotify, this)); +	getChild<LLButton>("btn_next"    )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onNext, this)); +	getChild<LLButton>("btn_prev"    )->setCommitCallback(boost::bind(&LLPanelExperienceLog::onPrev, this)); + +	LLCheckBoxCtrl* check = getChild<LLCheckBoxCtrl>("notify_all"); +	check->set(log->getNotifyNewEvent()); +	check->setCommitCallback(boost::bind(&LLPanelExperienceLog::notifyChanged, this)); + + +	LLSpinCtrl* spin = getChild<LLSpinCtrl>("logsizespinner"); +	spin->set(log->getMaxDays()); +	spin->setCommitCallback(boost::bind(&LLPanelExperienceLog::logSizeChanged, this)); + +	mPageSize = log->getPageSize(); +	refresh(); +	mNewEvent = LLExperienceLog::instance().addUpdateSignal(boost::bind(&LLPanelExperienceLog::refresh, this)); +	return TRUE; +} + +LLPanelExperienceLog* LLPanelExperienceLog::create() +{ +	return new LLPanelExperienceLog(); +} + +void LLPanelExperienceLog::refresh() +{ +	S32 selected = mEventList->getFirstSelectedIndex(); +	mEventList->deleteAllItems(); +	const LLSD& events = LLExperienceLog::instance().getEvents(); + +	if(events.size() == 0) +	{ +		mEventList->setCommentText(getString("no_events")); +		return; +	} + +	setAllChildrenEnabled(FALSE); + +	LLSD item; +	bool waiting = false; +	LLUUID waiting_id; + +	int itemsToSkip = mPageSize*mCurrentPage; +	int items = 0; +	bool moreItems = false; + +	for(LLSD::map_const_iterator day = events.beginMap(); day != events.endMap() ; ++day) +	{ +		const LLSD& dayArray = day->second; +		int size = dayArray.size(); +		if(itemsToSkip > size) +		{ +			itemsToSkip -= size; +			continue; +		} +		if(items >= mPageSize && size > 0) +		{ +			moreItems = true; +			break; +		} +		for(int i = itemsToSkip ; i < dayArray.size(); i++) +		{ +			if(items >= mPageSize) +			{ +				moreItems = true; +				break; +			} +			const LLSD& event = dayArray[i]; +			LLUUID id = event[LLExperienceCache::EXPERIENCE_ID].asUUID(); +			const LLSD& experience = LLExperienceCache::get(id); +			if(experience.isUndefined()){ +				waiting = true; +				waiting_id = id; +			} +			if(!waiting) +			{ +				item["id"] = event; + +				LLSD& columns = item["columns"]; +				columns[0]["column"] = "time"; +				columns[0]["value"] = day->first+event["Time"].asString(); +				columns[1]["column"] = "event"; +				columns[1]["value"] = LLExperienceLog::getPermissionString(event, "ExperiencePermissionShort"); +				columns[2]["column"] = "experience_name"; +				columns[2]["value"] = experience[LLExperienceCache::NAME].asString(); +				columns[3]["column"] = "object_name"; +				columns[3]["value"] = event["ObjectName"].asString(); +				mEventList->addElement(item); +			} +			++items; +		} +	} +	if(waiting) +	{ +		mEventList->deleteAllItems(); +		mEventList->setCommentText(getString("loading")); +		LLExperienceCache::get(waiting_id, boost::bind(&LLPanelExperienceLog::refresh, this)); +	} +	else +	{ +		setAllChildrenEnabled(TRUE); + +		mEventList->setEnabled(TRUE); +		getChild<LLButton>("btn_next")->setEnabled(moreItems); +		getChild<LLButton>("btn_prev")->setEnabled(mCurrentPage>0); +		getChild<LLButton>("btn_clear")->setEnabled(mEventList->getItemCount()>0); +		if(selected<0) +		{ +			selected = 0; +		} +		mEventList->selectNthItem(selected); +		onSelectionChanged(); +	} +} + +void LLPanelExperienceLog::onProfileExperience() +{ +	LLSD& event = getSelectedEvent(); +	if(event.isDefined()) +	{ +		LLFloaterReg::showInstance("experience_profile", event[LLExperienceCache::EXPERIENCE_ID].asUUID(), true); +	} +} + +void LLPanelExperienceLog::onReportExperience() +{ +	LLSD& event = getSelectedEvent(); +	if(event.isDefined()) +	{ +		LLFloaterReporter::showFromExperience(event[LLExperienceCache::EXPERIENCE_ID].asUUID()); +	} +} + +void LLPanelExperienceLog::onNotify() +{ +	LLSD& event = getSelectedEvent(); +	if(event.isDefined()) +	{ +		LLExperienceLog::instance().notify(event); +	} +} + +void LLPanelExperienceLog::onNext() +{ +	mCurrentPage++; +	refresh(); +} + +void LLPanelExperienceLog::onPrev() +{ +	if(mCurrentPage>0) +	{ +		mCurrentPage--; +		refresh(); +	} +} + +void LLPanelExperienceLog::notifyChanged() +{ +	LLExperienceLog::instance().setNotifyNewEvent(getChild<LLCheckBoxCtrl>("notify_all")->get()); +} + +void LLPanelExperienceLog::logSizeChanged() +{ +	int value = (int)(getChild<LLSpinCtrl>("logsizespinner")->get()); +	bool dirty = value > 0 && value < LLExperienceLog::instance().getMaxDays(); +	LLExperienceLog::instance().setMaxDays(value); +	if(dirty) +	{ +		refresh(); +	} +} + +void LLPanelExperienceLog::onSelectionChanged() +{ +	bool enabled = (1 == mEventList->getNumSelected()); +	getChild<LLButton>(BTN_REPORT_XP)->setEnabled(enabled); +	getChild<LLButton>(BTN_PROFILE_XP)->setEnabled(enabled); +	getChild<LLButton>("btn_notify")->setEnabled(enabled); +} + +LLSD LLPanelExperienceLog::getSelectedEvent() +{ +	LLScrollListItem* item = mEventList->getFirstSelected(); +	if(item) +	{ +		return item->getValue(); +	} +	return LLSD(); +} diff --git a/indra/newview/llpanelexperiencelog.h b/indra/newview/llpanelexperiencelog.h new file mode 100644 index 0000000000..4135d9cac9 --- /dev/null +++ b/indra/newview/llpanelexperiencelog.h @@ -0,0 +1,64 @@ +/**  + * @file llpanelexperiencelog.h + * @brief llpanelexperiencelog and related class definitions + * + * $LicenseInfo:firstyear=2014&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2014, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + + +#ifndef LL_LLPANELEXPERIENCELOG_H +#define LL_LLPANELEXPERIENCELOG_H + +#include "llpanel.h" +class LLScrollListCtrl; + +class LLPanelExperienceLog +	: public LLPanel  +{ +public: + +	LLPanelExperienceLog(); + +	static LLPanelExperienceLog* create(); + +	/*virtual*/ BOOL postBuild(void); + +	void refresh(); +protected: +	void logSizeChanged(); +	void notifyChanged(); +	void onNext(); +	void onNotify(); +	void onPrev(); +	void onProfileExperience(); +	void onReportExperience(); +	void onSelectionChanged(); + +	LLSD getSelectedEvent(); +private: +	LLScrollListCtrl* mEventList; +	U32 mPageSize; +	U32 mCurrentPage; +	boost::signals2::scoped_connection mNewEvent; +}; + +#endif // LL_LLPANELEXPERIENCELOG_H diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp new file mode 100644 index 0000000000..0d67dbf916 --- /dev/null +++ b/indra/newview/llpanelexperiences.cpp @@ -0,0 +1,171 @@ +/**  + * @file llpanelexperiences.cpp + * @brief LLPanelExperiences class implementation + * + * $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 "llpanelprofile.h" +#include "lluictrlfactory.h" +#include "llexperiencecache.h" +#include "llagent.h" + +#include "llpanelexperiences.h" +#include "llslurl.h" +#include "lllayoutstack.h" + + +static LLPanelInjector<LLPanelExperiences> register_experiences_panel("experiences_panel"); + + +LLPanelExperiences::LLPanelExperiences(  ) +	: mExperiencesList(NULL) +{ +    buildFromFile("panel_experiences.xml"); +} + +BOOL LLPanelExperiences::postBuild( void ) +{ +	mExperiencesList = getChild<LLFlatListView>("experiences_list"); +	if(hasString("no_experiences")) +	{ +		mExperiencesList->setNoItemsCommentText(getString("no_experiences")); +	} + +	return TRUE; +} + + + +LLExperienceItem* LLPanelExperiences::getSelectedExperienceItem() +{ +	LLPanel* selected_item = mExperiencesList->getSelectedItem(); +	if (!selected_item) return NULL; + +	return dynamic_cast<LLExperienceItem*>(selected_item); +} + +void LLPanelExperiences::setExperienceList( const LLSD& experiences ) +{ +    mExperiencesList->clear(); + +    LLSD::array_const_iterator it = experiences.beginArray(); +    for( /**/ ; it != experiences.endArray(); ++it) +    { +        LLUUID public_key = it->asUUID(); +        LLExperienceItem* item = new LLExperienceItem(); + +        item->init(public_key); +        mExperiencesList->addItem(item, public_key); +    } +} + +LLPanelExperiences* LLPanelExperiences::create(const std::string& name) +{ +    LLPanelExperiences* panel= new LLPanelExperiences(); +    panel->setName(name); +    return panel; +} + +void LLPanelExperiences::removeExperiences( const LLSD& ids ) +{ +    LLSD::array_const_iterator it = ids.beginArray(); +    for( /**/ ; it != ids.endArray(); ++it) +    { +        removeExperience(it->asUUID()); +    } +} + +void LLPanelExperiences::removeExperience( const LLUUID& id ) +{ +    mExperiencesList->removeItemByUUID(id); +} + +void LLPanelExperiences::addExperience( const LLUUID& id ) +{ +    if(!mExperiencesList->getItemByValue(id)) +    { +        LLExperienceItem* item = new LLExperienceItem(); + +        item->init(id); +        mExperiencesList->addItem(item, id); +    } +} + +void LLPanelExperiences::setButtonAction(const std::string& label, const commit_signal_t::slot_type& cb ) +{ +	if(label.empty()) +	{ +		getChild<LLLayoutPanel>("button_panel")->setVisible(false); +	} +	else +	{ +		getChild<LLLayoutPanel>("button_panel")->setVisible(true); +		LLButton* child = getChild<LLButton>("btn_action"); +		child->setCommitCallback(cb); +		child->setLabel(getString(label)); +	} +} + +void LLPanelExperiences::enableButton( bool enable ) +{ +	getChild<LLButton>("btn_action")->setEnabled(enable); +} + + +LLExperienceItem::LLExperienceItem() +{ +	buildFromFile("panel_experience_list_item.xml"); +} + +void LLExperienceItem::init( const LLUUID& id) +{ +    getChild<LLUICtrl>("experience_name")->setValue(LLSLURL("experience", id, "profile").getSLURLString()); +} + + +LLExperienceItem::~LLExperienceItem() +{ + +} + +void LLPanelSearchExperiences::doSearch() +{ + +} + +LLPanelSearchExperiences* LLPanelSearchExperiences::create( const std::string& name ) +{ +    LLPanelSearchExperiences* panel= new LLPanelSearchExperiences(); +    panel->getChild<LLPanel>("results")->addChild(LLPanelExperiences::create(name)); +    return panel; +} + +BOOL LLPanelSearchExperiences::postBuild( void ) +{ +    childSetAction("search_button", boost::bind(&LLPanelSearchExperiences::doSearch, this)); +    return TRUE; +} diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h new file mode 100644 index 0000000000..c525b1f548 --- /dev/null +++ b/indra/newview/llpanelexperiences.h @@ -0,0 +1,85 @@ +/**  + * @file llpanelexperiences.h + * @brief LLPanelExperiences class definition + * + * $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_LLPANELEXPERIENCES_H +#define LL_LLPANELEXPERIENCES_H + +#include "llaccordionctrltab.h" +#include "llflatlistview.h" +#include "llpanelavatar.h" + +class LLExperienceItem; +class LLPanelProfile;  + + +class LLPanelSearchExperiences  +    : public LLPanel +{ +public: +    LLPanelSearchExperiences(){} +    static LLPanelSearchExperiences* create(const std::string& name); +    /*virtual*/ BOOL postBuild(void); + +    void doSearch(); +}; + +class LLPanelExperiences +	: public LLPanel  +{ +public: +    LLPanelExperiences(); + +    static LLPanelExperiences* create(const std::string& name); + +	/*virtual*/ BOOL postBuild(void); +	/*virtual*/ void onClosePanel(); + +    void setExperienceList(const LLSD& experiences); + +    LLExperienceItem* getSelectedExperienceItem(); +    void removeExperiences( const LLSD& ids ); +    void removeExperience( const LLUUID& id); +    void addExperience( const LLUUID& id); +	void setButtonAction(const std::string& label, const commit_signal_t::slot_type& cb); +	void enableButton(bool enable); +protected: + +private: +	LLFlatListView* mExperiencesList; +}; + + +class LLExperienceItem  +	: public LLPanel +{ +public: +	LLExperienceItem(); +	~LLExperienceItem(); + +	void init(const LLUUID& experience_id); +protected: +}; +#endif // LL_LLPANELEXPERIENCES_H diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index e533be7f24..a4798f3911 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -87,6 +87,9 @@  #include "llviewercontrol.h"  #include "llappviewer.h"  #include "llfloatergotoline.h" +#include "llexperiencecache.h" +#include "llfloaterexperienceprofile.h" +#include "llexperienceassociationresponder.h"  const std::string HELLO_LSL =  	"default\n" @@ -119,6 +122,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  /// --------------------------------------------------------------------------- @@ -392,6 +415,55 @@ 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::onViewProfile( LLUICtrl *ui, void* userdata ) +{ +    LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; + +    LLUUID id; +    if(self->mExperienceEnabled->get()) +    { +        id=self->mScriptEd->getAssociatedExperience(); +        if(id.notNull()) +        { +             LLFloaterReg::showInstance("experience_profile", id, true); +        } +    } + +} + +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"); @@ -407,7 +479,7 @@ BOOL LLScriptEdCore::postBuild()  	childSetAction("Edit_btn", boost::bind(&LLScriptEdCore::openInExternalEditor, this));  	initMenu(); - +	  	std::vector<std::string> funcs;  	std::vector<std::string> tooltips; @@ -1212,6 +1284,130 @@ 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() +{ +    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(getIsModifiable()); +		mExperiences->setVisible(TRUE); +		mExperienceEnabled->set(TRUE); +        buildExperienceList(); +    } +} + +void LLLiveLSLEditor::buildExperienceList() +{ +    mExperiences->clearRows(); +    bool foundAssociated=false; +	const LLUUID& associated = mScriptEd->getAssociatedExperience(); +	LLUUID last; +	LLScrollListItem* item; +    for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it) +    { +        LLUUID id = it->asUUID(); +		EAddPosition position = ADD_BOTTOM; +		if(id == associated) +		{ +			foundAssociated = true; +			position = ADD_TOP; +		} +		 +		const LLSD& experience = LLExperienceCache::get(id); +		if(experience.isUndefined()) +		{ +			mExperiences->add(getString("loading"), id, position); +			last = id; +		} +		else +		{ +			mExperiences->add(experience[LLExperienceCache::NAME].asString(), id, position); +		}  +    } + +    if(!foundAssociated ) +	{ +		const LLSD& experience = LLExperienceCache::get(associated); +		if(experience.isDefined()) +		{ +			item=mExperiences->add(experience[LLExperienceCache::NAME].asString(), associated, ADD_TOP); +		}  +		else +		{ +			item=mExperiences->add(getString("loading"), associated, ADD_TOP); +			last = associated; +		} +		item->setEnabled(FALSE); +    } + +	if(last.notNull()) +	{ +		mExperiences->setEnabled(FALSE); +		LLExperienceCache::get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this));   +	} +	else +	{ +		mExperiences->setEnabled(TRUE); +		getChild<LLButton>("view_profile")->setVisible(TRUE); +	} +} + + +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  /// --------------------------------------------------------------------------- @@ -1771,6 +1967,16 @@ 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); +    childSetCommitCallback("view_profile", onViewProfile, this); +     +  	return LLPreview::postBuild();  } @@ -1814,59 +2020,58 @@ void LLLiveLSLEditor::loadAsset()  		if(object)  		{  			LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(object->getInventoryObject(mItemUUID)); -			if(item  -				&& (gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) -				   || gAgent.isGodlike())) -			{ -				mItem = new LLViewerInventoryItem(item); -				//llinfos << "asset id " << mItem->getAssetUUID() << llendl; -			} -			if(!gAgent.isGodlike() -			   && (item -				   && (!gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE) -					   || !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)))) -			{ -				mItem = new LLViewerInventoryItem(item); -				mScriptEd->setScriptText(getString("not_allowed"), FALSE); -				mScriptEd->mEditor->makePristine(); -				mScriptEd->enableSave(FALSE); -				mAssetStatus = PREVIEW_ASSET_LOADED; -			} -			else if(item && mItem.notNull()) -			{ -				// request the text from the object -				LLUUID* user_data = new LLUUID(mItemUUID); //  ^ mObjectUUID -				gAssetStorage->getInvItemAsset(object->getRegion()->getHost(), -											   gAgent.getID(), -											   gAgent.getSessionID(), -											   item->getPermissions().getOwner(), -											   object->getID(), -											   item->getUUID(), -											   item->getAssetUUID(), -											   item->getType(), -											   &LLLiveLSLEditor::onLoadComplete, -											   (void*)user_data, -											   TRUE); -				LLMessageSystem* msg = gMessageSystem; -				msg->newMessageFast(_PREHASH_GetScriptRunning); -				msg->nextBlockFast(_PREHASH_Script); -				msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID); -				msg->addUUIDFast(_PREHASH_ItemID, mItemUUID); -				msg->sendReliable(object->getRegion()->getHost()); -				mAskedForRunningInfo = TRUE; -				mAssetStatus = PREVIEW_ASSET_LOADING; -			} -			else -			{ +            if(item) +            { +                ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1)); + +                bool isGodlike = gAgent.isGodlike(); +                bool copyManipulate = gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE); +                mIsModifiable = gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); +                +                if(!isGodlike && (!copyManipulate || !mIsModifiable)) +                { +                    mItem = new LLViewerInventoryItem(item); +                    mScriptEd->setScriptText(getString("not_allowed"), FALSE); +                    mScriptEd->mEditor->makePristine(); +                    mScriptEd->enableSave(FALSE); +                    mAssetStatus = PREVIEW_ASSET_LOADED; +                } +                else if(copyManipulate || isGodlike) +                { +                    mItem = new LLViewerInventoryItem(item); +                    // request the text from the object +                    LLUUID* user_data = new LLUUID(mItemUUID); //  ^ mObjectUUID +                    gAssetStorage->getInvItemAsset(object->getRegion()->getHost(), +                        gAgent.getID(), +                        gAgent.getSessionID(), +                        item->getPermissions().getOwner(), +                        object->getID(), +                        item->getUUID(), +                        item->getAssetUUID(), +                        item->getType(), +                        &LLLiveLSLEditor::onLoadComplete, +                        (void*)user_data, +                        TRUE); +                    LLMessageSystem* msg = gMessageSystem; +                    msg->newMessageFast(_PREHASH_GetScriptRunning); +                    msg->nextBlockFast(_PREHASH_Script); +                    msg->addUUIDFast(_PREHASH_ObjectID, mObjectUUID); +                    msg->addUUIDFast(_PREHASH_ItemID, mItemUUID); +                    msg->sendReliable(object->getRegion()->getHost()); +                    mAskedForRunningInfo = TRUE; +                    mAssetStatus = PREVIEW_ASSET_LOADING; +                } +            } + +            if(mItem.isNull()) +            {  				mScriptEd->setScriptText(LLStringUtil::null, FALSE);  				mScriptEd->mEditor->makePristine();  				mAssetStatus = PREVIEW_ASSET_LOADED; -			} +                mIsModifiable = FALSE; +            } -			mIsModifiable = item && gAgent.allowOperation(PERM_MODIFY,  -										item->getPermissions(), -				   						GP_OBJECT_MANIPULATE);  			// This is commented out, because we don't completely  			// handle script exports yet. @@ -1881,7 +2086,7 @@ void LLLiveLSLEditor::loadAsset()  			LLHost host(object->getRegion()->getIP(),  						object->getRegion()->getPort());  			gMessageSystem->sendReliable(host); -			*/ +			*/             		}  	}  	else @@ -1904,6 +2109,8 @@ void LLLiveLSLEditor::loadAsset()  										  time_corrected());  		mAssetStatus = PREVIEW_ASSET_LOADED;  	} + +    requestExperiences();  }  // static @@ -1914,7 +2121,7 @@ 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 ) @@ -2167,8 +2374,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)  	{ @@ -2176,11 +2383,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; @@ -2188,6 +2391,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));  } @@ -2439,3 +2643,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 9fb0a4fb63..0264601d83 100755 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -34,7 +34,7 @@  #include "llcombobox.h"  #include "lliconctrl.h"  #include "llframetimer.h" -#include "llfloatergotoline.h" +//#include "llfloatergotoline.h"  class LLLiveLSLFile;  class LLMessageSystem; @@ -51,6 +51,7 @@ class LLVFS;  class LLViewerInventoryItem;  class LLScriptEdContainer;  class LLFloaterGotoLine; +class LLFloaterExperienceProfile;  // Inner, implementation class.  LLPreviewScript and LLLiveLSLEditor each own one of these.  class LLScriptEdCore : public LLPanel @@ -103,12 +104,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(); @@ -136,8 +139,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; @@ -149,6 +152,7 @@ private:  	BOOL			mEnableSave;  	BOOL			mHasScriptData;  	LLLiveLSLFile*	mLiveFile; +    LLUUID          mAssociatedExperience;  	LLScriptEdContainer* mContainer; // parent view  }; @@ -230,7 +234,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); +    static void onViewProfile(LLUICtrl *ui, void* userdata); +     +    void setExperienceIds(const LLSD& experience_ids); +    void buildExperienceList(); +    void updateExperiencePanel(); +    void requestExperiences(); +    void experienceChanged(); +    void addAssociatedExperience(const LLSD& experience);  private:  	virtual BOOL canClose(); @@ -240,11 +255,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, @@ -285,9 +296,16 @@ private:  	S32					mPendingUploads;  	BOOL getIsModifiable() const { return mIsModifiable; } // Evaluated on load assert -	 +  	LLCheckBoxCtrl*	mMonoCheckbox;  	BOOL mIsModifiable; + + +    LLComboBox     	*mExperiences; +    LLCheckBoxCtrl  *mExperienceEnabled; +    LLSD            mExperienceIds; + +    LLHandle<LLFloater> mExperienceProfile;  };  #endif  // LL_LLPREVIEWSCRIPT_H diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index e52b2f2559..e2a17c6c88 100755 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -43,6 +43,9 @@  #include "llviewercontrol.h"  #include "llviewerinventory.h"  #include "llviewerobjectlist.h" +#include "llexperienceassociationresponder.h" +#include "llexperiencecache.h" +#include "lltrans.h"  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -316,6 +319,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 +678,29 @@ 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)) +        { +            id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); +        } +        if(id.notNull()) +        { +            info->getChild<LLTextBox>("LabelItemExperience")->setText(LLSLURL("experience", id, "profile").getSLURLString());     +        } +        else +        { +            info->getChild<LLTextBox>("LabelItemExperience")->setText(LLTrans::getString("ExperienceNameNull")); +        } +    } +} + +  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 d5f8a1e46e..b3633740ca 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" @@ -197,6 +198,7 @@  #include "llevents.h"  #include "llstartuplistener.h"  #include "lltoolbarview.h" +#include "llexperiencelog.h"  #if LL_WINDOWS  #include "lldxhardware.h" @@ -1302,6 +1304,9 @@ bool idle_startup()  		gAgent.setPositionAgent(agent_start_position_region);  		display_startup(); +		LLStartUp::initExperiences(); + +		display_startup();  		LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT );  		LLConversationLog::getInstance(); @@ -2824,6 +2829,14 @@ void LLStartUp::initNameCache()  	LLAvatarNameCache::setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames"));  } + +void LLStartUp::initExperiences() +{ +	LLAppViewer::instance()->loadExperienceCache(); +	LLExperienceCache::initClass(); +	LLExperienceLog::instance().initialize(); +} +  void LLStartUp::cleanupNameCache()  {  	LLAvatarNameCache::cleanupClass(); @@ -3520,3 +3533,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..94ecbcd333 100755 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -91,6 +91,7 @@ public:  	static void fontInit();  	static void initNameCache(); +	static void initExperiences();  	static void cleanupNameCache(); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index a8eeddb798..d7f7bcb07e 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -58,6 +58,9 @@  #include "llfloatereditsky.h"  #include "llfloatereditwater.h"  #include "llfloaterenvironmentsettings.h" +#include "llfloaterexperienceprofile.h" +#include "llfloaterexperiences.h" +#include "llfloaterexperiencepicker.h"  #include "llfloaterevent.h"  #include "llfloaterdestinations.h"  #include "llfloaterfonttest.h" @@ -208,8 +211,11 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("env_edit_water", "floater_edit_water_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditWater>);  	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("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>); +    LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiences>); +	LLFloaterReg::add("experience_profile", "floater_experienceprofile.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperienceProfile>); +	LLFloaterReg::add("experience_search", "floater_experience_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiencePicker>); +  	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/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index df5c7d5c2e..c7d3fe948d 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -117,6 +117,7 @@  #include <boost/regex.hpp>  #include "llnotificationmanager.h" // +#include "llexperiencecache.h"  #if LL_MSVC  // disable boost::lexical_cast warning @@ -6367,6 +6368,12 @@ bool script_question_cb(const LLSD& notification, const LLSD& response)  		return false;  	} +	LLUUID experience; +	if(notification["payload"].has("experience")) +	{ +		experience = notification["payload"]["experience"].asUUID(); +	} +  	// check whether permissions were granted or denied  	BOOL allowed = TRUE;  	// the "yes/accept" button is the first button in the template, making it button 0 @@ -6375,7 +6382,17 @@ bool script_question_cb(const LLSD& notification, const LLSD& response)  	{  		new_questions = 0;  		allowed = FALSE; -	}	 +	} +	else if(experience.notNull()) +	{ +		LLSD permission; +		LLSD data; +		permission["permission"]="Allow"; + +		data[experience.asString()]=permission; +		data["experience"]=experience; +		LLEventPumps::instance().obtain("experience_permission").post(data); +	}  	LLUUID task_id = notification["payload"]["task_id"].asUUID();  	LLUUID item_id = notification["payload"]["item_id"].asUUID(); @@ -6402,7 +6419,27 @@ bool script_question_cb(const LLSD& notification, const LLSD& response)  	{  		script_question_mute(task_id,notification["payload"]["object_name"].asString());  	} +	if ( response["BlockExperience"] ) +	{ +		if(experience.notNull()) +		{ +			LLViewerRegion* region = gAgent.getRegion(); +			if (!region) +			    return false; +			 +			std::string lookup_url=region->getCapability("ExperiencePreferences");  +			if(lookup_url.empty()) +			    return false; +			LLSD permission; +			LLSD data; +			permission["permission"]="Block"; +			data[experience.asString()]=permission; +			LLHTTPClient::put(lookup_url, data, NULL); +			data["experience"]=experience; +			LLEventPumps::instance().obtain("experience_permission").post(data); +		} +	}  	return false;  } @@ -6435,8 +6472,24 @@ void script_question_mute(const LLUUID& task_id, const std::string& object_name)  static LLNotificationFunctorRegistration script_question_cb_reg_1("ScriptQuestion", script_question_cb);  static LLNotificationFunctorRegistration script_question_cb_reg_2("ScriptQuestionCaution", script_question_cb); +static LLNotificationFunctorRegistration script_question_cb_reg_3("ScriptQuestionExperience", script_question_cb);  static LLNotificationFunctorRegistration unknown_script_question_cb_reg("UnknownScriptQuestion", unknown_script_question_cb); +void process_script_experience_details(const LLSD& experience_details, LLSD args, LLSD payload) +{ +	if(experience_details[LLExperienceCache::PROPERTIES].asInteger() & LLExperienceCache::PROPERTY_GRID) +	{ +		args["GRID_WIDE"] = LLTrans::getString("GRID_WIDE")+ " "; +	} +	else +	{ +		args["GRID_WIDE"] = ""; +	} +	args["EXPERIENCE"] = LLSLURL("experience", experience_details[LLExperienceCache::EXPERIENCE_ID].asUUID(), "profile").getSLURLString(); + +	LLNotificationsUtil::add("ScriptQuestionExperience", args, payload); +} +  void process_script_question(LLMessageSystem *msg, void **user_data)  {  	// *TODO: Translate owner name -> [FIRST] [LAST] @@ -6448,6 +6501,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data)  	S32		questions;  	std::string object_name;  	std::string owner_name; +	LLUUID experienceid;  	// taskid -> object key of object requesting permissions  	msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid ); @@ -6457,6 +6511,11 @@ void process_script_question(LLMessageSystem *msg, void **user_data)  	msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, owner_name);  	msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions ); +	if(msg->has(_PREHASH_Experience)) +	{ +		msg->getUUIDFast(_PREHASH_Experience, _PREHASH_ExperienceID, experienceid); +	} +  	// Special case. If the objects are owned by this agent, throttle per-object instead  	// of per-owner. It's common for residents to reset a ton of scripts that re-request  	// permissions, as with tier boxes. UUIDs can't be valid agent names and vice-versa, @@ -6542,21 +6601,21 @@ void process_script_question(LLMessageSystem *msg, void **user_data)  			payload["object_name"] = object_name;  			payload["owner_name"] = owner_name; -			// check whether cautions are even enabled or not -			if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) +			const char* notification = "ScriptQuestion"; + +			if(caution && gSavedSettings.getBOOL("PermissionsCautionEnabled"))  			{ -				if (caution) -				{ -					args["FOOTERTEXT"] = (count > 1) ? LLTrans::getString("AdditionalPermissionsRequestHeader") + "\n\n" + script_question : ""; -				} -				// display the caution permissions prompt -				LLNotificationsUtil::add(caution ? "ScriptQuestionCaution" : "ScriptQuestion", args, payload); +				args["FOOTERTEXT"] = (count > 1) ? LLTrans::getString("AdditionalPermissionsRequestHeader") + "\n\n" + script_question : ""; +				notification = "ScriptQuestionCaution";  			} -			else +			else if(experienceid.notNull())  			{ -				// fall back to default behavior if cautions are entirely disabled -				LLNotificationsUtil::add("ScriptQuestion", args, payload); +				payload["experience"]=experienceid; +				LLExperienceCache::get(experienceid, boost::bind(process_script_experience_details, _1, args, payload)); +				return;  			} + +			LLNotificationsUtil::add(notification, args, payload);  		}  	}  } diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index a271690349..6fcf265680 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1609,8 +1609,20 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)  	}  	capabilityNames.append("GetDisplayNames"); +	capabilityNames.append("GetExperiences"); +	capabilityNames.append("AgentExperiences"); +	capabilityNames.append("FindExperienceByName"); +	capabilityNames.append("GetExperienceInfo"); +	capabilityNames.append("GetAdminExperiences"); +	capabilityNames.append("GetCreatorExperiences"); +	capabilityNames.append("ExperiencePreferences"); +	capabilityNames.append("UpdateExperience"); +	capabilityNames.append("IsExperienceAdmin"); +	capabilityNames.append("IsExperienceContributor"); +	capabilityNames.append("RegionExperiences");  	capabilityNames.append("GetMesh");  	capabilityNames.append("GetMesh2"); +	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 c47b6d2335..af615d03f1 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 @@ -2140,7 +2141,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)  		idleUpdateBelowWater();	// wind effect uses this  		idleUpdateWindEffect();  	} -	 +		  	idleUpdateNameTag( root_pos_last );  	idleUpdateRenderCost();  } diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 94c187e21a..bce3cc0e63 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -220,6 +220,7 @@ with the same filename but different name    <texture name="ForwardArrow_Off" file_name="icons/ForwardArrow_Off.png" preload="false" />    <texture name="ForwardArrow_Press" file_name="icons/ForwardArrow_Press.png" preload="false" /> +  <texture name="Generic_Experience" file_name="Blank.png" preload="false" />    <texture name="Generic_Group" file_name="icons/Generic_Group.png" preload="false" />    <texture name="Generic_Group_Large" file_name="icons/Generic_Group_Large.png" preload="false" />    <texture name="icon_group.tga" file_name="icons/Generic_Group.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_experience_search.xml b/indra/newview/skins/default/xui/en/floater_experience_search.xml new file mode 100644 index 0000000000..8b44dada40 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experience_search.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater +  positioning="cascading" +  legacy_header_height="18" +  can_resize="true" +  height="350" +  layout="topleft" +  min_height="200" +  min_width="400" +  name="experiencepicker" +  help_topic="experiencepicker" +  title="CHOOSE EXPERIENCE" +  width="350"> + +  <floater.string +    name="not_found"> +    '[TEXT]' not found +  </floater.string> +  <floater.string +    name="no_results"> +    No results +  </floater.string> +  <floater.string +    name="searching"> +    Searching... +  </floater.string> +  <floater.string +    name="loading"> +    Loading... +  </floater.string> +  <floater.string +    name="maturity_icon_general"> +    "Parcel_PG_Light" +  </floater.string> +  <floater.string +    name="maturity_icon_moderate"> +    "Parcel_M_Light" +  </floater.string> +  <floater.string +    name="maturity_icon_adult"> +    "Parcel_R_Light" +  </floater.string> +  <panel +    follows="all" +    left="5" +    right="-3" +    bottom="-2" +    top="18" +    name="search_panel"> +    <text +      left="0" +      follows="top|left|right" +      right="-1"> +      Enter part of the name: +    </text> +    <line_editor +      left="0" +      follows="left|top|right" +      name="edit" +      height="18" +      right="-60"/> +    <button label="Go" +            follows="top|right" +            top_pad="-18" +            left_pad="2" +            right="-1" +            height="18" +            name="find"/> +    <text +      top_pad="6" +      left="0" +      follows="top|left" +      width="180"> +      Max Content Rating: +    </text> +    <icons_combo_box +      follows="left|top" +      height="20" +      label="Moderate" +      layout="topleft" +      name="maturity" +      top_pad="-13" +      left_pad="0" +      right="-1"> +      <icons_combo_box.drop_down_button +        image_overlay="Parcel_M_Light" +        image_overlay_alignment="left" +        imgoverlay_label_space="3" +        pad_left="3"/> +      <icons_combo_box.item +        label="Adult" +        name="Adult" +        value="42"> +        <item.columns +          halign="center" +          type="icon" +          value="Parcel_R_Light" +          width="20"/> +      </icons_combo_box.item> +      <icons_combo_box.item +        label="Moderate" +        name="Mature" +        value="21"> +        <item.columns +          halign="center" +          type="icon" +          value="Parcel_M_Light" +          width="20"/> +      </icons_combo_box.item> +      <icons_combo_box.item +        label="General" +        name="PG" +        value="13"> +        <item.columns +          halign="center" +          type="icon" +          value="Parcel_PG_Light" +          width="20"/> +      </icons_combo_box.item> +    </icons_combo_box> +    <scroll_list +      draw_heading="true" +      left="0" +      right="-1" +      height="239" +      top_pad="4" +      follows="all"  +      column_padding="5" +      can_resize="true" +      name="search_results"> +      <columns +        halign="center" +        width="24" +        name="maturity" +        label=""/> +      <columns +        user_resize="true" +        name="experience_name" +        label="Name"/> +      <columns +        user_resize="true" +        name="owner" +        label="Owner"/> +    </scroll_list> + +    <button +      follows="left|bottom" +      height="23" +      label="OK" +      label_selected="OK" +      name="ok_btn" +      top_pad="3" +      left="0" +      width="100" /> +    <button +      follows="left|bottom" +      height="23" +      label="Cancel" +      name="cancel_btn" +      width="100" +      left_pad="3" /> +    <button +      follows="left|bottom" +      height="23" +      label="View Profile" +      name="profile_btn" +      width="100" +      left_pad="3" /> +  </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_experienceprofile.xml b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml new file mode 100644 index 0000000000..c6a749b45f --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml @@ -0,0 +1,686 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> + +<floater +  positioning="cascading" +  can_close="true" +  enabled="true" +  can_resize="true" +  title="EXPERIENCE PROFILE" +  save_rect="true" +  min_width="325" +  min_height="325" +  width="358" +  height="650"> +  <floater.string +    name="empty_slurl"> +    (none) +  </floater.string> +  <floater.string +    name="maturity_icon_general"> +    "Parcel_PG_Light" +  </floater.string> +  <floater.string +    name="maturity_icon_moderate"> +    "Parcel_M_Light" +  </floater.string> +  <floater.string +    name="maturity_icon_adult"> +    "Parcel_R_Light" +  </floater.string> +  <text +    follows="top|left|right" +    font="SansSerifHugeBold" +    height="26" +    layout="topleft" +    left_pad="4" +    name="edit_title" +    top="2" +    value="Experience Profile" +    use_ellipses="true" +    left="6" +    right="-3"/> +  <tab_container +    hide_tabs="true" +    follows="all" +    height="615" +    layout="topleft" +    left="5" +    min_height="250" +    top_pad="3" +    width="348" +    name="tab_container"> +    <panel +      background_visible="true" +      follows="all" +      height="540" +      layout="topleft" +      left="0" +      min_height="250" +      top="0" +      width="348" +      name="panel_experience_info"> +      <scroll_container +        color="DkGray2" +        follows="all" +        height="520" +        layout="topleft" +        left="9" +        name="xp_scroll" +        opaque="true" +        top_pad="10" +        width="330"> +        <panel +          bg_alpha_color="DkGray2" +          follows="top|left|right" +          height="550" +          layout="topleft" +          left="0" +          name="scrolling_panel" +          top="0" +          width="315" +          min_width="315"> +          <layout_stack +            follows="all" +            height="550" +            layout="topleft" +            left="0" +            animate="false" +            top="0" +            orientation="vertical" +            width="315"> +            <layout_panel +              follows="all" +              height="29" +              layout="topleft" +              left="0" +              top="0" +              auto_resize="false" +              visible="false" +              width="315" +              name="top panel"> +              <text +                type="string" +                length="1" +                follows="left|top|right" +                font="SansSerif" +                height="19" +                top="10" +                layout="topleft" +                left="10" +                right="-123" +                visible="false" +                name="grid_wide"/> +              <button +                follows="top|right" +                height="23" +                label="Edit" +                layout="topleft" +                name="edit_btn" +                width="100" +                visible="false" +                top_pad="-23" +                right="-14"/> +            </layout_panel> +            <layout_panel +              follows="all" +              height="197" +              layout="topleft" +              left="0" +              top="0" +              auto_resize="false" +              visible="false" +              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" +                top="0" +                use_ellipses="true" +                value="" +                width="288"/> +            </layout_panel> +            <layout_panel +              follows="" +              height="50" +              layout="topleft" +              left="0" +              top="0" +              auto_resize="false" +              width="315" +              visible="false" +              name="description panel"> +              <expandable_text +                follows="left|top|right" +                font="SansSerif" +                height="50" +                layout="topleft" +                left="7" +                name="experience_description" +                top="0" +                value="" +                width="293"/> +            </layout_panel> +            <layout_panel +              follows="" +              height="18" +              layout="topleft" +              left="0" +              top="0" +              auto_resize="false" +              width="315" +              visible="true" +              name="maturity panel"> +              <text +                type="string" +                length="1" +                follows="left|top" +                height="16" +                layout="topleft" +                left="10" +                name="ContentRating" +                width="75"> +                Rating: +              </text> +              <text +                type="string" +                length="1" +                follows="left|top|right" +                height="18" +                layout="topleft" +                left_pad="2" +                valign="center" +                name="ContentRatingText" +                top_delta="-3" +                width="188"> +              </text> +            </layout_panel> +            <layout_panel +              follows="all" +              height="46" +              layout="topleft" +              left="0" +              top="5" +              width="313" +              visible="false" +              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" +                use_ellipses="true" +                name="LocationTextText" +                width="288"> +              </text> +            </layout_panel> +            <layout_panel +              follows="all" +              height="53" +              layout="topleft" +              left="0" +              top="5" +              width="313" +              visible="false" +              auto_resize="false" +              name="marketplace panel"> +              <text +                type="string" +                length="1" +                follows="left|top" +                height="16" +                layout="topleft" +                left="10" +                width="290"> +                Marketplace store: +              </text> +              <text +                type="string" +                length="1" +                follows="left|top|right" +                height="18" +                layout="topleft" +                left="10" +                valign="center" +                use_ellipses="true" +                name="marketplace" +                width="288"> +              </text> +            </layout_panel> +            <layout_panel +              follows="left|top|right" +              height="18" +              left="0" +              top="0" +              auto_resize="false" +              width="315"> +              <text +                type="string" +                length="1" +                follows="left|top" +                height="16" +                layout="topleft" +                left="10" +                name="Owner" +                width="75"> +                Owner: +              </text> +              <text +                type="string" +                length="1" +                follows="left|top|right" +                height="18" +                layout="topleft" +                left_pad="2" +                valign="center" +                name="OwnerText" +                use_ellipses="true" +                top_delta="-2" +                width="188"> +              </text> +            </layout_panel> +            <layout_panel +              follows="all" +              height="18" +              layout="topleft" +              left="0" +              top="5" +              width="313" +              visible="false" +              auto_resize="false" +              name="group_panel"> +              <text +                type="string" +                length="1" +                follows="left|top" +                height="16" +                layout="topleft" +                left="10" +                name="Group" +                width="75"> +                Group: +              </text> +              <text +                type="string" +                length="1" +                follows="left|top|right" +                height="18" +                layout="topleft" +                left_pad="2" +                valign="center" +                name="GroupText" +                use_ellipses="true" +                top_delta="-2" +                width="188"> +              </text> +            </layout_panel> +            <layout_panel +              follows="all" +              height="75" +              layout="topleft" +              left="0" +              top="5" +              width="313" +              auto_resize="false" +              visible="true" +              name="perm panel"> +              <button +                follows="bottom|left" +                height="23" +                label="Allow" +                layout="topleft" +                name="allow_btn" +                width="94" +                top_pad="3" +                left="10" +                enabled="false"/> +              <button +                follows="bottom|left" +                height="23" +                label="Forget" +                layout="topleft" +                name="forget_btn" +                width="94" +                top_pad="-23" +                left_pad="3" +                enabled="false"/> +              <button +                follows="bottom|left" +                height="23" +                label="Block" +                layout="topleft" +                name="block_btn" +                width="94" +                top_pad="-23" +                left_pad="3" +                enabled="false"/> +              <text +                type="string" +                halign="center" +                length="1" +                follows="left|top|right" +                height="16" +                layout="topleft" +                left="10" +                name="privileged" +                visible="false" +                width="288"> +                This experience is enabled for all residents. +              </text> +              <button +                follows="bottom|left" +                height="23" +                label="Report Abuse" +                layout="topleft" +                name="report_btn" +                width="94" +                top_pad="3" +                left="107" +                enabled="true"/> +            </layout_panel> +          </layout_stack> +        </panel> +      </scroll_container> +    </panel> +    <panel +      background_visible="true" +      follows="all" +      layout="topleft" +      height="540" +      left="0" +      top="0" +      width="348" +      name="edit_panel_experience_info"> +      <scroll_container +        color="DkGray2" +        follows="all" +        height="520" +        layout="topleft" +        left="9" +        name="edit_xp_scroll" +        opaque="true" +        top_pad="10" +        width="330"> +        <panel +          bg_alpha_color="DkGray2" +          follows="top|left|right" +          height="590" +          layout="topleft" +          left="0" +          name="edit_scrolling_panel" +          top="0" +          width="310"> +          <texture_picker +            enabled="true" +            fallback_image="default_land_picture.j2c" +            follows="left|top" +            height="197" +            layout="topleft" +            left="10" +            name="edit_logo" +            top="10" +            width="290" /> +          <text +            follows="left|top|right" +            height="14" +            layout="topleft" +            left="10" +            name="edit_experience_title_label" +            use_ellipses="true" +            value="Name:" +            right="-10"/> +          <line_editor +            follows="left|top|right" +            height="19" +            layout="topleft" +            left="10" +            name="edit_experience_title" +            max_length_bytes="63" +            text_color="black" +            right="-10"/> +          <text +            follows="left|top|right" +            height="14" +            layout="topleft" +            left="10" +            top_pad="10" +            name="edit_experience_desc_label" +            use_ellipses="true" +            value="Description:" +            right="-10"/> +          <text_editor +            follows="left|top|right" +            height="57" +            layout="topleft" +            left="11" +            name="edit_experience_description" +            max_length="2048" +            text_color="black" +            right="-11" +            word_wrap="true"/> +          <button +            top_pad="10" +            left="10" +            width="125" +            height="23" +            name="Group_btn" +            label="Group" +            /> +          <text +            type="string" +            length="1" +            follows="left|top|right" +            height="14" +            layout="topleft" +            left_pad="10" +            top_pad="-18" +            use_ellipses="true" +            name="edit_GroupText" +            right="-10" /> +          <text +            top_pad="10" +            type="string" +            length="1" +            follows="left|top|right" +            height="16" +            layout="topleft" +            left="10" +            name="edit_ContentRating" +            right="-10"> +            Rating: +          </text> +          <icons_combo_box +            follows="right|top" +            height="20" +            label="Moderate" +            layout="topleft" +            right="-10" +            top_pad="-19" +            tool_tip="Increasing the maturity rating on an experience will reset permission for all residents which have allowed the experience." +            name="edit_ContentRatingText" +            width="105"> +            <icons_combo_box.drop_down_button +              image_overlay="Parcel_M_Light" +              image_overlay_alignment="left" +              imgoverlay_label_space="3" +              pad_left="3"/> +            <icons_combo_box.item +              label="Adult" +              name="Adult" +              value="42"> +              <item.columns +                halign="center" +                type="icon" +                value="Parcel_R_Light" +                width="20"/> +            </icons_combo_box.item> +            <icons_combo_box.item +              label="Moderate" +              name="Mature" +              value="21"> +              <item.columns +                halign="center" +                type="icon" +                value="Parcel_M_Light" +                width="20"/> +            </icons_combo_box.item> +            <icons_combo_box.item +              label="General" +              name="PG" +              value="13"> +              <item.columns +                halign="center" +                type="icon" +                value="Parcel_PG_Light" +                width="20"/> +            </icons_combo_box.item> +          </icons_combo_box> +          <text +            type="string" +            length="1" +            follows="left|top" +            height="14" +            layout="topleft" +            left="10" +            top_pad="10" +            name="edit_Location" +            right="90"> +            Location: +          </text> +          <text +            type="string" +            length="1" +            follows="left|top|right" +            height="14" +            layout="topleft" +            left_pad="5" +            top_pad="-14" +            use_ellipses="true" +            name="edit_LocationTextText" +            right="-10" /> +          <button +            left="10" +            width="125" +            height="23" +            name="location_btn" +            label="Set to Current" +            /> +          <button +            top_pad="-23" +            follows="top|right" +            right="-10" +            width="125" +            name="clear_btn" +            label="Clear Location"/> +          <text +            type="string" +            length="1" +            follows="left|top|right" +            height="14" +            top_pad="10" +            layout="topleft" +            left="10" +            right="-10"> +            Marketplace store: +          </text> +          <line_editor +            type="string" +            length="1" +            follows="left|top|right" +            height="19" +            layout="topleft" +            left="10" +            max_length_bytes="255" +            valign="center" +            name="edit_marketplace" +            right="-10"/> +          <check_box width="140" +                     height="21" +                     left="10" +                     layout="topleft" +                     follows="top|left" +                     tool_tip="" +                     label="Enable Experience" +                     name="edit_enable_btn"/> +          <check_box width="125" +                     height="21" +                     top_pad="-21" +                     right="-10" +                     visible="false" +                     layout="topleft" +                     follows="top|left|right" +                     label="Hide In Search" +                     name="edit_private_btn"/> +          <button +            follows="top|left" +            height="23" +            label="Back" +            layout="topleft" +            name="cancel_btn" +            width="125" +            top_pad="15" +            left="10" +            visible="true"/> +          <button +            follows="top|right" +            height="23" +            label="Save" +            layout="topleft" +            name="save_btn" +            top_pad="-23" +            width="125" +            right="-10" +            visible="true"/> +          <text +            follows="left|top|right" +            height="25" +            layout="topleft" +            left="10" +            top_pad="10" +            name="changes" +            use_ellipses="true" +            word_wrap="true" +            value="Experience changes may take several minutes to be seen on all regions." +            right="-10"/> +        </panel> +      </scroll_container> +    </panel> +  </tab_container> +</floater> 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..e727512b7f --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experiences.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> + +<floater +  can_close="true" +  can_resize="true" +  height="400" +  width="300" +  min_height="300" +  min_width="300" +  layout="topleft" +  name="floater_experiences" +  save_rect="true" +  single_instance="true" +  reuse_instance="false" +  bg_opaque_color="0 0.5 0 0.3" +  title="EXPERIENCES"> +  <tab_container +    top="3" +    left="3" +    layout="topleft" +    width="294" +    follows="all" +    height="394" +    name="xp_tabs"> +  </tab_container> + +</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..40b47dc1d7 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,114 @@  <?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> +  <floater.string +    name="loading"> +    Loading... +  </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 7e8d2aaf9a..f33acc75b2 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -74,6 +74,20 @@           function="Floater.ToggleOrBringToFront"           parameter="picks" />        </menu_item_call> +      <menu_item_call +        label="Experiences..." +        name="Experiences"> +        <menu_item_call.on_click +          function="Floater.ToggleOrBringToFront" +          parameter="experiences"/> +      </menu_item_call> +      <menu_item_call +        label="Experience Search..." +        name="Experience Search"> +        <menu_item_call.on_click +          function="Floater.ToggleOrBringToFront" +          parameter="experience_search"/> +      </menu_item_call>        <menu_item_separator/>        <menu_item_call         label="Camera Controls..." diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index c97af4e9ef..edcc401b9a 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6995,6 +6995,137 @@ Is this OK?    </notification>    <notification +    icon="alertmodal.tga" +    name="ExperienceAcquireFailed" +    type="alertmodal"> +Unable to acquire a new experience: +    [ERROR_MESSAGE] +    <tag>fail</tag> +    <usetemplate +      name="okbutton" +      yestext="OK"/> +  </notification> + +  <notification +    icon="notify.tga" +    name="NotInGroupExperienceProfileMessage" +    persist="false" +    type="notify"> +    A change to the experience group was ignored because the owner is not a member of the selected group. +  </notification> + +  <notification +    icon="notify.tga" +    name="UneditableExperienceProfileMessage" +    persist="false" +    type="notify"> +    The uneditable field '[field]' was ignored when updating the experience profile. +  </notification> + +  <notification +    icon="notify.tga" +    name="RestrictedToOwnerExperienceProfileMessage" +    persist="false" +    type="notify"> +    Ignored changes to the field '[field]' which can only be set by the experience owner. +  </notification> + +  <notification +    icon="notify.tga" +    name="MaturityRatingExceedsOwnerExperienceProfileMessage" +    persist="false" +    type="notify"> +    You may not set the maturity rating of an experience higher than that of the owner. +  </notification> + +  <notification +    icon="notify.tga" +    name="RestrictedTermExperienceProfileMessage" +    persist="false" +    type="notify"> +    The following terms prevented the update of the experience profile name and/or description: [extra_info] +  </notification> +   +  <notification +    icon="notify.tga" +    name="TeleportedHomeExperienceRemoved" +    persist="false" +    type="notify"> +    You have been teleported from the region [region_name] for removing the experience secondlife:///app/experience/[public_id]/profile and are no longer permitted in the region. +    <form name="form"> +      <ignore name="ignore" +              text="Kicked from region for removing an experience"/> +    </form> +  </notification> + +  <notification +    icon="notify.tga" +    name="TrustedExperienceEntry" +    persist="false" +    type="notify"> +    You have been allowed into the region [region_name] by participating in the trusted experience secondlife:///app/experience/[public_id]/profile removing this experience may kick you from the region. +    <form name="form"> +      <ignore name="ignore" +              text="Allowed into a region by an experience"/> +    </form> +  </notification> + +  <notification +    icon="notify.tga" +    name="ExperienceEvent" +    persist="false" +    type="notifytip"> +    An object was allowed to [EventType] by the secondlife:///app/experience/[public_id]/profile experience. +    Owner: secondlife:///app/agent/[OwnerID]/inspect +    Object Name: [ObjectName] +    Parcel Name: [ParcelName] +  </notification> + +  <notification +    icon="notify.tga" +    name="ExperienceEventAttachment" +    persist="false" +    type="notifytip"> +    An attachment was allowed to [EventType] by the secondlife:///app/experience/[public_id]/profile experience. +    Owner: secondlife:///app/agent/[OwnerID]/inspect +  </notification> +   +  <notification +   icon="notify.tga" +   name="ScriptQuestionExperience" +   persist="false" +   type="notify"> +'<nolink>[OBJECTNAME]</nolink>', an object owned by '[NAME]', requests your participation in the [GRID_WIDE]experience: + +[EXPERIENCE] + +Once permission is granted you will not see this message again for this experience unless it is revoked from the experience profile. + +Scripts associated with this experience will be able to do the following on regions where the experience is active:  + +[QUESTIONS]Is this OK? +  <tag>confirm</tag> +    <form name="form"> +      <button +       index="0" +       name="Yes" +       text="Yes"/> +      <button +       index="1" +       name="No" +       text="No"/> +      <button +        index="2" +        name="Mute" +        text="Block Object"/> +      <button +       index="3" +       name="BlockExperience" +       text="Block Experience"/> +    </form> +  </notification> + +  <notification     icon="notify.tga"     name="ScriptQuestionCaution"     priority="critical" 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_experience_list_editor.xml b/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml new file mode 100644 index 0000000000..acd9471916 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel +  layout="topleft" +  left="0" +  top="0" +  width="300" +  height="100" +  min_height="100" +  follows="all" +  border="true" +  name="experince_list_editor"> +  <panel.string +    name="loading"> +    loading... +  </panel.string> +  <panel.string +    name="no_results"> +    (empty) +  </panel.string> +  <text +    layout="topleft" +    name="text_name" +    left="3" +    right="-1" +    height="12" +    follows="left|top|right"> +    Experience List +  </text> +  <scroll_list +    draw_heading="false" +    left="3" +    width="225" +    height="77" +    follows="all" +    can_resize="false" +    name="experience_list"> +    <columns +      width="225" +      user_resize="false" +      name="experience_name" +      label="Name"/> +  </scroll_list> +  <button +    layout="topleft" +    follows="top|right" +    top_pad="-77"  +    left_pad="3" +    width="65" +    name="btn_add" +    label="Add..."/> +  <button +    layout="topleft" +    follows="top|right" +    width="64" +    name="btn_remove" +    label="Remove"/> +  <button +    layout="topleft" +    follows="top|right" +    width="64" +    name="btn_profile" +    label="Profile..."/> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_experience_list_item.xml b/indra/newview/skins/default/xui/en/panel_experience_list_item.xml new file mode 100644 index 0000000000..a9777c51e7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experience_list_item.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + +<panel +  layout="topleft" +  top="100" +  left="5" +  width="100" +  height="19" +  label="Experiences" +  follows="top|right|left"> +  <text +    left="3" +    top="3" +    height="16" +    width="177" +    follows="all" +    name="experience_name" +    > +    Dummy Name +  </text> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_experience_log.xml b/indra/newview/skins/default/xui/en/panel_experience_log.xml new file mode 100644 index 0000000000..6869a135d8 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experience_log.xml @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + +<panel +  layout="topleft" +  top="3" +  left="3" +  width="500" +  height="300" +  label="EVENTS" +  bg_opaque_color="0 0.5 0 0.3" +  follows="all"> +  <string +    name="no_events" +    value="No events."/> +  <string +    name="loading" +    value="loading..."/> +  <layout_stack +    layout="topleft" +    top="0" +    left="4"  +    right="-1" +    bottom="-4" +    orientation="vertical" +    follows="all"> +    <layout_panel +      layout="topleft" +      top="0" +      left="1" +      right="-1" +      height="250" +      follows="all"> +      <scroll_list +        draw_heading="true" +        left="1" +        right="-86" +        height="250" +        follows="all" +        name="experience_log_list"> +        <columns +          width="100" +          user_resize="true" +          name="time" +          label="Time"/> +        <columns +          width="100" +          user_resize="true" +          name="event" +          label="Event"/> +        <columns +          width="100" +          user_resize="true" +          name="experience_name" +          label="Experience"/> +        <columns +          width="100" +          user_resize="true" +          name="object_name" +          label="Object"/> +      </scroll_list> +       +      <button +        layout="topleft" +        follows="top|right" +        name="btn_notify" +        label="Notify" +        top_pad="-225" +        left_pad="5" +        right="-1" +        enabled="false"/> +       +      <button +        layout="topleft" +        follows="top|right" +        name="btn_profile_xp" +        label="Profile" +        top_pad="5" +        right="-1" +        enabled="false"/> + +      <button +        layout="topleft" +        follows="top|right" +        name="btn_report_xp" +        label="Report" +        top_pad="5" +        right="-1" +        enabled="false"/> +    </layout_panel> +    <layout_panel +      layout="topleft" +      top="0" +      left="1" +      right="-1" +      height="30" +      min_height="30" +      follows="all" +      name="button_panel" +      visible="true"> +      <check_box +        top="0" +        follows="top|left" +        height="26" +        label="Notify All Events   Days" +        width="140" +        name="notify_all" /> + +      <spinner +        top="5" +        control_name="LogDays" +        decimal_digits="0" +        follows="left|top" +        height="23" +        increment="1" +        initial_value="7" +        label_width="30" +        layout="topleft" +        left_pad="5" +        max_val="14" +        min_val="0" +        name="logsizespinner" +        width="40" /> +      <button +        top="5" +        left="280" +        layout="topleft" +        follows="top|left" +        name="btn_clear" +        label="Clear" +        width="80" +        enabled="false"/> +      <button +        top="5" +        layout="topleft" +        left_pad="5" +        width="20" +        follows="top|left" +        name="btn_prev" +        label="<" +        enabled="false"/> +      <button +        top="5" +        layout="topleft" +        follows="top|left" +        name="btn_next" +        label=">" +        left_pad="5" +        width="20" +        enabled="false"/> +    </layout_panel> +  </layout_stack> +</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..82b69fc344 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experiences.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + +<panel +  layout="topleft" +  top="3" +  left="3" +  width="200" +  height="300" +  label="Experiences" +  bg_opaque_color="0 0.5 0 0.3" +  follows="all"> +  <string +    name="no_experiences" +    value="No experiences."/> +  <string +    name="acquire" +    value="Acquire an Experience" /> +  <layout_stack +    layout="topleft" +    top="0" +    left="4"  +    right="-1" +    bottom="-4" +    orientation="vertical" +    follows="all"> +    <layout_panel +      layout="topleft" +      top="0" +      left="1" +      right="-1" +      height="250" +      follows="all"> +      <flat_list_view +        name="experiences_list" +        layout="topleft" +        top="0" +        left="1" +        right="-1" +        bottom="-1" +        follows="all"> +      </flat_list_view> +    </layout_panel> +    <layout_panel +      layout="topleft" +      top="0" +      left="1" +      right="-1" +      height="24" +      min_height="24" +      follows="all" +      name="button_panel" +      visible="false"> +      <button +        layout="topleft" +        left="2" +        top="0" +        follows="top|left" +        name="btn_action" +        enabled="true"/> +    </layout_panel> +  </layout_stack> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_region_experiences.xml b/indra/newview/skins/default/xui/en/panel_region_experiences.xml new file mode 100644 index 0000000000..b7602f8fd3 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_region_experiences.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel +  border="true" +  follows="all" +  height="320" +  help_topic="panel_region_experience_tab" +  label="Experiences" +  name="Experiences" +  layout="topleft" +  left="0" +  top="320" +  width="480"> +  <panel.string +    name="panel_allowed"> +    Allowed Experiences +  </panel.string> +  <panel.string +    name="panel_blocked"> +    Blocked Experiences +  </panel.string> +  <panel.string +    name="panel_trusted"> +    Trusted Experiences +  </panel.string> + +  <layout_stack +    left="5" +    layout="topleft" +    follows="all" +    top="0" +    right="-5" +    height="320" +    min_height="140" +    orientation="vertical"> +    <layout_panel +      height="100" +      min_height="100" +      width="530"> +      <panel follows="all" +             width="530" +             name="panel_trusted" +             class="panel_experience_list_editor" +             filename="panel_experience_list_editor.xml" /> +    </layout_panel> +    <layout_panel +      height="100" +      min_height="100" +      width="530"> +      <panel +        label="SOME LABEL" +        width="530" +        name="panel_allowed" +        class="panel_experience_list_editor" +        filename="panel_experience_list_editor.xml"/> +    </layout_panel> +    <layout_panel +      height="100" +      min_height="100" +      width="530"> +      <panel +        width="530" +        name="panel_blocked" +        class="panel_experience_list_editor" +        filename="panel_experience_list_editor.xml"/> +    </layout_panel> +    <layout_panel +      height="30" +      min_height="30" +      max_height="30" +      width="530"> +      <button +        layout="topleft" +        width="85" +        left="3" +        label="Apply" +        name="apply_btn"/> +    </layout_panel> +  </layout_stack> +</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 bcdef96138..469f6f036d 100755 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -1,211 +1,212 @@  <?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..." /> +  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" +    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_item_call               label="Go to line..."               layout="topleft"               name="Go to line..." /> -        </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 +    </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="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 +    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..fc3fdbcfa5 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,472 @@  <?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="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> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="10" +        layout="topleft" +        left="5" +        name="LabelItemExperienceTitle" +        top_pad="0" +        width="78" +        visible="true"> +        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="true" +        /> +      <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"/> +    </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 67f75fe1d2..a17a895b42 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -461,7 +461,7 @@ Please try logging in again in a minute.</string>  	<string name="NotConnected">Not Connected</string>  	<string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name --> -	<string name="JoinAnExperience">Join an experience</string> <!-- not used --> +	<string name="JoinAnExperience"></string> <!-- intentionally left blank -->  	<string name="SilentlyManageEstateAccess">Suppress alerts when managing estate access lists</string>  	<string name="OverrideYourAnimations">Replace your default animations</string>  	<string name="ScriptReturnObjects">Return objects on your behalf</string> @@ -492,8 +492,6 @@ Please try logging in again in a minute.</string>  	<string name="load_file_verb">Load</string>  	<string name="targa_image_files">Targa Images</string>  	<string name="bitmap_image_files">Bitmap Images</string> -	<string name="png_image_files">PNG Images</string> -	<string name="save_texture_image_files">Targa or PNG Images</string>  	<string name="avi_movie_file">AVI Movie File</string>  	<string name="xaf_animation_file">XAF Anim File</string>  	<string name="xml_file">XML File</string> @@ -3977,6 +3975,33 @@ 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> +  <string name="ExperienceNameNull">(no experience)</string> +  <string name="GRID_WIDE">Grid-wide</string> +  <string name="Allowed_Experiences_Tab">ALLOWED</string> +  <string name="Blocked_Experiences_Tab">BLOCKED</string> +  <string name="Contrib_Experiences_Tab">CONTRIBUTOR</string> +  <string name="Admin_Experiences_Tab">ADMIN</string> +  <string name="Recent_Experiences_Tab">RECENT</string> +  <string name="Owned_Experiences_Tab">OWNED</string> +  <string name="ExperiencePermission1">take over your controls</string> +  <string name="ExperiencePermission3">trigger animations on your avatar</string> +  <string name="ExperiencePermission4">attach to your avatar</string> +  <string name="ExperiencePermission9">track your camera</string> +  <string name="ExperiencePermission10">control your camera</string> +  <string name="ExperiencePermission11">teleport you</string> +  <string name="ExperiencePermission12">automatically accept experience permissions</string> +  <string name="ExperiencePermissionShortUnknown">perform an unknown operation: [Permission]</string> +  <string name="ExperiencePermissionShort1">Take Controls</string> +  <string name="ExperiencePermissionShort3">Trigger Animations</string> +  <string name="ExperiencePermissionShort4">Attach</string> +  <string name="ExperiencePermissionShort9">Track Camera</string> +  <string name="ExperiencePermissionShort10">Control Camera</string> +  <string name="ExperiencePermissionShort11">Teleport</string> +  <string name="ExperiencePermissionShort12">Permission</string> +  <string name="ExperiencePermissionShortUnknown">Unknown: [Permission]</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>
 | 
