diff options
Diffstat (limited to 'indra')
40 files changed, 4713 insertions, 734 deletions
diff --git a/indra/llcommon/roles_constants.h b/indra/llcommon/roles_constants.h index effd15ea72..65ec290200 100755 --- a/indra/llcommon/roles_constants.h +++ b/indra/llcommon/roles_constants.h @@ -53,7 +53,7 @@ enum LLRoleChangeType // KNOWN HOLES: use these for any single bit powers you need // bit 0x1 << 46 -// bit 0x1 << 49 and above +// bit 0x1 << 51 and above // These powers were removed to make group roles simpler // bit 0x1 << 41 (GP_ACCOUNTING_VIEW) @@ -146,6 +146,9 @@ const U64 GP_SESSION_JOIN = 0x1LL << 16; //can join session const U64 GP_SESSION_VOICE = 0x1LL << 27; //can hear/talk const U64 GP_SESSION_MODERATOR = 0x1LL << 37; //can mute people's session +const U64 GP_EXPERIENCE_ADMIN = 0x1LL << 49; // has admin rights to any experiences owned by this group +const U64 GP_EXPERIENCE_CREATOR = 0x1LL << 50; // can sign scripts for experiences owned by this group + const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE | GP_LAND_ALLOW_SET_HOME | GP_NOTICES_RECEIVE diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index d193e367eb..5740584edb 100755 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -38,6 +38,7 @@ set(llmessage_SOURCE_FILES llcurl.cpp lldatapacker.cpp lldispatcher.cpp + llexperiencecache.cpp llfiltersd2xmlrpc.cpp llhost.cpp llhttpassetstorage.cpp @@ -128,6 +129,7 @@ set(llmessage_HEADER_FILES lldbstrings.h lldispatcher.h lleventflags.h + llexperiencecache.h llfiltersd2xmlrpc.h llfollowcamparams.h llhost.h diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp new file mode 100644 index 0000000000..b131156905 --- /dev/null +++ b/indra/llmessage/llexperiencecache.cpp @@ -0,0 +1,671 @@ +/** + * @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()); + } + + if(row.has(OWNER_ID)) + { + sPendingQueue.erase(row[OWNER_ID].asUUID()); + } + + if(!row.has(OWNER_ID)) + { + if(row.has(AGENT_ID) && row[AGENT_ID].asUUID().notNull()) + { + row[OWNER_ID]=row[AGENT_ID]; + } + else + { + row[OWNER_ID]=row[GROUP_ID]; + } + } + + //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_INFOS("ExperienceCache") << "loaded " << sCache.size() << LL_ENDL; + } + + void exportFile(std::ostream& ostr) + { + LLSD experiences; + + cache_t::const_iterator it =sCache.begin(); + for( ; it != sCache.end() ; ++it) + { + if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() || + it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID)) + continue; + + experiences[it->first.asString()] = it->second; + } + + LLSD data; + data["experiences"] = experiences; + + LLSDSerialize::toPrettyXML(data, ostr); + } + + class LLExperienceResponder : public LLHTTPClient::Responder + { + public: + LLExperienceResponder(const ask_queue_t& keys) + :mKeys(keys) + { + + } + + virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + mHeaders = content; + } + + virtual void result(const LLSD& content) + { + LLSD experiences = content["experience_keys"]; + LLSD::array_const_iterator it = experiences.beginArray(); + for( /**/ ; it != experiences.endArray(); ++it) + { + const LLSD& row = *it; + LLUUID public_key = row[EXPERIENCE_ID].asUUID(); + + + LL_INFOS("ExperienceCache") << "Received result for " << public_key + << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ; + + processExperience(public_key, row); + } + + LLSD error_ids = content["error_ids"]; + LLSD::array_const_iterator errIt = error_ids.beginArray(); + for( /**/ ; errIt != error_ids.endArray() ; ++errIt ) + { + LLUUID id = errIt->asUUID(); + LLSD exp; + exp[EXPIRES]=DEFAULT_EXPIRATION; + exp[EXPERIENCE_ID] = id; + exp[PROPERTIES]=PROPERTY_INVALID; + exp[MISSING]=true; + exp[QUOTA] = DEFAULT_QUOTA; + + processExperience(id, exp); + LL_INFOS("ExperienceCache") << "Error result for " << id << LL_ENDL ; + } + + LL_INFOS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL; + } + + virtual void error(U32 status, const std::string& reason) + { + LL_WARNS("ExperienceCache") << "Request failed "<<status<<" "<<reason<< LL_ENDL; + // We're going to construct a dummy record and cache it for a while, + // either briefly for a 503 Service Unavailable, or longer for other + // errors. + F64 retry_timestamp = errorRetryTimestamp(status); + + + // Add dummy records for all agent IDs in this request + ask_queue_t::const_iterator it = mKeys.begin(); + for ( ; it != mKeys.end(); ++it) + { + + LLSD exp; + //leave the properties alone if we already have a cache entry for this xp + if(!get(it->first, exp)) + { + 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); + LL_INFOS("ExperienceCache") << "Error result for " << it->first << LL_ENDL ; + } + + } + + // Return time to retry a request that generated an error, based on + // error type and headers. Return value is seconds-since-epoch. + F64 errorRetryTimestamp(S32 status) + { + + // Retry-After takes priority + LLSD retry_after = mHeaders["retry-after"]; + if (retry_after.isDefined()) + { + // We only support the delta-seconds type + S32 delta_seconds = retry_after.asInteger(); + if (delta_seconds > 0) + { + // ...valid delta-seconds + return F64(delta_seconds); + } + } + + // If no Retry-After, look for Cache-Control max-age + F64 expires = 0.0; + if (LLExperienceCache::expirationFromCacheControl(mHeaders, &expires)) + { + return expires; + } + + // No information in header, make a guess + if (status == 503) + { + // ...service unavailable, retry soon + const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min + return SERVICE_UNAVAILABLE_DELAY; + } + else if (status == 499) + { + // ...we were probably too busy, retry quickly + const F64 BUSY_DELAY = 10.0; // 10 seconds + return BUSY_DELAY; + + } + else + { + // ...other unexpected error + const F64 DEFAULT_DELAY = 3600.0; // 1 hour + return DEFAULT_DELAY; + } + } + + private: + ask_queue_t mKeys; + LLSD mHeaders; + }; + + void requestExperiences() + { + if(sAskQueue.empty() || sLookupURL.empty()) + return; + + F64 now = LLFrameTimer::getTotalSeconds(); + + const U32 EXP_URL_SEND_THRESHOLD = 3000; + + + std::ostringstream ostr; + + ask_queue_t keys; + + ostr << sLookupURL; + + char arg='?'; + + int request_count = 0; + for(ask_queue_t::const_iterator it = sAskQueue.begin() ; it != sAskQueue.end() && request_count < sMaximumLookups; ++it) + { + const LLUUID& key = it->first; + const std::string& key_type = it->second; + + ostr << arg << key_type << '=' << key.asString() ; + + keys[key]=key_type; + request_count++; + + sPendingQueue[key] = now; + + arg='&'; + + if(ostr.tellp() > EXP_URL_SEND_THRESHOLD) + { + LL_INFOS("ExperienceCache") << " query: " << ostr.str() << LL_ENDL; + LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); + ostr.clear(); + ostr.str(sLookupURL); + arg='?'; + keys.clear(); + } + } + + if(ostr.tellp() > sLookupURL.size()) + { + LL_INFOS("ExperienceCache") << " query: " << ostr.str() << LL_ENDL; + LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); + } + + sAskQueue.clear(); + } + + bool isRequestPending(const LLUUID& public_key) + { + bool isPending = false; + const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; + + pending_queue_t::const_iterator it = sPendingQueue.find(public_key); + + if(it != sPendingQueue.end()) + { + F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; + isPending = (it->second > expire_time); + } + + return isPending; + } + + + void setLookupURL( const std::string& lookup_url ) + { + sLookupURL = lookup_url; + if(!sLookupURL.empty()) + { + sLookupURL += "id/"; + } + } + + bool hasLookupURL() + { + return !sLookupURL.empty(); + } + + void idle() + { + + const F32 SECS_BETWEEN_REQUESTS = 0.1f; + if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) + { + return; + } + + // Must be large relative to above + const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds + if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) + { + eraseExpired(); + } + + + if(!sAskQueue.empty()) + { + requestExperiences(); + } + } + + void erase( const LLUUID& key ) + { + cache_t::iterator it = sCache.find(key); + + if(it != sCache.end()) + { + sCache.erase(it); + } + } + + void eraseExpired() + { + F64 now = LLFrameTimer::getTotalSeconds(); + cache_t::iterator it = sCache.begin(); + while (it != sCache.end()) + { + cache_t::iterator cur = it; + LLSD& exp = cur->second; + ++it; + + if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now) + { + if(!exp.has(EXPERIENCE_ID)) + { + LL_INFOS("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_INFOS("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_INFOS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ; + sAskQueue[key]=EXPERIENCE_ID; + + return true; + } + return false; + } + + void insert(const LLSD& experience_data ) + { + if(experience_data.has(EXPERIENCE_ID)) + { + processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data); + } + else + { + LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; + } + } + + bool get( const LLUUID& key, LLSD& experience_data ) + { + if(key.isNull()) return false; + cache_t::const_iterator it = sCache.find(key); + + if (it != sCache.end()) + { + experience_data = it->second; + return true; + } + + fetch(key); + + return false; + } + + + void get( const LLUUID& key, callback_slot_t slot ) + { + if(key.isNull()) return; + + cache_t::const_iterator it = sCache.find(key); + if (it != sCache.end()) + { + // ...name already exists in cache, fire callback now + callback_signal_t signal; + signal.connect(slot); + + signal(it->second); + return; + } + + fetch(key); + + // always store additional callback, even if request is pending + signal_map_t::iterator sig_it = sSignalMap.find(key); + if (sig_it == sSignalMap.end()) + { + // ...new callback for this id + callback_signal_t* signal = new callback_signal_t(); + signal->connect(slot); + sSignalMap[key] = signal; + } + else + { + // ...existing callback, bind additional slot + callback_signal_t* signal = sig_it->second; + signal->connect(slot); + } + } + +} + + +void LLExperienceCache::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..8b3443e5a9 --- /dev/null +++ b/indra/llmessage/llexperiencecache.h @@ -0,0 +1,105 @@ +/** + * @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 OWNER_ID = "owner_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); + bool get(const LLUUID& key, LLSD& experience_data); + + // If name information is in cache, callback will be called immediately. + void get(const LLUUID& key, callback_slot_t slot); + + const cache_t& getCached(); + + // 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/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l index 1c9f50202d..6fbdf13df7 100755 --- a/indra/lscript/lscript_compile/indra.l +++ b/indra/lscript/lscript_compile/indra.l @@ -694,6 +694,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 3a09bc530c..65067ce054 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -188,6 +188,7 @@ set(viewer_SOURCE_FILES lleventnotifier.cpp lleventpoll.cpp llexpandabletextbox.cpp + llexperienceassociationresponder.cpp llexternaleditor.cpp llface.cpp llfasttimerview.cpp @@ -226,6 +227,8 @@ set(viewer_SOURCE_FILES llfloatereditwater.cpp llfloaterenvironmentsettings.cpp llfloaterevent.cpp + llfloaterexperienceprofile.cpp + llfloaterexperiences.cpp llfloaterfonttest.cpp llfloatergesture.cpp llfloatergodtools.cpp @@ -396,6 +399,7 @@ set(viewer_SOURCE_FILES llpanelclassified.cpp llpanelcontents.cpp llpaneleditwearable.cpp + llpanelexperiences.cpp llpanelface.cpp llpanelgenerictip.cpp llpanelgroup.cpp @@ -772,6 +776,7 @@ set(viewer_HEADER_FILES lleventnotifier.h lleventpoll.h llexpandabletextbox.h + llexperienceassociationresponder.h llexternaleditor.h llface.h llfasttimerview.h @@ -810,6 +815,8 @@ set(viewer_HEADER_FILES llfloatereditwater.h llfloaterenvironmentsettings.h llfloaterevent.h + llfloaterexperienceprofile.h + llfloaterexperiences.h llfloaterfonttest.h llfloatergesture.h llfloatergodtools.h @@ -973,6 +980,7 @@ set(viewer_HEADER_FILES llpanelclassified.h llpanelcontents.h llpaneleditwearable.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 a87310955f..5de79b4bf5 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 @@ -668,6 +669,24 @@ 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_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 02eb67bf80..d4cb2be92e 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -100,6 +100,7 @@ // Linden library includes #include "llavatarnamecache.h" #include "lldiriterator.h" +#include "llexperiencecache.h" #include "llimagej2c.h" #include "llmemory.h" #include "llprimitive.h" @@ -4395,7 +4396,7 @@ void LLAppViewer::loadNameCache() } void LLAppViewer::saveNameCache() - { +{ // display names cache std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); @@ -4403,7 +4404,7 @@ void LLAppViewer::saveNameCache() if(name_cache_stream.is_open()) { LLAvatarNameCache::exportFile(name_cache_stream); -} + } if (!gCacheName) return; @@ -4416,6 +4417,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. @@ -4610,7 +4637,7 @@ void LLAppViewer::idle() // floating throughout the various object lists. // idleNameCache(); - + idleExperienceCache(); idleNetwork(); @@ -5030,6 +5057,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 // @@ -5192,6 +5235,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 3ae8a78845..e30c9f7f16 100755 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -119,6 +119,10 @@ public: void loadNameCache(); void saveNameCache(); + void loadExperienceCache(); + void saveExperienceCache(); + + void removeMarkerFile(bool leave_logout_marker = false); // LLAppViewer testing helpers. @@ -224,6 +228,7 @@ private: void idle(); void idleShutdown(); // update avatar SLID and display name caches + void idleExperienceCache(); void idleNameCache(); void idleNetwork(); diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 3cf3c739d9..80a80f4298 100755 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -172,21 +172,20 @@ void ll_nvapi_init(NvDRSSessionHandle hSession) nvapi_error(status); return; } + + // (5) Now we apply (or save) our changes to the system + status = NvAPI_DRS_SaveSettings(hSession); + if (status != NVAPI_OK) + { + nvapi_error(status); + return; + } } else if (status != NVAPI_OK) { nvapi_error(status); return; } - - - - // (5) Now we apply (or save) our changes to the system - status = NvAPI_DRS_SaveSettings(hSession); - if (status != NVAPI_OK) - { - nvapi_error(status); - } } //#define DEBUGGING_SEH_FILTER 1 diff --git a/indra/newview/llexperienceassociationresponder.cpp b/indra/newview/llexperienceassociationresponder.cpp new file mode 100644 index 0000000000..33ada4906d --- /dev/null +++ b/indra/newview/llexperienceassociationresponder.cpp @@ -0,0 +1,97 @@ +/** + * @file llexperienceassociationresponder.cpp + * @brief llexperienceassociationresponder implementation. This class combines + * a lookup for a script association and an experience details request. The first + * is always async, but the second may be cached locally. + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llexperienceassociationresponder.h" +#include "llexperiencecache.h" +#include "llviewerregion.h" +#include "llagent.h" + +ExperienceAssociationResponder::ExperienceAssociationResponder(ExperienceAssociationResponder::callback_t callback):mCallback(callback) +{ + ref(); +} + +void ExperienceAssociationResponder::fetchAssociatedExperience( const LLUUID& object_id, const LLUUID& item_id, callback_t callback ) +{ + LLSD request; + request["object-id"]=object_id; + request["item-id"]=item_id; + fetchAssociatedExperience(request, callback); +} + +void ExperienceAssociationResponder::fetchAssociatedExperience(LLSD& request, callback_t callback) +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string lookup_url=region->getCapability("GetMetadata"); + if(!lookup_url.empty()) + { + LLSD fields; + fields.append("experience"); + request["fields"] = fields; + LLHTTPClient::post(lookup_url, request, new ExperienceAssociationResponder(callback)); + } + } +} + +void ExperienceAssociationResponder::error( U32 status, const std::string& reason ) +{ + LLSD msg; + msg["error"]=(LLSD::Integer)status; + msg["message"]=reason; + LL_INFOS("ExperienceAssociation") << "Failed to look up associated experience: " << status << ": " << reason << LL_ENDL; + + sendResult(msg); + +} +void ExperienceAssociationResponder::result( const LLSD& content ) +{ + if(!content.has("experience")) + { + + LLSD msg; + msg["message"]="no experience"; + msg["error"]=-1; + sendResult(msg); + return; + } + + LLExperienceCache::get(content["experience"].asUUID(), boost::bind(&ExperienceAssociationResponder::sendResult, this, _1)); + +} + +void ExperienceAssociationResponder::sendResult( const LLSD& experience ) +{ + mCallback(experience); + unref(); +} + + + diff --git a/indra/newview/llexperienceassociationresponder.h b/indra/newview/llexperienceassociationresponder.h new file mode 100644 index 0000000000..8ff62a3dbc --- /dev/null +++ b/indra/newview/llexperienceassociationresponder.h @@ -0,0 +1,58 @@ +#include "llhttpclient.h" +#include "llsd.h" +/** + * @file llexperienceassociationresponder.h + * @brief llexperienceassociationresponder and related class definitions + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + + +#ifndef LL_LLEXPERIENCEASSOCIATIONRESPONDER_H +#define LL_LLEXPERIENCEASSOCIATIONRESPONDER_H + +#include "llhttpclient.h" +#include "llsd.h" + +class ExperienceAssociationResponder : public LLHTTPClient::Responder +{ +public: + typedef boost::function<void(const LLSD& experience)> callback_t; + + ExperienceAssociationResponder(callback_t callback); + + virtual void result(const LLSD& content); + virtual void error(U32 status, const std::string& reason); + + static void fetchAssociatedExperience(const LLUUID& object_it, const LLUUID& item_id, callback_t callback); + +private: + static void fetchAssociatedExperience(LLSD& request, callback_t callback); + + void sendResult(const LLSD& experience); + + callback_t mCallback; + +}; + +#endif // LL_LLEXPERIENCEASSOCIATIONRESPONDER_H diff --git a/indra/newview/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp new file mode 100644 index 0000000000..a565b5c8e7 --- /dev/null +++ b/indra/newview/llfloaterexperienceprofile.cpp @@ -0,0 +1,646 @@ +/** + * @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 "llagent.h" +#include "llexpandabletextbox.h" +#include "llexperiencecache.h" +#include "llfloaterexperienceprofile.h" +#include "llfloaterreg.h" +#include "llhttpclient.h" +#include "lllayoutstack.h" +#include "llsdserialize.h" +#include "llslurl.h" +#include "lltabcontainer.h" +#include "lltextbox.h" +#include "lltexturectrl.h" +#include "lltrans.h" +#include "llviewerregion.h" +#include "lllineeditor.h" +#include "llcombobox.h" +#include "llcheckboxctrl.h" +#include "llnotificationsutil.h" +#include "llappviewer.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 EDIT "edit_" + +#define IMG_LOGO "logo" + +#define PNL_IMAGE "image_panel" +#define PNL_DESC "description panel" +#define PNL_LOC "location panel" +#define PNL_MRKT "marketplace panel" + +#define BTN_EDIT "edit_btn" +#define BTN_ALLOW "allow_btn" +#define BTN_FORGET "forget_btn" +#define BTN_BLOCK "block_btn" +#define BTN_CANCEL "cancel_btn" +#define BTN_SAVE "save_btn" +#define BTN_ENABLE "enable_btn" +#define BTN_PRIVATE "private_btn" + + +LLFloaterExperienceProfile::LLFloaterExperienceProfile(const LLSD& data) + : LLFloater(data) + , mExperienceId(data.asUUID()) + , mImagePanel(NULL) + , mDescriptionPanel(NULL) + , mLocationPanel(NULL) + , mMarketplacePanel(NULL) + , 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 HandleResponder<LLFloaterExperienceProfile> +{ +public: + ExperiencePreferencesResponder(const LLHandle<LLFloaterExperienceProfile>& parent):HandleResponder<LLFloaterExperienceProfile>(parent) + { + } + + + virtual void result(const LLSD& content) + { + LLFloaterExperienceProfile* parent=mParent.get(); + if(parent) + { + parent->setPreferences(content); + } + } +}; + + +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; + } + + parent->getChild<LLButton>(BTN_EDIT)->setVisible(enabled && content["status"].asBoolean()); + } +}; + +BOOL LLFloaterExperienceProfile::postBuild() +{ + mImagePanel = getChild<LLLayoutPanel>(PNL_IMAGE); + mDescriptionPanel = getChild<LLLayoutPanel>(PNL_DESC); + mLocationPanel = getChild<LLLayoutPanel>(PNL_LOC); + mMarketplacePanel = getChild<LLLayoutPanel>(PNL_MRKT); + + 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>())); + } + + lookup_url=region->getCapability("ExperiencePreferences"); + if(!lookup_url.empty()) + { + LLHTTPClient::get(lookup_url+"?"+mExperienceId.asString(), new ExperiencePreferencesResponder(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)); + + + getChild<LLUICtrl>(EDIT TF_NAME)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + getChild<LLUICtrl>(EDIT TF_DESC)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + getChild<LLUICtrl>(EDIT TF_SLURL)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + getChild<LLUICtrl>(EDIT TF_MATURITY)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + getChild<LLUICtrl>(EDIT TF_MRKT)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + getChild<LLLineEditor>(EDIT TF_NAME)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + childSetAction(EDIT BTN_ENABLE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + childSetAction(EDIT BTN_PRIVATE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this)); + + getChild<LLTextEditor>(EDIT TF_DESC)->setCommitOnFocusLost(TRUE); + + return TRUE; +} + +void LLFloaterExperienceProfile::experienceCallback(LLHandle<LLFloaterExperienceProfile> handle, const LLSD& experience ) +{ + LLFloaterExperienceProfile* pllpep = handle.get(); + if(pllpep) + { + pllpep->refreshExperience(experience); + } +} + +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(getDerivedHandle<LLFloaterExperienceProfile>())); + +} + + +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(getDerivedHandle<LLFloaterExperienceProfile>())); +} + +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; + + if(experience.has(LLExperienceCache::MISSING)) + { + mImagePanel->setVisible(FALSE); + mDescriptionPanel->setVisible(FALSE); + mLocationPanel->setVisible(FALSE); + mMarketplacePanel->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); + mDescriptionPanel->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); + child->setText(value); + mLocationPanel->setVisible(value.length()>0); + + linechild = getChild<LLLineEditor>(EDIT TF_SLURL); + linechild->setText(value); + + setMaturityString((U8)(experience[LLExperienceCache::MATURITY].asInteger()), getChild<LLTextBox>(TF_MATURITY), getChild<LLComboBox>(EDIT TF_MATURITY)); + + child = getChild<LLTextBox>(TF_OWNER); + + LLUUID id = experience[LLExperienceCache::GROUP_ID].asUUID(); + if(id.notNull()) + { + value = LLSLURL("group", id, "inspect").getSLURLString(); + } + else + { + id = experience[LLExperienceCache::AGENT_ID].asUUID(); + value = LLSLURL("agent", id, "inspect").getSLURLString(); + } + child->setText(value); + + 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); + mMarketplacePanel->setVisible(TRUE); + } + else + { + mMarketplacePanel->setVisible(FALSE); + } + + linechild = getChild<LLLineEditor>(EDIT TF_MRKT); + linechild->setText(value); + + if(data.has(IMG_LOGO)) + { + LLTextureCtrl* logo = getChild<LLTextureCtrl>(IMG_LOGO); + logo->setImageAssetID(data[IMG_LOGO].asUUID()); + mImagePanel->setVisible(TRUE); + + logo = getChild<LLTextureCtrl>(EDIT IMG_LOGO); + logo->setImageAssetID(data[IMG_LOGO].asUUID()); + } + } + + LLCheckBoxCtrl* enable = getChild<LLCheckBoxCtrl>(EDIT BTN_ENABLE); + enable->set( 0 == (mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger() & LLExperienceCache::PROPERTY_DISABLED)); + + enable = getChild<LLCheckBoxCtrl>(EDIT BTN_PRIVATE); + enable->set( 0 != (mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger() & LLExperienceCache::PROPERTY_PRIVATE)); + + mDirty=false; + setCanClose(!mDirty); + + getChild<LLButton>(BTN_SAVE)->setEnabled(mDirty); +} + +void LLFloaterExperienceProfile::setPreferences( const LLSD& content ) +{ + const LLSD& experiences = content["experiences"]; + const LLSD& blocked = content["blocked"]; + LLButton* button; + + + for(LLSD::array_const_iterator it = experiences.beginArray(); it != experiences.endArray() ; ++it) + { + if(it->asUUID()==mExperienceId) + { + button=getChild<LLButton>(BTN_ALLOW); + button->setEnabled(FALSE); + + button=getChild<LLButton>(BTN_FORGET); + button->setEnabled(TRUE); + + button=getChild<LLButton>(BTN_BLOCK); + button->setEnabled(TRUE); + return; + } + } + + for(LLSD::array_const_iterator it = blocked.beginArray(); it != blocked.endArray() ; ++it) + { + if(it->asUUID()==mExperienceId) + { + button=getChild<LLButton>(BTN_ALLOW); + button->setEnabled(TRUE); + + button=getChild<LLButton>(BTN_FORGET); + button->setEnabled(TRUE); + + button=getChild<LLButton>(BTN_BLOCK); + button->setEnabled(FALSE); + return; + } + } + + + 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::onFieldChanged() +{ + mDirty=true; + setCanClose(!mDirty); + 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; + + LLSD package=mExperienceDetails; + + package[LLExperienceCache::NAME] = getChild<LLLineEditor>(EDIT TF_NAME)->getText(); + package[LLExperienceCache::DESCRIPTION] = getChild<LLTextEditor>(EDIT TF_DESC)->getText(); + package[LLExperienceCache::SLURL] = getChild<LLLineEditor>(EDIT TF_SLURL)->getText(); + + package[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)) + { + package[LLExperienceCache::METADATA]=os.str(); + } + + int properties = package[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; + } + + package[LLExperienceCache::PROPERTIES] = properties; + + LLHTTPClient::post(url, package, new ExperienceUpdateResponder(getDerivedHandle<LLFloaterExperienceProfile>())); +} + +void LLFloaterExperienceProfile::onSaveComplete( const LLSD& content ) +{ + LLUUID id = getExperienceId(); + + 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)); + } +} diff --git a/indra/newview/llfloaterexperienceprofile.h b/indra/newview/llfloaterexperienceprofile.h new file mode 100644 index 0000000000..1f1e135c01 --- /dev/null +++ b/indra/newview/llfloaterexperienceprofile.h @@ -0,0 +1,91 @@ +/** + * @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(); + +protected: + void onClickEdit(); + void onClickPermission(const char* permission); + void onClickForget(); + void onClickCancel(); + + void changeToView(); + + void onClickSave(); + + void onFieldChanged(); + + static void experienceCallback(LLHandle<LLFloaterExperienceProfile> handle, const LLSD& experience); + + 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 ); + LLUUID mExperienceId; + LLSD mExperienceDetails; + + LLLayoutPanel* mImagePanel; + LLLayoutPanel* mDescriptionPanel; + LLLayoutPanel* mLocationPanel; + LLLayoutPanel* mMarketplacePanel; + 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..b862b41bba --- /dev/null +++ b/indra/newview/llfloaterexperiences.cpp @@ -0,0 +1,14 @@ +#include "llviewerprecompiledheaders.h" + +#include "llpanelexperiences.h" +#include "llfloaterexperiences.h" + +LLFloaterExperiences::LLFloaterExperiences(const LLSD& data) + :LLFloater(data) +{ +} + +BOOL LLFloaterExperiences::postBuild() +{ + return TRUE; +} diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h new file mode 100644 index 0000000000..1e5f216f8d --- /dev/null +++ b/indra/newview/llfloaterexperiences.h @@ -0,0 +1,45 @@ +/** + * @file llfloaterexperiences.h + * @brief LLFloaterExperiences class definition + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATEREXPERIENCES_H +#define LL_LLFLOATEREXPERIENCES_H + +#include "llfloater.h" + +class LLFloaterExperiences : + public LLFloater +{ +public: + LLFloaterExperiences(const LLSD& data); + +protected: + /*virtual*/ BOOL postBuild(); + +private: + +}; + +#endif //LL_LLFLOATEREXPERIENCES_H diff --git a/indra/newview/llpanelexperiences.cpp b/indra/newview/llpanelexperiences.cpp new file mode 100644 index 0000000000..617ceef615 --- /dev/null +++ b/indra/newview/llpanelexperiences.cpp @@ -0,0 +1,302 @@ +#include "llviewerprecompiledheaders.h" + + +#include "llpanelprofile.h" +#include "lluictrlfactory.h" +#include "llexperiencecache.h" +#include "llagent.h" + +#include "llpanelexperiences.h" + + +static LLRegisterPanelClassWrapper<LLPanelExperiences> register_experiences_panel("experiences_panel"); + + +LLPanelExperiences::LLPanelExperiences( ) + : mExperiencesList(NULL), + mExperiencesAccTab(NULL), + mProfilePanel(NULL), + mPanelExperienceInfo(NULL), + mNoExperiences(false) +{ + +} + +void* LLPanelExperiences::create( void* data ) +{ + return new LLPanelExperiences(); +} + +void ExperienceResult(LLHandle<LLPanelExperiences> panel, const LLSD& experience) +{ + LLPanelExperiences* experiencePanel = panel.get(); + if(experiencePanel) + { + experiencePanel->addExperienceInfo(experience); + } +} + +class LLExperienceListResponder : public LLHTTPClient::Responder +{ +public: + LLExperienceListResponder(const LLHandle<LLPanelExperiences>& parent):mParent(parent) + { + } + + LLHandle<LLPanelExperiences> mParent; + + virtual void result(const LLSD& content) + { + if(mParent.isDead()) + return; + + LLSD experiences = content["experiences"]; + LLSD::array_const_iterator it = experiences.beginArray(); + for( /**/ ; it != experiences.endArray(); ++it) + { + LLUUID public_key = it->asUUID(); + + LLExperienceCache::get(public_key, boost::bind(ExperienceResult, mParent, _1)); + } + } +}; + +void LLPanelExperiences::addExperienceInfo(const LLSD& experience) +{ + LLExperienceItem* item = new LLExperienceItem(); + if(experience.has(LLExperienceCache::NAME)) + { + item->setExperienceName(experience[LLExperienceCache::NAME].asString()); + } + else if(experience.has("error")) + { + item->setExperienceName(experience["error"].asString()); + } + + if(experience.has(LLExperienceCache::DESCRIPTION)) + { + item->setExperienceDescription(experience[LLExperienceCache::DESCRIPTION].asString()); + } + + mExperiencesList->addItem(item); + +} + + +BOOL LLPanelExperiences::postBuild( void ) +{ + mExperiencesList = getChild<LLFlatListView>("experiences_list"); + if(hasString("no_experiences")) + { + mExperiencesList->setNoItemsCommentText(getString("no_experiences")); + } + + + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string lookup_url=region->getCapability("GetExperiences"); + if(!lookup_url.empty()) + { + LLHTTPClient::get(lookup_url, new LLExperienceListResponder(getDerivedHandle<LLPanelExperiences>())); + } + } + + mExperiencesAccTab = getChild<LLAccordionCtrlTab>("tab_experiences"); + mExperiencesAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelExperiences::onAccordionStateChanged, this, mExperiencesAccTab)); + mExperiencesAccTab->setDisplayChildren(true); + + return TRUE; +} + +void LLPanelExperiences::onOpen( const LLSD& key ) +{ + LLPanel::onOpen(key); +} + +void LLPanelExperiences::onClosePanel() +{ + if (mPanelExperienceInfo) + { + onPanelExperienceClose(mPanelExperienceInfo); + } +} + +void LLPanelExperiences::updateData() +{ + if(isDirty()) + { + mNoExperiences = false; + } +} + +LLExperienceItem* LLPanelExperiences::getSelectedExperienceItem() +{ + LLPanel* selected_item = mExperiencesList->getSelectedItem(); + if (!selected_item) return NULL; + + return dynamic_cast<LLExperienceItem*>(selected_item); +} + +void LLPanelExperiences::setProfilePanel( LLPanelProfile* profile_panel ) +{ + mProfilePanel = profile_panel; +} + +void LLPanelExperiences::onListCommit( const LLFlatListView* f_list ) +{ + if(f_list == mExperiencesList) + { + mExperiencesList->resetSelection(true); + } + else + { + llwarns << "Unknown list" << llendl; + } + + //updateButtons(); +} + +void LLPanelExperiences::onAccordionStateChanged( const LLAccordionCtrlTab* acc_tab ) +{ + if(!mExperiencesAccTab->getDisplayChildren()) + { + mExperiencesList->resetSelection(true); + } + +} + +void LLPanelExperiences::openExperienceInfo() +{ + LLSD selected_value = mExperiencesList->getSelectedValue(); + if(selected_value.isUndefined()) + { + return; + } + + LLExperienceItem* experience = (LLExperienceItem*)mExperiencesList->getSelectedItem(); + + createExperienceInfoPanel(); + + LLSD params; + params["experience_name"] = experience->getExperienceName(); + params["experience_desc"] = experience->getExperienceDescription(); + + getProfilePanel()->openPanel(mPanelExperienceInfo, params); + +} + + +void LLPanelExperiences::createExperienceInfoPanel() +{ + if(!mPanelExperienceInfo) + { + mPanelExperienceInfo = LLPanelExperienceInfo::create(); + mPanelExperienceInfo->setExitCallback(boost::bind(&LLPanelExperiences::onPanelExperienceClose, this, mPanelExperienceInfo)); + mPanelExperienceInfo->setVisible(FALSE); + } +} + +void LLPanelExperiences::onPanelExperienceClose( LLPanel* panel ) +{ + getProfilePanel()->closePanel(panel); +} + +LLPanelProfile* LLPanelExperiences::getProfilePanel() +{ + llassert_always(NULL != mProfilePanel); + + return mProfilePanel; +} + + + + + + + + + + + +LLExperienceItem::LLExperienceItem() +{ + buildFromFile("panel_experience_info.xml"); +} + +void LLExperienceItem::init( LLSD* experience_data ) +{ + if(experience_data) + { + setExperienceDescription(experience_data->has(LLExperienceCache::DESCRIPTION)?(*experience_data)[LLExperienceCache::DESCRIPTION].asString() : std::string()); + setExperienceName(experience_data->has(LLExperienceCache::NAME)?(*experience_data)[LLExperienceCache::NAME].asString() : std::string()); + } +} + +void LLExperienceItem::setExperienceDescription( const std::string& val ) +{ + mExperienceDescription = val; + getChild<LLUICtrl>("experience_desc")->setValue(val); +} + +void LLExperienceItem::setExperienceName( const std::string& val ) +{ + mExperienceName = val; + getChild<LLUICtrl>("experience_name")->setValue(val); +} + +BOOL LLExperienceItem::postBuild() +{ + return TRUE; +} + +void LLExperienceItem::update() +{ + +} + +void LLExperienceItem::processProperties( void* data, EAvatarProcessorType type ) +{ + +} + +LLExperienceItem::~LLExperienceItem() +{ + +} + + +void LLPanelExperienceInfo::setExperienceName( const std::string& name ) +{ + getChild<LLUICtrl>("experience_name")->setValue(name); +} + +void LLPanelExperienceInfo::setExperienceDesc( const std::string& desc ) +{ + getChild<LLUICtrl>("experience_desc")->setValue(desc); +} + +void LLPanelExperienceInfo::onOpen( const LLSD& key ) +{ + setExperienceName(key["experience_name"]); + setExperienceDesc(key["experience_desc"]); + + /* + LLAvatarPropertiesProcessor::getInstance()->addObserver( + getAvatarId(), this); + LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest( + getAvatarId(), getPickId()); + */ +} + +LLPanelExperienceInfo* LLPanelExperienceInfo::create() +{ + LLPanelExperienceInfo* panel = new LLPanelExperienceInfo(); + panel->buildFromFile("panel_experience_info.xml"); + return panel; +} + +void LLPanelExperienceInfo::setExitCallback( const commit_callback_t& cb ) +{ + getChild<LLButton>("back_btn")->setClickedCallback(cb); +} diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h new file mode 100644 index 0000000000..1fe3f6ae1d --- /dev/null +++ b/indra/newview/llpanelexperiences.h @@ -0,0 +1,119 @@ +/** + * @file llpanelpicks.h + * @brief LLPanelPicks and related class definitions + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELEXPERIENCES_H +#define LL_LLPANELEXPERIENCES_H + +#include "llaccordionctrltab.h" +#include "llflatlistview.h" +#include "llpanelavatar.h" + +class LLExperienceItem; +class LLPanelProfile; + +class LLPanelExperienceInfo + : public LLPanel +{ +public: + static LLPanelExperienceInfo* create(); + + void onOpen(const LLSD& key); + void setExperienceName( const std::string& name ); + void setExperienceDesc( const std::string& desc ); + + + virtual void setExitCallback(const commit_callback_t& cb); +}; + + +class LLPanelExperiences + : public LLPanel /*LLPanelProfileTab*/ +{ +public: + LLPanelExperiences(); + + static void* create(void* data); + + /*virtual*/ BOOL postBuild(void); + + /*virtual*/ void onOpen(const LLSD& key); + + /*virtual*/ void onClosePanel(); + + void updateData(); + + LLExperienceItem* getSelectedExperienceItem(); + + void setProfilePanel(LLPanelProfile* profile_panel); + void addExperienceInfo(const LLSD& experience); +protected: + + void onListCommit(const LLFlatListView* f_list); + void onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab); + + + void openExperienceInfo(); + void createExperienceInfoPanel(); + void onPanelExperienceClose(LLPanel* panel); + LLPanelProfile* getProfilePanel(); +private: + LLFlatListView* mExperiencesList; + LLAccordionCtrlTab* mExperiencesAccTab; + LLPanelProfile* mProfilePanel; + LLPanelExperienceInfo* mPanelExperienceInfo; + bool mNoExperiences; +}; + + +class LLExperienceItem + : public LLPanel + //, public LLAvatarPropertiesObserver +{ +public: + LLExperienceItem(); + ~LLExperienceItem(); + + void init(LLSD* experience_data); + /*virtual*/ BOOL postBuild(); + void update(); + + /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); + + void setCreatorID(const LLUUID& val) { mCreatorID = val; } + void setExperienceDescription(const std::string& val); + void setExperienceName(const std::string& val); + + const LLUUID& getCreatorID() const { return mCreatorID; } + const std::string& getExperienceName() const { return mExperienceName; } + const std::string& getExperienceDescription() const { return mExperienceDescription; } + +protected: + LLUUID mCreatorID; + + std::string mExperienceName; + std::string mExperienceDescription; +}; +#endif // LL_LLPANELEXPERIENCES_H diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index e533be7f24..1d3c7a3f20 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,106 @@ bool LLScriptEdCore::enableLoadFromFileMenu(void* userdata) return (self && self->mEditor) ? self->mEditor->canLoadOrSaveToFile() : FALSE; } +LLUUID LLScriptEdCore::getAssociatedExperience()const +{ + return mAssociatedExperience; +} + +void LLLiveLSLEditor::setExperienceIds( const LLSD& experience_ids ) +{ + mExperienceIds=experience_ids; + updateExperiencePanel(); +} + + +void LLLiveLSLEditor::updateExperiencePanel() +{ + BOOL editable = getIsModifiable(); + + if(mScriptEd->getAssociatedExperience().isNull()) + { + mExperienceEnabled->set(FALSE); + mExperiences->setVisible(FALSE); + if(mExperienceIds.size()>0) + { + mExperienceEnabled->setEnabled(TRUE); + mExperienceEnabled->setToolTip(getString("add_experiences")); + } + else + { + mExperienceEnabled->setEnabled(FALSE); + mExperienceEnabled->setToolTip(getString("no_experiences")); + } + getChild<LLButton>("view_profile")->setVisible(FALSE); + } + else + { + mExperienceEnabled->setToolTip(getString("experience_enabled")); + mExperienceEnabled->setEnabled(editable); + mExperienceEnabled->set(TRUE); + mExperiences->setVisible(TRUE); + getChild<LLButton>("view_profile")->setVisible(TRUE); + buildExperienceList(); + } +} + +void LLLiveLSLEditor::addExperienceInfo(const LLSD& experience, BOOL enabled) +{ + LLUUID id = experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); + EAddPosition position = (id == mScriptEd->getAssociatedExperience())?ADD_TOP:ADD_BOTTOM; + LLScrollListItem* item=mExperiences->add(experience[LLExperienceCache::NAME], id, position ); + if(!enabled) + { + item->setEnabled(FALSE); + } +} + +void LLLiveLSLEditor::buildExperienceList() +{ + mExperiences->clearRows(); + bool foundAssociated=false; + for(LLSD::array_const_iterator it = mExperienceIds.beginArray(); it != mExperienceIds.endArray(); ++it) + { + LLUUID id = it->asUUID(); + foundAssociated |= (id == mScriptEd->getAssociatedExperience()); + LLExperienceCache::get(id, boost::bind(&LLLiveLSLEditor::addExperienceInfo, this, _1, TRUE)); + } + + if(!foundAssociated ) + { + LLExperienceCache::get(mScriptEd->getAssociatedExperience(), boost::bind(&LLLiveLSLEditor::addExperienceInfo, this, _1, FALSE)); + } + +} + + +void LLScriptEdCore::setAssociatedExperience( const LLUUID& experience_id ) +{ + mAssociatedExperience = experience_id; +} + + + +void LLLiveLSLEditor::requestExperiences() +{ + if (!getIsModifiable()) + { + return; + } + + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string lookup_url=region->getCapability("GetCreatorExperiences"); + if(!lookup_url.empty()) + { + LLHTTPClient::get(lookup_url, new ExperienceResponder(getDerivedHandle<LLLiveLSLEditor>())); + } + } +} + + + /// --------------------------------------------------------------------------- /// LLScriptEdContainer /// --------------------------------------------------------------------------- @@ -1771,6 +1943,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(); } @@ -1819,8 +2001,8 @@ void LLLiveLSLEditor::loadAsset() || gAgent.isGodlike())) { mItem = new LLViewerInventoryItem(item); - //llinfos << "asset id " << mItem->getAssetUUID() << llendl; } + ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLLiveLSLEditor::setAssociatedExperience, getDerivedHandle<LLLiveLSLEditor>(), _1)); if(!gAgent.isGodlike() && (item @@ -1881,7 +2063,7 @@ void LLLiveLSLEditor::loadAsset() LLHost host(object->getRegion()->getIP(), object->getRegion()->getPort()); gMessageSystem->sendReliable(host); - */ + */ } } else @@ -1904,6 +2086,8 @@ void LLLiveLSLEditor::loadAsset() time_corrected()); mAssetStatus = PREVIEW_ASSET_LOADED; } + + requestExperiences(); } // static @@ -1914,7 +2098,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 +2351,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 +2360,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 +2368,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 +2620,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..29d1443d6c 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,19 @@ 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 addExperienceInfo( const LLSD& experience, BOOL enabled ); + void setExperienceIds(const LLSD& experience_ids); + void buildExperienceList(); + void updateExperiencePanel(); + void requestExperiences(); + void experienceChanged(); + void addAssociatedExperience(const LLSD& experience); private: virtual BOOL canClose(); @@ -240,11 +256,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 +297,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 92c2863ffd..040c4ee72d 100755 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -43,6 +43,8 @@ #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerobjectlist.h" +#include "llexperiencecache.h" +#include "llexperienceassociationresponder.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -316,6 +318,15 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) is_obj_modify = object->permOwnerModify(); } + if(item->getInventoryType() == LLInventoryType::IT_LSL) + { + getChildView("LabelItemExperienceTitle")->setVisible(TRUE); + LLTextBox* tb = getChild<LLTextBox>("LabelItemExperience"); + tb->setText(getString("loading_experience")); + tb->setVisible(TRUE); + ExperienceAssociationResponder::fetchAssociatedExperience(item->getParentUUID(), item->getUUID(), boost::bind(&LLSidepanelItemInfo::setAssociatedExperience, getDerivedHandle<LLSidepanelItemInfo>(), _1)); + } + ////////////////////// // ITEM NAME & DESC // ////////////////////// @@ -666,6 +677,30 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } } + +void LLSidepanelItemInfo::setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience ) +{ + LLSidepanelItemInfo* info = hInfo.get(); + if(info) + { + LLUUID id; + if(experience.has(LLExperienceCache::EXPERIENCE_ID) && experience.has(LLExperienceCache::NAME)) + { + id=experience[LLExperienceCache::EXPERIENCE_ID].asUUID(); + } + LLTextBox* xpName = info->getChild<LLTextBox>("LabelItemExperience"); + if(id.isNull()) + { + xpName->setText(info->getString("no_experience")); + } + else + { + xpName->setText(experience[LLExperienceCache::NAME].asString()); + } + } +} + + void LLSidepanelItemInfo::startObjectInventoryObserver() { if (!mObjectInventoryObserver) diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h index 12aaca923e..2e24e58a2a 100755 --- a/indra/newview/llsidepaneliteminfo.h +++ b/indra/newview/llsidepaneliteminfo.h @@ -67,6 +67,8 @@ protected: void refreshFromItem(LLViewerInventoryItem* item); private: + static void setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience ); + void startObjectInventoryObserver(); void stopObjectInventoryObserver(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 67a76460a7..8997a46aa0 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" @@ -1407,6 +1408,9 @@ bool idle_startup() LLStartUp::initNameCache(); display_startup(); + LLStartUp::initExperienceCache(); + display_startup(); + // update the voice settings *after* gCacheName initialization // so that we can construct voice UI that relies on the name cache LLVoiceClient::getInstance()->updateSettings(); @@ -2820,6 +2824,13 @@ void LLStartUp::initNameCache() LLAvatarNameCache::setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames")); } + +void LLStartUp::initExperienceCache() +{ + LLAppViewer::instance()->loadExperienceCache(); + LLExperienceCache::initClass(); +} + void LLStartUp::cleanupNameCache() { LLAvatarNameCache::cleanupClass(); @@ -3516,3 +3527,4 @@ void transition_back_to_login_panel(const std::string& emsg) reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW ); gSavedSettings.setBOOL("AutoLogin", FALSE); } + diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 760e38890b..00e03bcda6 100755 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -91,6 +91,7 @@ public: static void fontInit(); static void initNameCache(); + static void initExperienceCache(); static void cleanupNameCache(); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index c6b28b9e5e..13ceaf491c 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -58,6 +58,8 @@ #include "llfloatereditsky.h" #include "llfloatereditwater.h" #include "llfloaterenvironmentsettings.h" +#include "llfloaterexperienceprofile.h" +#include "llfloaterexperiences.h" #include "llfloaterevent.h" #include "llfloaterdestinations.h" #include "llfloaterfonttest.h" @@ -206,8 +208,10 @@ 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("font_test", "floater_font_test.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFontTest>); LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGesture>); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 8422708add..60e0df90a2 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1596,7 +1596,16 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) } capabilityNames.append("GetDisplayNames"); - capabilityNames.append("GetMesh"); + capabilityNames.append("GetExperiences"); + capabilityNames.append("GetExperienceInfo"); + capabilityNames.append("GetAdminExperiences"); + capabilityNames.append("GetCreatorExperiences"); + capabilityNames.append("ExperiencePreferences"); + capabilityNames.append("UpdateExperience"); + capabilityNames.append("IsExperienceAdmin"); + capabilityNames.append("IsExperienceContributor"); + capabilityNames.append("GetMesh"); + capabilityNames.append("GetMetadata"); capabilityNames.append("GetObjectCost"); capabilityNames.append("GetObjectPhysicsData"); capabilityNames.append("GetTexture"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 1a050800b4..15d5dd63c7 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -43,6 +43,7 @@ #include "llanimationstates.h" #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" +#include "llexperiencecache.h" #include "llphysicsmotion.h" #include "llviewercontrol.h" #include "llcallingcard.h" // IDEVO for LLAvatarTracker @@ -2037,7 +2038,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) idleUpdateBelowWater(); // wind effect uses this idleUpdateWindEffect(); } - + idleUpdateNameTag( root_pos_last ); idleUpdateRenderCost(); } diff --git a/indra/newview/skins/default/xui/en/floater_experienceprofile.xml b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml new file mode 100644 index 0000000000..f47042ea33 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experienceprofile.xml @@ -0,0 +1,537 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> + +<floater + positioning="cascading" + can_close="true" + enabled="true" + can_resize="true" + title="EXPERIENCE PROFILE" + width="358"> + <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" + text_color="White" + top="2" + value="Experience Profile" + use_ellipses="true" + left="3" + right="-3"/> + <tab_container + hide_tabs="true" + follows="all" + height="540" + layout="topleft" + left="5" + min_height="250" + top_pad="3" + width="348" + name="tab_container"> + <panel + background_visible="true" + follows="all" + height="570" + layout="topleft" + left="0" + min_height="250" + top="0" + width="348" + name="panel_experience_info"> + <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="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" + 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" + 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="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="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" + name="LocationTextText" + width="288"> + someplace + </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" + name="marketplace" + width="288"> + someplace + </text> + </layout_panel> + <layout_panel + follows="left|top|right" + height="92" + 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="-3" + 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="3" + width="120" + left="73" + visible="false"/> + <button + follows="bottom|left" + height="23" + label="Allow" + layout="topleft" + name="allow_btn" + width="80" + top_pad="3" + left="10" + enabled="false"/> + <button + follows="bottom|left" + height="23" + label="Forget" + layout="topleft" + name="forget_btn" + width="80" + top_pad="-23" + left_pad="3" + enabled="false"/> + <button + follows="bottom|left" + height="23" + label="Block" + layout="topleft" + name="block_btn" + width="80" + top_pad="-23" + left_pad="3" + enabled="false"/> + + </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="525" + 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="525" + 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"/> + <text + type="string" + length="1" + follows="left|top|right" + height="14" + layout="topleft" + left="10" + top_pad="10" + name="edit_Location" + right="-10"> + Location: + </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_LocationTextText" + right="-10"/> + <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"/> + <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="-17" + 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> + <check_box width="140" + height="21" + left="10" + layout="topleft" + follows="top|left" + label="Enable Experience" + name="edit_enable_btn"/> + <check_box width="130" + height="21" + top_pad="-21" + right="-10" + layout="topleft" + follows="top|left" + label="Hide In Search" + name="edit_private_btn"/> + <button + follows="top|left" + height="23" + label="Cancel" + layout="topleft" + name="cancel_btn" + width="120" + top_pad="15" + left="10" + visible="true"/> + <button + follows="top|right" + height="23" + label="Save" + layout="topleft" + name="save_btn" + top_pad="-23" + width="120" + right="-10" + visible="true"/> + </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..57541c8b35 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_experiences.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> + +<floater + positioning="cascading" + can_close="true" + can_resize="true" + height="400" + help_topic="sidebar_experiences" + min_height="300" + min_width="300" + layout="topleft" + name="floater_experiences" + save_rect="false" + single_instance="true" + reuse_instance="false" + title="EXPERIENCES" + width="400"> + <panel + top="3" + left="3" + layout="topleft" + right="-3" + follows="all" + height="300" + class="experiences_panel" + filename="panel_experiences.xml" + > + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml index 5cd7cd196d..d8c2a753a1 100755 --- a/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml +++ b/indra/newview/skins/default/xui/en/floater_live_lsleditor.xml @@ -1,71 +1,110 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater - legacy_header_height="18" - bevel_style="none" - border_style="line" - can_resize="true" - height="580" - layout="topleft" - min_height="271" - min_width="290" - name="script ed float" - help_topic="script_ed_float" - save_rect="true" - title="SCRIPT: NEW SCRIPT" - width="508"> - <floater.string - name="not_allowed"> - You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. - </floater.string> - <floater.string - name="script_running"> - Running - </floater.string> - <floater.string - name="Title"> - SCRIPT: [NAME] - </floater.string> - <panel - bevel_style="none" + legacy_header_height="18" + bevel_style="none" + border_style="line" + can_resize="true" + height="582" + layout="topleft" + min_height="271" + min_width="328" + name="script ed float" + help_topic="script_ed_float" + save_rect="true" + title="SCRIPT: NEW SCRIPT" + width="508"> + <floater.string + name="not_allowed"> + You can not view or edit this script, since it has been set as "no copy". You need full permissions to view or edit a script inside an object. + </floater.string> + <floater.string + name="script_running"> + Running + </floater.string> + <floater.string + name="Title"> + SCRIPT: [NAME] + </floater.string> + <floater.string + name="experience_enabled"> + Uncheck to remove the current experience + </floater.string> + <floater.string + name="no_experiences"> + You are not authorized for any experiences + </floater.string> + <floater.string + name="add_experiences"> + Select to add an experience + </floater.string> + <panel + bevel_style="none" - border_style="line" - follows="left|top|right|bottom" - height="522" - layout="topleft" - left="10" - name="script ed panel" - top="20" - width="497" /> - <button - follows="left|bottom" - height="23" - label="Reset" - label_selected="Reset" - layout="topleft" - name="Reset" - left="10" - width="85" /> - <check_box + border_style="line" + follows="left|top|right|bottom" + height="499" + layout="topleft" + left="10" + name="script ed panel" + top="16" + width="501" /> + <button + follows="left|bottom" + height="23" + label="Reset" + label_selected="Reset" + layout="topleft" + name="Reset" + left="10" + width="85" /> + <check_box left_delta="90" top_delta="3" - enabled="false" - follows="left|bottom" - font="SansSerif" - height="18" - initial_value="true" - label="Running" - layout="topleft" - name="running" - width="205" /> - <check_box + enabled="false" + follows="left|bottom" + font="SansSerif" + height="18" + initial_value="true" + label="Running" + layout="topleft" + name="running" + width="205" /> + <check_box left_delta="140" - enabled="true" - follows="left|bottom" - font="SansSerif" - height="18" - initial_value="true" - label="Mono" - layout="topleft" - name="mono" - width="100" /> + enabled="true" + follows="left|bottom" + font="SansSerif" + height="18" + initial_value="true" + label="Mono" + layout="topleft" + name="mono" + width="100" /> + <check_box width="130" + height="21" + enabled="false" + left="9" + top_pad="10" + layout="topleft" + follows="bottom|left" + label="Use Experience:" + name="enable_xp"/> + <combo_box + label="" + top_pad="-21" + left="149" + right="467" + layout="topleft" + follows="left|bottom|right" + visible="false" + name="Experiences..."/> + <button label=">" + name="view_profile" + height="23" + width="23" + right="496" + layout="topleft" + top_pad="-23" + follows="right" + visible="false"/> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index f253ed3e06..1cfe147b07 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -53,6 +53,14 @@ parameter="" /> </menu_item_call> <menu_item_call + label="Experiences..." + name="Experiences" + shortcut="control|E"> + <menu_item_call.on_click + function="Floater.ToggleOrBringToFront" + parameter="experiences"/> + </menu_item_call> + <menu_item_call label="Places..." name="Places"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/panel_experience_info.xml b/indra/newview/skins/default/xui/en/panel_experience_info.xml new file mode 100644 index 0000000000..268e7462c1 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experience_info.xml @@ -0,0 +1,377 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + background_visible="true" + follows="all" + height="570" + layout="topleft" + left="5" + min_height="350" + top="5" + width="348" + name="panel_experience_info"> + <text + follows="top|left|right" + font="SansSerifHugeBold" + height="26" + layout="topleft" + left_pad="4" + name="title" + text_color="White" + top="2" + value="Experience Profile" + use_ellipses="true" + left="3" + right="-3"/> + <scroll_container + color="DkGray2" + follows="all" + height="532" + layout="topleft" + left="9" + name="xp_scroll" + opaque="true" + top_pad="10" + width="330"> + <panel + bg_alpha_color="DkGray2" + follows="top|left" + height="480" + layout="topleft" + left="0" + min_height="480" + name="scrolling_panel" + top="0" + width="315" + min_width="315"> + <layout_stack + follows="all" + height="480" + layout="topleft" + left="0" + top="0" + orientation="vertical" + width="315"> + <layout_panel + follows="all" + height="197" + layout="topleft" + left="0" + top="0" + auto_resize="false" + visible="true" + width="315" + name="image_panel"> + <texture_picker + enabled="false" + fallback_image="default_land_picture.j2c" + follows="left|top" + height="197" + layout="topleft" + left="10" + name="logo" + top="10" + width="290" /> + </layout_panel> + <layout_panel + follows="all" + height="19" + layout="topleft" + left="0" + top="5" + width="313" + auto_resize="false" + > + <text + follows="left|top|right" + font="SansSerifLarge" + height="14" + layout="topleft" + left="10" + name="experience_title" + text_color="white" + top="0" + use_ellipses="true" + value="Kyle's Superhero RPG" + width="288"/> + </layout_panel> + <layout_panel + follows="" + height="50" + layout="topleft" + left="0" + top="0" + auto_resize="false" + width="315" + name="description panel"> + <expandable_text + follows="left|top|right" + font="SansSerif" + height="50" + layout="topleft" + left="7" + name="experience_description" + top="0" + value="It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. It is mainly just a lot of men in tights on patrol for evil-doers. " + width="293"/> + </layout_panel> + <layout_panel + follows="all" + height="69" + layout="topleft" + left="0" + top="5" + width="313" + visible="true" + auto_resize="false" + name="location panel" + > + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" + name="Location" + width="290"> + Location: + </text> + <text + type="string" + length="1" + follows="left|top|right" + height="18" + layout="topleft" + left="10" + valign="center" + name="LocationTextText" + width="288"> + someplace + </text> + <button + follows="bottom|left" + height="23" + label="Teleport" + layout="topleft" + name="teleport_btn" + width="151" + left="10"/> + <button + follows="bottom|left" + height="23" + label="Map" + layout="topleft" + name="map_btn" + top_pad="-23" + width="101" + left_pad="5"/> + </layout_panel> + <layout_panel + follows="all" + height="53" + layout="topleft" + left="0" + top="5" + width="313" + visible="true" + auto_resize="false" + name="marketplace panel" + + > + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" + name="Location" + width="290"> + Marketplace store: + </text> + <text + type="string" + length="1" + follows="left|top|right" + height="18" + layout="topleft" + left="10" + valign="center" + name="LocationTextText" + width="288"> + someplace + </text> + </layout_panel> + <layout_panel + follows="left|top|right" + height="69" + left="0" + top="0" + auto_resize="false" + width="315" + > + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" + name="ContentRating" + width="100"> + Rating: + </text> + <text + type="string" + length="1" + follows="left|top|right" + height="18" + layout="topleft" + left_pad="2" + valign="center" + name="ContentRatingText" + top_delta="-2" + width="188"> + Adult + </text> + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" + name="Owner" + width="100"> + Owner: + </text> + <text + type="string" + length="1" + follows="left|top|right" + height="18" + layout="topleft" + left_pad="2" + valign="center" + name="OwnerText" + top_delta="-2" + width="188"> + Kyle + </text> + <button + follows="bottom|left" + height="23" + label="Edit" + layout="topleft" + name="edit_btn" + top_pad="0" + width="151" + left="10"/> + <!-- + + + <expandable_text + allow_scroll="false" + bg_visible="false" + follows="left|top|right" + h_pad="0" + height="35" + width="280" + layout="topleft" + font="SansSerifBig" + font.style="BOLD" + left="10" + top_pad="10" + name="pick_name" + read_only="false" + text_color="white" + v_pad="0" + use_ellipses="true" + value="It's an experience" /> + + <text + follows="left|top" + height="16" + layout="topleft" + left="10" + top_pad="10" + value="Maturity level:" + width="130" /> + <icon + follows="top|left" + height="16" + image_name="unknown" + layout="topleft" + left_pad="10" + name="maturity_icon" + top_pad="-18" + width="18" /> + <text + follows="top|left" + height="16" + layout="topleft" + left_pad="5" + name="maturity_value" + top_pad="-14" + value="unknown" + width="118" /> + <panel + follows="left|top|right" + name="location_panel" + left="10" + width="280" + visible="false" + top_pad="10" + height="42"> + <text + follows="left|top|right" + height="16" + layout="topleft" + left="00" + top="0" + value="Location:" + width="280" /> + + <text + follows="left|top|right" + height="16" + layout="topleft" + left="0" + top_pad="10" + value="Location:" + width="280" /> + </panel> + + <panel + follows="left|top|right" + name="location_panel" + left="10" + width="280" + top_pad="10" + height="42"> + <text + follows="left|top|right" + height="16" + layout="topleft" + left="00" + top="0" + value="Location:" + width="280" /> + + <text + follows="left|top|right" + height="16" + layout="topleft" + left="0" + top_pad="10" + value="Location:" + width="280" /> + </panel> + +--> + </layout_panel> + </layout_stack> + </panel> + </scroll_container> + +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_experiences.xml b/indra/newview/skins/default/xui/en/panel_experiences.xml new file mode 100644 index 0000000000..47a3005aae --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_experiences.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + +<panel + layout="topleft" + top="3" + left="3" + right="-3" + bottom="-3" + label="Experiences" + follows="all"> + <string + name="no_experiences" + value="No experiences."/> + + <accordion + fit_parent="true" + layout="topleft" + top="0" + left="3" + right="-3" + bottom="-3" + single_expansion="true" + follows="all"> + <accordion_tab + name="tab_experiences" + layout="topleft" + top="0" + left="0" + right="-3" + title="Experiences" + follows="all"> + <flat_list_view + name="experiences_list" + layout="topleft" + top="0" + left="0" + follows="all"/> + </accordion_tab> + </accordion> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index 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..f1f8843a9d 100755 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -1,440 +1,477 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - follows="all" - height="570" - layout="topleft" - name="item properties" - help_topic="item_properties" - title="Item Profile" - width="333"> - <panel.string - name="unknown"> - (unknown) - </panel.string> - <panel.string - name="unknown_multiple"> - (unknown / multiple) - </panel.string> - <panel.string - name="public"> - (public) - </panel.string> - <panel.string - name="you_can"> - You can: - </panel.string> - <panel.string - name="owner_can"> - Owner can: - </panel.string> - <panel.string - name="acquiredDate"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] - </panel.string> - <panel.string - name="origin_inventory"> - (Inventory) - </panel.string> - <panel.string - name="origin_inworld"> - (Inworld) - </panel.string> - <icon - follows="top|right" - height="18" - image_name="Lock" - layout="topleft" - right="-15" - mouse_opaque="true" - name="IconLocked" - top="8" - width="18" /> - <button - follows="top|left" - height="24" - image_hover_unselected="BackButton_Over" - image_pressed="BackButton_Press" - image_unselected="BackButton_Off" - layout="topleft" - left="12" - name="back_btn" - tab_stop="false" - top="2" - width="30" - use_draw_context_alpha="false" /> - <text - follows="top|left|right" - font="SansSerifHugeBold" - height="26" - layout="topleft" - left_pad="3" - name="title" - text_color="LtGray" - top="2" - use_ellipses="true" - value="Item Profile" - width="275" /> - <text - follows="top|left|right" - height="13" - layout="topleft" - left="45" - name="origin" - text_color="LtGray_50" - use_ellipses="true" - value="(Inventory)" - width="275" /> - <scroll_container - color="DkGray2" - follows="all" - layout="topleft" - left="9" - name="item_profile_scroll" - opaque="true" - height="493" - width="313" - top="45"> - <panel - follows="left|top|right" - height="390" - help_topic="" - label="" - layout="topleft" - left="0" - name="item_profile" - top="0" - width="295"> - <text - type="string" - length="1" - follows="left|top" - height="10" - layout="topleft" - left="5" - name="LabelItemNameTitle" - top="10" - width="78"> - Name: - </text> - <line_editor - border_style="line" - border_thickness="1" - follows="left|top|right" - height="20" - layout="topleft" - left_delta="78" - max_length_bytes="63" - name="LabelItemName" - top_delta="0" - width="210" /> - <text - type="string" - length="1" - follows="left|top" - height="10" - layout="topleft" - left="5" - name="LabelItemDescTitle" - top_pad="10" - width="78"> - Description: - </text> - <line_editor - border_style="line" - border_thickness="1" - follows="left|top|right" - height="23" - layout="topleft" - left_delta="78" - max_length_bytes="127" - name="LabelItemDesc" - top_delta="-5" - width="210" /> - <text - type="string" - length="1" - follows="left|top" - height="23" - layout="topleft" - left="5" - name="LabelCreatorTitle" - top_pad="10" - width="78"> - Creator: - </text> - <avatar_icon - follows="top|left" - height="20" - default_icon_name="Generic_Person" - layout="topleft" - left_pad="0" - top_delta="-6" - mouse_opaque="true" - width="20" /> - <text - type="string" - follows="left|right|top" - font="SansSerifSmall" - height="15" - layout="topleft" - left_pad="5" - name="LabelCreatorName" - top_delta="6" - use_ellipses="true" - width="165"> - </text> - <button - follows="top|right" - height="16" + follows="all" + height="570" + layout="topleft" + name="item properties" + help_topic="item_properties" + title="Item Profile" + width="333"> + <panel.string + name="no_experience"> + (none) + </panel.string> + <panel.string + name="loading_experience"> + (loading) + </panel.string> + <panel.string + name="unknown"> + (unknown) + </panel.string> + <panel.string + name="unknown_multiple"> + (unknown / multiple) + </panel.string> + <panel.string + name="public"> + (public) + </panel.string> + <panel.string + name="you_can"> + You can: + </panel.string> + <panel.string + name="owner_can"> + Owner can: + </panel.string> + <panel.string + name="acquiredDate"> + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + </panel.string> + <panel.string + name="origin_inventory"> + (Inventory) + </panel.string> + <panel.string + name="origin_inworld"> + (Inworld) + </panel.string> + <icon + follows="top|right" + height="18" + image_name="Lock" + layout="topleft" + right="-15" + mouse_opaque="true" + name="IconLocked" + top="8" + width="18" /> + <button + follows="top|left" + height="24" + image_hover_unselected="BackButton_Over" + image_pressed="BackButton_Press" + image_unselected="BackButton_Off" + layout="topleft" + left="12" + name="back_btn" + tab_stop="false" + top="2" + width="30" + use_draw_context_alpha="false" /> + <text + follows="top|left|right" + font="SansSerifHugeBold" + height="26" + layout="topleft" + left_pad="3" + name="title" + text_color="LtGray" + top="2" + use_ellipses="true" + value="Item Profile" + width="275" /> + <text + follows="top|left|right" + height="13" + layout="topleft" + left="45" + name="origin" + text_color="LtGray_50" + use_ellipses="true" + value="(Inventory)" + width="275" /> + <scroll_container + color="DkGray2" + follows="all" + layout="topleft" + left="9" + name="item_profile_scroll" + opaque="true" + height="493" + width="313" + top="45"> + <panel + follows="left|top|right" + height="390" + help_topic="" + label="" + layout="topleft" + left="0" + name="item_profile" + top="0" + width="295"> + <text + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="5" + name="LabelItemNameTitle" + top="10" + width="78"> + Name: + </text> + <line_editor + border_style="line" + border_thickness="1" + follows="left|top|right" + height="20" + layout="topleft" + left_delta="78" + max_length_bytes="63" + name="LabelItemName" + top_delta="0" + width="210" /> + <text + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="5" + name="LabelItemDescTitle" + top_pad="10" + width="78"> + Description: + </text> + <line_editor + border_style="line" + border_thickness="1" + follows="left|top|right" + height="23" + layout="topleft" + left_delta="78" + max_length_bytes="127" + name="LabelItemDesc" + top_delta="-5" + width="210" /> + <text + type="string" + length="1" + follows="left|top" + height="23" + layout="topleft" + left="5" + name="LabelCreatorTitle" + top_pad="10" + width="78"> + Creator: + </text> + <avatar_icon + follows="top|left" + height="20" + default_icon_name="Generic_Person" + layout="topleft" + left_pad="0" + top_delta="-6" + mouse_opaque="true" + width="20" /> + <text + type="string" + follows="left|right|top" + font="SansSerifSmall" + height="15" + layout="topleft" + left_pad="5" + name="LabelCreatorName" + top_delta="6" + use_ellipses="true" + width="165"> + </text> + <button + follows="top|right" + height="16" + image_selected="Inspector_I" + image_unselected="Inspector_I" + layout="topleft" + right="-5" + name="BtnCreator" + top_delta="-6" + width="16" /> + <text + type="string" + length="1" + follows="left|top" + height="23" + layout="topleft" + left="5" + name="LabelOwnerTitle" + top_pad="10" + width="78"> + Owner: + </text> + <avatar_icon + follows="top|left" + height="20" + default_icon_name="Generic_Person" + layout="topleft" + left_pad="0" + top_delta="-6" + mouse_opaque="true" + width="20" /> + <text + type="string" + follows="left|right|top" + font="SansSerifSmall" + height="15" + layout="topleft" + left_pad="5" + name="LabelOwnerName" + top_delta="6" + use_ellipses="true" + width="165"> + </text> + <button + follows="top|right" + height="16" image_selected="Inspector_I" image_unselected="Inspector_I" + layout="topleft" + right="-5" + name="BtnOwner" + top_delta="-3" + width="16" /> + <text + type="string" + length="1" + follows="left|top" + height="23" + layout="topleft" + left="5" + name="LabelAcquiredTitle" + top_pad="10" + width="78"> + Acquired: + </text> + <text + type="string" + length="1" + follows="left|top|right" + height="23" + layout="topleft" + left_delta="78" + name="LabelAcquiredDate" + top_delta="0" + width="210"> + </text> + <panel + border="false" + follows="left|top|right" + layout="topleft" + mouse_opaque="false" + name="perms_inv" + left="0" + top_pad="25" + height="155" + width="313"> + <text + type="string" + length="1" + left="10" + top_pad="13" + text_color="EmphasisColor" + height="15" + follows="left|top|right" + layout="topleft" + name="perm_modify" + width="200"> + You can: + </text> + <check_box + height="18" + label="Modify" + layout="topleft" + left="20" + name="CheckOwnerModify" + top_pad="0" + width="90" /> + <check_box + height="18" + label="Copy" + layout="topleft" + left_pad="0" + name="CheckOwnerCopy" + width="90" /> + <check_box + height="18" + label="Transfer" + layout="topleft" + left_pad="0" + name="CheckOwnerTransfer" + width="106" /> + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" + name="AnyoneLabel" + top_pad="8" + width="100"> + Anyone: + </text> + <check_box + height="18" + label="Copy" layout="topleft" - right="-5" - name="BtnCreator" - top_delta="-6" - width="16" /> - <text + left_pad="0" + name="CheckEveryoneCopy" + top_delta="-2" + width="150" /> + <text type="string" length="1" follows="left|top" - height="23" + height="16" layout="topleft" - left="5" - name="LabelOwnerTitle" - top_pad="10" - width="78"> - Owner: - </text> - <avatar_icon - follows="top|left" - height="20" - default_icon_name="Generic_Person" - layout="topleft" - left_pad="0" - top_delta="-6" - mouse_opaque="true" - width="20" /> - <text + left="10" + name="GroupLabel" + top_pad="8" + width="100"> + Group: + </text> + <check_box + height="18" + label="Share" + layout="topleft" + left_pad="0" + top_delta="-2" + name="CheckShareWithGroup" + tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions." + width="150" /> + <text type="string" - follows="left|right|top" - font="SansSerifSmall" - height="15" - layout="topleft" - left_pad="5" - name="LabelOwnerName" - top_delta="6" - use_ellipses="true" - width="165"> - </text> - <button - follows="top|right" - height="16" - image_selected="Inspector_I" - image_unselected="Inspector_I" - layout="topleft" - right="-5" - name="BtnOwner" - top_delta="-3" - width="16" /> - <text - type="string" - length="1" - follows="left|top" - height="23" - layout="topleft" - left="5" - name="LabelAcquiredTitle" - top_pad="10" - width="78"> - Acquired: - </text> - <text - type="string" - length="1" - follows="left|top|right" - height="23" - layout="topleft" - left_delta="78" - name="LabelAcquiredDate" - top_delta="0" - width="210"> - </text> - <panel - border="false" - follows="left|top|right" - layout="topleft" - mouse_opaque="false" - name="perms_inv" - left="0" - top_pad="25" - height="155" - width="313"> - <text - type="string" - length="1" - left="10" - top_pad="13" - text_color="EmphasisColor" - height="15" - follows="left|top|right" - layout="topleft" - name="perm_modify" - width="200"> - You can: - </text> - <check_box - height="18" - label="Modify" - layout="topleft" - left="20" - name="CheckOwnerModify" - top_pad="0" - width="90" /> - <check_box - height="18" - label="Copy" - layout="topleft" - left_pad="0" - name="CheckOwnerCopy" - width="90" /> - <check_box - height="18" - label="Transfer" - layout="topleft" - left_pad="0" - name="CheckOwnerTransfer" - width="106" /> - <text - type="string" - length="1" - follows="left|top" - height="16" - layout="topleft" - left="10" - name="AnyoneLabel" - top_pad="8" - width="100"> - Anyone: - </text> - <check_box - height="18" - label="Copy" - layout="topleft" - left_pad="0" - name="CheckEveryoneCopy" - top_delta="-2" - width="150" /> - <text - type="string" - length="1" - follows="left|top" - height="16" - layout="topleft" - left="10" - name="GroupLabel" - top_pad="8" - width="100"> - Group: - </text> - <check_box - height="18" - label="Share" - layout="topleft" - left_pad="0" - top_delta="-2" - name="CheckShareWithGroup" - tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions." - width="150" /> - <text - type="string" - length="1" - follows="left|top" - height="16" - layout="topleft" - left="10" - name="NextOwnerLabel" - top_pad="8" - width="200" - word_wrap="true"> - Next owner: - </text> - <check_box - height="18" - label="Modify" - layout="topleft" - left="20" - top_pad="0" - name="CheckNextOwnerModify" - width="90" /> - <check_box - height="18" - label="Copy" - layout="topleft" - left_pad="0" - name="CheckNextOwnerCopy" - width="90" /> - <check_box - height="18" - label="Transfer" - layout="topleft" - left_pad="0" - name="CheckNextOwnerTransfer" - tool_tip="Next owner can give away or resell this object" - width="106" /> - </panel> + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" + name="NextOwnerLabel" + top_pad="8" + width="200" + word_wrap="true"> + Next owner: + </text> <check_box - height="18" - label="For Sale" - layout="topleft" - left="20" - name="CheckPurchase" - top_pad="20" - width="100" /> - <combo_box - height="23" - left_pad="0" - layout="topleft" - follows="left|top" - name="combobox sale copy" - width="170"> - <combo_box.item - label="Copy" - name="Copy" - value="Copy" /> - <combo_box.item - label="Original" - name="Original" - value="Original" /> - </combo_box> - <spinner - follows="left|top" - decimal_digits="0" - increment="1" - control_name="Edit Cost" - name="Edit Cost" - label="Price: L$" - label_width="75" - left="120" - width="170" - min_val="0" - height="23" - max_val="999999999" - top_pad="10"/> - </panel> - </scroll_container> - <panel - height="30" - layout="topleft" - name="button_panel" - left="5" - top_pad="0" - width="313"> - <button - height="23" - label="Cancel" - layout="topleft" - name="cancel_btn" - right="-1" - width="100" /> - </panel> - </panel> + height="18" + label="Modify" + layout="topleft" + left="20" + top_pad="0" + name="CheckNextOwnerModify" + width="90" /> + <check_box + height="18" + label="Copy" + layout="topleft" + left_pad="0" + name="CheckNextOwnerCopy" + width="90" /> + <check_box + height="18" + label="Transfer" + layout="topleft" + left_pad="0" + name="CheckNextOwnerTransfer" + tool_tip="Next owner can give away or resell this object" + width="106" /> + </panel> + <check_box + height="18" + label="For Sale" + layout="topleft" + left="20" + name="CheckPurchase" + top_pad="20" + width="100" /> + <combo_box + height="23" + left_pad="0" + layout="topleft" + follows="left|top" + name="combobox sale copy" + width="170"> + <combo_box.item + label="Copy" + name="Copy" + value="Copy" /> + <combo_box.item + label="Original" + name="Original" + value="Original" /> + </combo_box> + <spinner + follows="left|top" + decimal_digits="0" + increment="1" + control_name="Edit Cost" + name="Edit Cost" + label="Price: L$" + label_width="75" + left="120" + width="170" + min_val="0" + height="23" + max_val="999999999" + top_pad="10"/> + + <text + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="5" + name="LabelItemExperienceTitle" + top_pad="10" + width="78" + visible="false"> + Experience: + </text> + <text + type="string" + length="1" + follows="left|top|right" + height="10" + layout="topleft" + left_delta="78" + name="LabelItemExperience" + top_delta="0" + width="210" + visible="false" + ></text> + </panel> + + </scroll_container> + <panel + height="30" + layout="topleft" + name="button_panel" + left="5" + top_pad="0" + width="313" + follows="top|right|left"> + <button + follows="top|right" + height="23" + label="Cancel" + layout="topleft" + name="cancel_btn" + right="-1" + width="100" /> + </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 73601ecb9f..b02d4e5d8f 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -3920,6 +3920,10 @@ Try enclosing path to the editor with double quotes. <!-- Spell check settings floater --> <string name="UserDictionary">[User]</string> + <!-- Experience Tools strings --> + <string name="experience_tools_experience">Experience</string> + + <!-- Conversation log messages --> <string name="logging_calls_disabled_log_empty"> Conversations are not being logged. To begin keeping a log, choose "Save: Log only" or "Save: Log and transcripts" under Preferences > Chat. diff --git a/indra/tools/vstool/DispatchUtility.cs b/indra/tools/vstool/DispatchUtility.cs new file mode 100644 index 0000000000..6056ac55a1 --- /dev/null +++ b/indra/tools/vstool/DispatchUtility.cs @@ -0,0 +1,271 @@ +#region Using Directives
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Reflection;
+using System.Security.Permissions;
+
+#endregion
+
+namespace TestDispatchUtility
+{
+ /// <summary>
+ /// Provides helper methods for working with COM IDispatch objects that have a registered type library.
+ /// </summary>
+ public static class DispatchUtility
+ {
+ #region Private Constants
+
+ private const int S_OK = 0; //From WinError.h
+ private const int LOCALE_SYSTEM_DEFAULT = 2 << 10; //From WinNT.h == 2048 == 0x800
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Gets whether the specified object implements IDispatch.
+ /// </summary>
+ /// <param name="obj">An object to check.</param>
+ /// <returns>True if the object implements IDispatch. False otherwise.</returns>
+ public static bool ImplementsIDispatch(object obj)
+ {
+ bool result = obj is IDispatchInfo;
+ return result;
+ }
+
+ /// <summary>
+ /// Gets a Type that can be used with reflection.
+ /// </summary>
+ /// <param name="obj">An object that implements IDispatch.</param>
+ /// <param name="throwIfNotFound">Whether an exception should be thrown if a Type can't be obtained.</param>
+ /// <returns>A .NET Type that can be used with reflection.</returns>
+ /// <exception cref="InvalidCastException">If <paramref name="obj"/> doesn't implement IDispatch.</exception>
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static Type GetType(object obj, bool throwIfNotFound)
+ {
+ RequireReference(obj, "obj");
+ Type result = GetType((IDispatchInfo)obj, throwIfNotFound);
+ return result;
+ }
+
+ /// <summary>
+ /// Tries to get the DISPID for the requested member name.
+ /// </summary>
+ /// <param name="obj">An object that implements IDispatch.</param>
+ /// <param name="name">The name of a member to lookup.</param>
+ /// <param name="dispId">If the method returns true, this holds the DISPID on output.
+ /// If the method returns false, this value should be ignored.</param>
+ /// <returns>True if the member was found and resolved to a DISPID. False otherwise.</returns>
+ /// <exception cref="InvalidCastException">If <paramref name="obj"/> doesn't implement IDispatch.</exception>
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static bool TryGetDispId(object obj, string name, out int dispId)
+ {
+ RequireReference(obj, "obj");
+ bool result = TryGetDispId((IDispatchInfo)obj, name, out dispId);
+ return result;
+ }
+
+ /// <summary>
+ /// Invokes a member by DISPID.
+ /// </summary>
+ /// <param name="obj">An object that implements IDispatch.</param>
+ /// <param name="dispId">The DISPID of a member. This can be obtained using
+ /// <see cref="TryGetDispId(object, string, out int)"/>.</param>
+ /// <param name="args">The arguments to pass to the member.</param>
+ /// <returns>The member's return value.</returns>
+ /// <remarks>
+ /// This can invoke a method or a property get accessor.
+ /// </remarks>
+ public static object Invoke(object obj, int dispId, object[] args)
+ {
+ string memberName = "[DispId=" + dispId + "]";
+ object result = Invoke(obj, memberName, args);
+ return result;
+ }
+
+ /// <summary>
+ /// Invokes a member by name.
+ /// </summary>
+ /// <param name="obj">An object.</param>
+ /// <param name="memberName">The name of the member to invoke.</param>
+ /// <param name="args">The arguments to pass to the member.</param>
+ /// <returns>The member's return value.</returns>
+ /// <remarks>
+ /// This can invoke a method or a property get accessor.
+ /// </remarks>
+ public static object Invoke(object obj, string memberName, object[] args)
+ {
+ RequireReference(obj, "obj");
+ Type type = obj.GetType();
+ object result = type.InvokeMember(memberName, BindingFlags.InvokeMethod | BindingFlags.GetProperty,
+ null, obj, args, null);
+ return result;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /// <summary>
+ /// Requires that the value is non-null.
+ /// </summary>
+ /// <typeparam name="T">The type of the value.</typeparam>
+ /// <param name="value">The value to check.</param>
+ /// <param name="name">The name of the value.</param>
+ private static void RequireReference<T>(T value, string name) where T : class
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(name);
+ }
+ }
+
+ /// <summary>
+ /// Gets a Type that can be used with reflection.
+ /// </summary>
+ /// <param name="dispatch">An object that implements IDispatch.</param>
+ /// <param name="throwIfNotFound">Whether an exception should be thrown if a Type can't be obtained.</param>
+ /// <returns>A .NET Type that can be used with reflection.</returns>
+ private static Type GetType(IDispatchInfo dispatch, bool throwIfNotFound)
+ {
+ RequireReference(dispatch, "dispatch");
+
+ Type result = null;
+ int typeInfoCount;
+ int hr = dispatch.GetTypeInfoCount(out typeInfoCount);
+ if (hr == S_OK && typeInfoCount > 0)
+ {
+ // Type info isn't usually culture-aware for IDispatch, so we might as well pass
+ // the default locale instead of looking up the current thread's LCID each time
+ // (via CultureInfo.CurrentCulture.LCID).
+ dispatch.GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, out result);
+ }
+
+ if (result == null && throwIfNotFound)
+ {
+ // If the GetTypeInfoCount called failed, throw an exception for that.
+ Marshal.ThrowExceptionForHR(hr);
+
+ // Otherwise, throw the same exception that Type.GetType would throw.
+ throw new TypeLoadException();
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Tries to get the DISPID for the requested member name.
+ /// </summary>
+ /// <param name="dispatch">An object that implements IDispatch.</param>
+ /// <param name="name">The name of a member to lookup.</param>
+ /// <param name="dispId">If the method returns true, this holds the DISPID on output.
+ /// If the method returns false, this value should be ignored.</param>
+ /// <returns>True if the member was found and resolved to a DISPID. False otherwise.</returns>
+ private static bool TryGetDispId(IDispatchInfo dispatch, string name, out int dispId)
+ {
+ RequireReference(dispatch, "dispatch");
+ RequireReference(name, "name");
+
+ bool result = false;
+
+ // Members names aren't usually culture-aware for IDispatch, so we might as well
+ // pass the default locale instead of looking up the current thread's LCID each time
+ // (via CultureInfo.CurrentCulture.LCID).
+ Guid iidNull = Guid.Empty;
+ int hr = dispatch.GetDispId(ref iidNull, ref name, 1, LOCALE_SYSTEM_DEFAULT, out dispId);
+
+ const int DISP_E_UNKNOWNNAME = unchecked((int)0x80020006); //From WinError.h
+ const int DISPID_UNKNOWN = -1; //From OAIdl.idl
+ if (hr == S_OK)
+ {
+ result = true;
+ }
+ else if (hr == DISP_E_UNKNOWNNAME && dispId == DISPID_UNKNOWN)
+ {
+ // This is the only supported "error" case because it means IDispatch
+ // is saying it doesn't know the member we asked about.
+ result = false;
+ }
+ else
+ {
+ // The other documented result codes are all errors.
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region Private Interfaces
+
+ /// <summary>
+ /// A partial declaration of IDispatch used to lookup Type information and DISPIDs.
+ /// </summary>
+ /// <remarks>
+ /// This interface only declares the first three methods of IDispatch. It omits the
+ /// fourth method (Invoke) because there are already plenty of ways to do dynamic
+ /// invocation in .NET. But the first three methods provide dynamic type metadata
+ /// discovery, which .NET doesn't provide normally if you have a System.__ComObject
+ /// RCW instead of a strongly-typed RCW.
+ /// <para/>
+ /// Note: The original declaration of IDispatch is in OAIdl.idl.
+ /// </remarks>
+ [ComImport]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ [Guid("00020400-0000-0000-C000-000000000046")]
+ private interface IDispatchInfo
+ {
+ /// <summary>
+ /// Gets the number of Types that the object provides (0 or 1).
+ /// </summary>
+ /// <param name="typeInfoCount">Returns 0 or 1 for the number of Types provided by <see cref="GetTypeInfo"/>.</param>
+ /// <remarks>
+ /// http://msdn.microsoft.com/en-us/library/da876d53-cb8a-465c-a43e-c0eb272e2a12(VS.85)
+ /// </remarks>
+ [PreserveSig]
+ int GetTypeInfoCount(out int typeInfoCount);
+
+ /// <summary>
+ /// Gets the Type information for an object if <see cref="GetTypeInfoCount"/> returned 1.
+ /// </summary>
+ /// <param name="typeInfoIndex">Must be 0.</param>
+ /// <param name="lcid">Typically, LOCALE_SYSTEM_DEFAULT (2048).</param>
+ /// <param name="typeInfo">Returns the object's Type information.</param>
+ /// <remarks>
+ /// http://msdn.microsoft.com/en-us/library/cc1ec9aa-6c40-4e70-819c-a7c6dd6b8c99(VS.85)
+ /// </remarks>
+ void GetTypeInfo(int typeInfoIndex, int lcid, [MarshalAs(UnmanagedType.CustomMarshaler,
+ MarshalTypeRef = typeof(System.Runtime.InteropServices.CustomMarshalers.TypeToTypeInfoMarshaler))] out Type typeInfo);
+
+ /// <summary>
+ /// Gets the DISPID of the specified member name.
+ /// </summary>
+ /// <param name="riid">Must be IID_NULL. Pass a copy of Guid.Empty.</param>
+ /// <param name="name">The name of the member to look up.</param>
+ /// <param name="nameCount">Must be 1.</param>
+ /// <param name="lcid">Typically, LOCALE_SYSTEM_DEFAULT (2048).</param>
+ /// <param name="dispId">If a member with the requested <paramref name="name"/>
+ /// is found, this returns its DISPID and the method's return value is 0.
+ /// If the method returns a non-zero value, then this parameter's output value is
+ /// undefined.</param>
+ /// <returns>Zero for success. Non-zero for failure.</returns>
+ /// <remarks>
+ /// http://msdn.microsoft.com/en-us/library/6f6cf233-3481-436e-8d6a-51f93bf91619(VS.85)
+ /// </remarks>
+ [PreserveSig]
+ int GetDispId(ref Guid riid, ref string name, int nameCount, int lcid, out int dispId);
+
+ // NOTE: The real IDispatch also has an Invoke method next, but we don't need it.
+ // We can invoke methods using .NET's Type.InvokeMember method with the special
+ // [DISPID=n] syntax for member "names", or we can get a .NET Type using GetTypeInfo
+ // and invoke methods on that through reflection.
+ // Type.InvokeMember: http://msdn.microsoft.com/en-us/library/de3dhzwy.aspx
+ }
+
+ #endregion
+ }
+}
diff --git a/indra/tools/vstool/app.config b/indra/tools/vstool/app.config new file mode 100644 index 0000000000..8494f728ff --- /dev/null +++ b/indra/tools/vstool/app.config @@ -0,0 +1,3 @@ +<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
|