summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/llassettype.cpp86
-rw-r--r--indra/llcommon/llassettype.h3
-rw-r--r--indra/llcommon/llversionserver.h2
-rw-r--r--indra/llcommon/llversionviewer.h2
-rw-r--r--indra/llmessage/llassetstorage.cpp6
-rw-r--r--indra/llmessage/lltransfersourceasset.cpp49
-rw-r--r--indra/llmessage/lltransfersourceasset.h20
-rw-r--r--indra/llplugin/CMakeLists.txt17
-rw-r--r--indra/llplugin/llplugincookiestore.cpp75
-rw-r--r--indra/llplugin/llplugincookiestore.h9
-rw-r--r--indra/llplugin/tests/llplugincookiestore_test.cpp211
-rw-r--r--indra/llwindow/llwindowsdl.cpp3
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/English.lproj/InfoPlist.strings4
-rw-r--r--indra/newview/Info-SecondLife.plist2
-rw-r--r--indra/newview/app_settings/settings.xml2
-rw-r--r--indra/newview/llagentwearables.cpp535
-rw-r--r--indra/newview/llagentwearablesfetch.cpp516
-rw-r--r--indra/newview/llagentwearablesfetch.h118
-rw-r--r--indra/newview/llfloateravatartextures.cpp92
-rw-r--r--indra/newview/llfolderviewitem.cpp292
-rw-r--r--indra/newview/llinventorybridge.cpp50
-rw-r--r--indra/newview/llinventoryfunctions.cpp41
-rw-r--r--indra/newview/llstartup.cpp7
-rw-r--r--indra/newview/llviewermedia.cpp99
-rw-r--r--indra/newview/llviewermedia.h8
-rw-r--r--indra/newview/llviewermenu.cpp7
-rw-r--r--indra/newview/llviewermessage.cpp2
-rw-r--r--indra/newview/res-sdl/toolbuy.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/toolopen.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/toolsit.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res/viewerRes.rc8
-rw-r--r--indra/newview/skins/default/xui/de/panel_status_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_tools.xml14
-rw-r--r--indra/newview/skins/default/xui/es/floater_tools.xml12
35 files changed, 1390 insertions, 906 deletions
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index 6d5b12d840..1c664e093b 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -45,12 +45,16 @@ struct AssetEntry : public LLDictionaryEntry
AssetEntry(const char *desc_name,
const char *type_name, // 8 character limit!
const char *human_name, // for decoding to human readable form; put any and as many printable characters you want in each one
- bool can_link) // can you create a link to this type?
+ bool can_link, // can you create a link to this type?
+ bool can_fetch, // can you fetch this asset by ID?
+ bool can_know) // can you see this asset's ID?
:
LLDictionaryEntry(desc_name),
mTypeName(type_name),
mHumanName(human_name),
- mCanLink(can_link)
+ mCanLink(can_link),
+ mCanFetch(can_fetch),
+ mCanKnow(can_know)
{
llassert(strlen(mTypeName) <= 8);
}
@@ -58,6 +62,8 @@ struct AssetEntry : public LLDictionaryEntry
const char *mTypeName;
const char *mHumanName;
bool mCanLink;
+ bool mCanFetch;
+ bool mCanKnow;
};
class LLAssetDictionary : public LLSingleton<LLAssetDictionary>,
@@ -69,32 +75,32 @@ public:
LLAssetDictionary::LLAssetDictionary()
{
- // DESCRIPTION TYPE NAME HUMAN NAME CAN LINK?
- // |--------------------|-----------|-------------------|-----------|
- addEntry(LLAssetType::AT_TEXTURE, new AssetEntry("TEXTURE", "texture", "texture", FALSE));
- addEntry(LLAssetType::AT_SOUND, new AssetEntry("SOUND", "sound", "sound", FALSE));
- addEntry(LLAssetType::AT_CALLINGCARD, new AssetEntry("CALLINGCARD", "callcard", "calling card", FALSE));
- addEntry(LLAssetType::AT_LANDMARK, new AssetEntry("LANDMARK", "landmark", "landmark", FALSE));
- addEntry(LLAssetType::AT_SCRIPT, new AssetEntry("SCRIPT", "script", "legacy script", FALSE));
- addEntry(LLAssetType::AT_CLOTHING, new AssetEntry("CLOTHING", "clothing", "clothing", TRUE));
- addEntry(LLAssetType::AT_OBJECT, new AssetEntry("OBJECT", "object", "object", TRUE));
- addEntry(LLAssetType::AT_NOTECARD, new AssetEntry("NOTECARD", "notecard", "note card", FALSE));
- addEntry(LLAssetType::AT_CATEGORY, new AssetEntry("CATEGORY", "category", "folder", TRUE));
- addEntry(LLAssetType::AT_LSL_TEXT, new AssetEntry("LSL_TEXT", "lsltext", "lsl2 script", FALSE));
- addEntry(LLAssetType::AT_LSL_BYTECODE, new AssetEntry("LSL_BYTECODE", "lslbyte", "lsl bytecode", FALSE));
- addEntry(LLAssetType::AT_TEXTURE_TGA, new AssetEntry("TEXTURE_TGA", "txtr_tga", "tga texture", FALSE));
- addEntry(LLAssetType::AT_BODYPART, new AssetEntry("BODYPART", "bodypart", "body part", TRUE));
- addEntry(LLAssetType::AT_SOUND_WAV, new AssetEntry("SOUND_WAV", "snd_wav", "sound", FALSE));
- addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", FALSE));
- addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", FALSE));
- addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", FALSE));
- addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", TRUE));
- addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", FALSE));
-
- addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "symbolic link", FALSE));
- addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "symbolic folder link", FALSE));
-
- addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE));
+ // DESCRIPTION TYPE NAME HUMAN NAME CAN LINK? CAN FETCH? CAN KNOW?
+ // |--------------------|-----------|-------------------|-----------|-----------|---------|
+ addEntry(LLAssetType::AT_TEXTURE, new AssetEntry("TEXTURE", "texture", "texture", false, false, true));
+ addEntry(LLAssetType::AT_SOUND, new AssetEntry("SOUND", "sound", "sound", false, true, true));
+ addEntry(LLAssetType::AT_CALLINGCARD, new AssetEntry("CALLINGCARD", "callcard", "calling card", false, false, false));
+ addEntry(LLAssetType::AT_LANDMARK, new AssetEntry("LANDMARK", "landmark", "landmark", false, true, true));
+ addEntry(LLAssetType::AT_SCRIPT, new AssetEntry("SCRIPT", "script", "legacy script", false, false, false));
+ addEntry(LLAssetType::AT_CLOTHING, new AssetEntry("CLOTHING", "clothing", "clothing", true, true, true));
+ addEntry(LLAssetType::AT_OBJECT, new AssetEntry("OBJECT", "object", "object", true, false, false));
+ addEntry(LLAssetType::AT_NOTECARD, new AssetEntry("NOTECARD", "notecard", "note card", false, false, true));
+ addEntry(LLAssetType::AT_CATEGORY, new AssetEntry("CATEGORY", "category", "folder", true, false, false));
+ addEntry(LLAssetType::AT_LSL_TEXT, new AssetEntry("LSL_TEXT", "lsltext", "lsl2 script", false, false, false));
+ addEntry(LLAssetType::AT_LSL_BYTECODE, new AssetEntry("LSL_BYTECODE", "lslbyte", "lsl bytecode", false, false, false));
+ addEntry(LLAssetType::AT_TEXTURE_TGA, new AssetEntry("TEXTURE_TGA", "txtr_tga", "tga texture", false, false, false));
+ addEntry(LLAssetType::AT_BODYPART, new AssetEntry("BODYPART", "bodypart", "body part", true, true, true));
+ addEntry(LLAssetType::AT_SOUND_WAV, new AssetEntry("SOUND_WAV", "snd_wav", "sound", false, false, false));
+ addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", false, false, false));
+ addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", false, false, false));
+ addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", false, true, true));
+ addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", true, true, true));
+ addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", false, false, false));
+
+ addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "sym link", false, false, true));
+ addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "sym folder link", false, false, true));
+
+ addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, false, false, false));
};
// static
@@ -225,3 +231,27 @@ const std::string &LLAssetType::badLookup()
return sBadLookup;
}
+
+// static
+bool LLAssetType::lookupIsAssetFetchByIDAllowed(EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mCanFetch;
+ }
+ return false;
+}
+
+// static
+bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mCanKnow;
+ }
+ return false;
+}
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index c7bbc2e74a..2c2dc27aaa 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -146,6 +146,9 @@ public:
static bool lookupCanLink(EType asset_type);
static bool lookupIsLinkType(EType asset_type);
+ static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download
+ static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer
+
static const std::string& badLookup(); // error string when a lookup fails
protected:
diff --git a/indra/llcommon/llversionserver.h b/indra/llcommon/llversionserver.h
index 0f1e59a18c..e3663544db 100644
--- a/indra/llcommon/llversionserver.h
+++ b/indra/llcommon/llversionserver.h
@@ -36,7 +36,7 @@
const S32 LL_VERSION_MAJOR = 1;
const S32 LL_VERSION_MINOR = 31;
const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 200030;
+const S32 LL_VERSION_BUILD = 203110;
const char * const LL_CHANNEL = "Second Life Server";
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 540aea4252..3ab4257fab 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -36,7 +36,7 @@
const S32 LL_VERSION_MAJOR = 2;
const S32 LL_VERSION_MINOR = 0;
const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 200030;
+const S32 LL_VERSION_BUILD = 203110;
const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
index 0ab1081200..02523467e8 100644
--- a/indra/llmessage/llassetstorage.cpp
+++ b/indra/llmessage/llassetstorage.cpp
@@ -408,6 +408,8 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, vo
{
lldebugs << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << llendl;
+ llinfos << "ASSET_TRACE requesting " << uuid << " type " << LLAssetType::lookup(type) << llendl;
+
if (mShutDown)
{
return; // don't get the asset or do any callbacks, we are shutting down
@@ -471,6 +473,8 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, vo
// we've already got the file
// theoretically, partial files w/o a pending request shouldn't happen
// unless there's a weird error
+ llinfos << "ASSET_TRACE asset " << uuid << " found in VFS" << llendl;
+
if (callback)
{
callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED);
@@ -528,6 +532,8 @@ void LLAssetStorage::downloadCompleteCallback(
LLAssetType::EType file_type,
void* user_data, LLExtStat ext_status)
{
+ llinfos << "ASSET_TRACE asset " << file_id << " downloadCompleteCallback" << llendl;
+
lldebugs << "LLAssetStorage::downloadCompleteCallback() for " << file_id
<< "," << LLAssetType::lookup(file_type) << llendl;
LLAssetRequest* req = (LLAssetRequest*)user_data;
diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp
index abfb432020..43f7c07e94 100644
--- a/indra/llmessage/lltransfersourceasset.cpp
+++ b/indra/llmessage/lltransfersourceasset.cpp
@@ -60,7 +60,7 @@ void LLTransferSourceAsset::initTransfer()
// to the simulator. This is subset of assets we allow to be
// simply pulled straight from the asset system.
LLUUID* tidp;
- if(is_asset_fetch_by_id_allowed(mParams.getAssetType()))
+ if(LLAssetType::lookupIsAssetFetchByIDAllowed(mParams.getAssetType()))
{
tidp = new LLUUID(getID());
gAssetStorage->getAssetData(
@@ -257,50 +257,3 @@ BOOL LLTransferSourceParamsAsset::unpackParams(LLDataPacker &dp)
return TRUE;
}
-
-/**
- * Helper functions
- */
-bool is_asset_fetch_by_id_allowed(LLAssetType::EType type)
-{
- // *FIX: Make this list smaller.
- bool rv = false;
- switch(type)
- {
- case LLAssetType::AT_SOUND:
- case LLAssetType::AT_LANDMARK:
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_BODYPART:
- case LLAssetType::AT_ANIMATION:
- case LLAssetType::AT_GESTURE:
- rv = true;
- break;
- default:
- break;
- }
- return rv;
-}
-
-bool is_asset_id_knowable(LLAssetType::EType type)
-{
- // *FIX: Make this list smaller.
- bool rv = false;
- switch(type)
- {
- case LLAssetType::AT_TEXTURE:
- case LLAssetType::AT_SOUND:
- case LLAssetType::AT_LANDMARK:
- case LLAssetType::AT_CLOTHING:
- case LLAssetType::AT_NOTECARD:
- case LLAssetType::AT_BODYPART:
- case LLAssetType::AT_ANIMATION:
- case LLAssetType::AT_GESTURE:
- case LLAssetType::AT_LINK:
- case LLAssetType::AT_LINK_FOLDER:
- rv = true;
- break;
- default:
- break;
- }
- return rv;
-}
diff --git a/indra/llmessage/lltransfersourceasset.h b/indra/llmessage/lltransfersourceasset.h
index 70b09b6aaf..8616595654 100644
--- a/indra/llmessage/lltransfersourceasset.h
+++ b/indra/llmessage/lltransfersourceasset.h
@@ -84,24 +84,4 @@ protected:
S32 mCurPos;
};
-/**
- * @brief Quick check to see if the asset allows direct download.
- *
- * This might not be the right place for this function call, but it
- * originally started life inside the LLTransferSourceAsset code.
- * @param type The type of asset.
- * @return Returns true if the asset can be fetched by id.
- */
-bool is_asset_fetch_by_id_allowed(LLAssetType::EType type);
-
-/**
- * @brief Quick check to see if all asset data can be known by the viewer.
- *
- * This might not be the right place for this function call, but it
- * originally started life inside the LLTransferSourceAsset code.
- * @param type The type of asset.
- * @return Returns true if the asset id can be transmitted to the viewer.
- */
-bool is_asset_id_knowable(LLAssetType::EType type);
-
#endif // LL_LLTRANSFERSOURCEASSET_H
diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt
index def9fcbeae..441becbae0 100644
--- a/indra/llplugin/CMakeLists.txt
+++ b/indra/llplugin/CMakeLists.txt
@@ -3,6 +3,7 @@
project(llplugin)
include(00-Common)
+include(CURL)
include(LLCommon)
include(LLImage)
include(LLMath)
@@ -55,3 +56,19 @@ list(APPEND llplugin_SOURCE_FILES ${llplugin_HEADER_FILES})
add_library (llplugin ${llplugin_SOURCE_FILES})
add_subdirectory(slplugin)
+
+# Add tests
+include(LLAddBuildTest)
+# UNIT TESTS
+SET(llplugin_TEST_SOURCE_FILES
+ llplugincookiestore.cpp
+ )
+
+# llplugincookiestore has a dependency on curl, so we need to link the curl library into the test.
+set_source_files_properties(
+ llplugincookiestore.cpp
+ PROPERTIES
+ LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES}"
+ )
+
+LL_ADD_PROJECT_UNIT_TESTS(llplugin "${llplugin_TEST_SOURCE_FILES}")
diff --git a/indra/llplugin/llplugincookiestore.cpp b/indra/llplugin/llplugincookiestore.cpp
index 1964b8d789..85b1e70d78 100644
--- a/indra/llplugin/llplugincookiestore.cpp
+++ b/indra/llplugin/llplugincookiestore.cpp
@@ -53,7 +53,7 @@ LLPluginCookieStore::~LLPluginCookieStore()
LLPluginCookieStore::Cookie::Cookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end):
- mCookie(s, cookie_start, cookie_end),
+ mCookie(s, cookie_start, cookie_end - cookie_start),
mNameStart(0), mNameEnd(0),
mValueStart(0), mValueEnd(0),
mDomainStart(0), mDomainEnd(0),
@@ -62,11 +62,11 @@ LLPluginCookieStore::Cookie::Cookie(const std::string &s, std::string::size_type
{
}
-LLPluginCookieStore::Cookie *LLPluginCookieStore::Cookie::createFromString(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end)
+LLPluginCookieStore::Cookie *LLPluginCookieStore::Cookie::createFromString(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, const std::string &host)
{
Cookie *result = new Cookie(s, cookie_start, cookie_end);
- if(!result->parse())
+ if(!result->parse(host))
{
delete result;
result = NULL;
@@ -92,7 +92,7 @@ std::string LLPluginCookieStore::Cookie::getKey() const
return result;
}
-bool LLPluginCookieStore::Cookie::parse()
+bool LLPluginCookieStore::Cookie::parse(const std::string &host)
{
bool first_field = true;
@@ -248,7 +248,50 @@ bool LLPluginCookieStore::Cookie::parse()
// The cookie MUST have a name
if(mNameEnd <= mNameStart)
return false;
+
+ // If the cookie doesn't have a domain, add the current host as the domain.
+ if(mDomainEnd <= mDomainStart)
+ {
+ if(host.empty())
+ {
+ // no domain and no current host -- this is a parse failure.
+ return false;
+ }
+
+ // Figure out whether this cookie ended with a ";" or not...
+ std::string::size_type last_char = mCookie.find_last_not_of(" ");
+ if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
+ {
+ mCookie += ";";
+ }
+
+ mCookie += " domain=";
+ mDomainStart = mCookie.size();
+ mCookie += host;
+ mDomainEnd = mCookie.size();
+
+ lldebugs << "added domain (" << mDomainStart << " to " << mDomainEnd << "), new cookie is: " << mCookie << llendl;
+ }
+
+ // If the cookie doesn't have a path, add "/".
+ if(mPathEnd <= mPathStart)
+ {
+ // Figure out whether this cookie ended with a ";" or not...
+ std::string::size_type last_char = mCookie.find_last_not_of(" ");
+ if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
+ {
+ mCookie += ";";
+ }
+
+ mCookie += " path=";
+ mPathStart = mCookie.size();
+ mCookie += "/";
+ mPathEnd = mCookie.size();
+ lldebugs << "added path (" << mPathStart << " to " << mPathEnd << "), new cookie is: " << mCookie << llendl;
+ }
+
+
return true;
}
@@ -409,13 +452,29 @@ void LLPluginCookieStore::setCookies(const std::string &cookies, bool mark_chang
while(start != std::string::npos)
{
- std::string::size_type end = cookies.find('\n', start);
+ std::string::size_type end = cookies.find_first_of("\r\n", start);
if(end > start)
{
// The line is non-empty. Try to create a cookie from it.
setOneCookie(cookies, start, end, mark_changed);
}
- start = cookies.find_first_not_of("\n ", end);
+ start = cookies.find_first_not_of("\r\n ", end);
+ }
+}
+
+void LLPluginCookieStore::setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed)
+{
+ std::string::size_type start = 0;
+
+ while(start != std::string::npos)
+ {
+ std::string::size_type end = cookies.find_first_of("\r\n", start);
+ if(end > start)
+ {
+ // The line is non-empty. Try to create a cookie from it.
+ setOneCookie(cookies, start, end, mark_changed, host);
+ }
+ start = cookies.find_first_not_of("\r\n ", end);
}
}
@@ -502,9 +561,9 @@ std::string LLPluginCookieStore::unquoteString(const std::string &s)
// When deleting with mark_changed set to true, this replaces the existing cookie in the list with an entry that's marked both dead and changed.
// Some time later when writeChangedCookies() is called with clear_changed set to true, the dead cookie is deleted from the list after being returned, so that the
// delete operation (in the form of the expired cookie) is passed along.
-void LLPluginCookieStore::setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed)
+void LLPluginCookieStore::setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed, const std::string &host)
{
- Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end);
+ Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end, host);
if(cookie)
{
lldebugs << "setting cookie: " << cookie->getCookie() << llendl;
diff --git a/indra/llplugin/llplugincookiestore.h b/indra/llplugin/llplugincookiestore.h
index 5250f008b6..a93f0c14f0 100644
--- a/indra/llplugin/llplugincookiestore.h
+++ b/indra/llplugin/llplugincookiestore.h
@@ -66,18 +66,21 @@ public:
void setCookies(const std::string &cookies, bool mark_changed = true);
void readCookies(std::istream& s, bool mark_changed = true);
+ // sets one or more cookies (without reinitializing anything), supplying a hostname the cookies came from -- use when setting a cookie manually
+ void setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed = true);
+
// quote or unquote a string as per the definition of 'quoted-string' in rfc2616
static std::string quoteString(const std::string &s);
static std::string unquoteString(const std::string &s);
private:
- void setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed);
+ void setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed, const std::string &host = LLStringUtil::null);
class Cookie
{
public:
- static Cookie *createFromString(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos);
+ static Cookie *createFromString(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos, const std::string &host = LLStringUtil::null);
// Construct a string from the cookie that uniquely represents it, to be used as a key in a std::map.
std::string getKey() const;
@@ -95,7 +98,7 @@ private:
private:
Cookie(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos);
- bool parse();
+ bool parse(const std::string &host);
std::string::size_type findFieldEnd(std::string::size_type start = 0, std::string::size_type end = std::string::npos);
bool matchName(std::string::size_type start, std::string::size_type end, const char *name);
diff --git a/indra/llplugin/tests/llplugincookiestore_test.cpp b/indra/llplugin/tests/llplugincookiestore_test.cpp
new file mode 100644
index 0000000000..020d9c1977
--- /dev/null
+++ b/indra/llplugin/tests/llplugincookiestore_test.cpp
@@ -0,0 +1,211 @@
+/**
+ * @file llplugincookiestore_test.cpp
+ * @brief Unit tests for LLPluginCookieStore.
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ * @endcond
+ */
+
+#include "linden_common.h"
+#include "../test/lltut.h"
+
+#include "../llplugincookiestore.h"
+
+
+namespace tut
+{
+ // Main Setup
+ struct LLPluginCookieStoreFixture
+ {
+ LLPluginCookieStoreFixture()
+ {
+ // We need dates definitively in the past and the future to properly test cookie expiration.
+ LLDate now = LLDate::now();
+ LLDate past(now.secondsSinceEpoch() - (60.0 * 60.0 * 24.0)); // 1 day in the past
+ LLDate future(now.secondsSinceEpoch() + (60.0 * 60.0 * 24.0)); // 1 day in the future
+
+ mPastString = past.asRFC1123();
+ mFutureString = future.asRFC1123();
+ }
+
+ std::string mPastString;
+ std::string mFutureString;
+ LLPluginCookieStore mCookieStore;
+
+ // List of cookies used for validation
+ std::list<std::string> mCookies;
+
+ // This sets up mCookies from a string returned by one of the functions in LLPluginCookieStore
+ void setCookies(const std::string &cookies)
+ {
+ mCookies.clear();
+ std::string::size_type start = 0;
+
+ while(start != std::string::npos)
+ {
+ std::string::size_type end = cookies.find_first_of("\r\n", start);
+ if(end > start)
+ {
+ std::string line(cookies, start, end - start);
+ if(line.find_first_not_of("\r\n\t ") != std::string::npos)
+ {
+ // The line has some non-whitespace characters. Save it to the list.
+ mCookies.push_back(std::string(cookies, start, end - start));
+ }
+ }
+ start = cookies.find_first_not_of("\r\n ", end);
+ }
+ }
+
+ // This ensures that a cookie matching the one passed is in the list.
+ void ensureCookie(const std::string &cookie)
+ {
+ std::list<std::string>::iterator iter;
+ for(iter = mCookies.begin(); iter != mCookies.end(); iter++)
+ {
+ if(*iter == cookie)
+ {
+ // Found the cookie
+ // TODO: this should do a smarter equality comparison on the two cookies, instead of just a string compare.
+ return;
+ }
+ }
+
+ // Didn't find this cookie
+ std::string message = "cookie not found: ";
+ message += cookie;
+ ensure(message, false);
+ }
+
+ // This ensures that the number of cookies in the list matches what's expected.
+ void ensureSize(const std::string &message, size_t size)
+ {
+ if(mCookies.size() != size)
+ {
+ std::stringstream full_message;
+
+ full_message << message << " (expected " << size << ", actual " << mCookies.size() << ")";
+ ensure(full_message.str(), false);
+ }
+ }
+ };
+
+ typedef test_group<LLPluginCookieStoreFixture> factory;
+ typedef factory::object object;
+ factory tf("LLPluginCookieStore test");
+
+ // Tests
+ template<> template<>
+ void object::test<1>()
+ {
+ // Test 1: cookie uniqueness and update lists.
+ // Valid, distinct cookies:
+
+ std::string cookie01 = "cookieA=value; domain=example.com; path=/";
+ std::string cookie02 = "cookieB=value; domain=example.com; path=/"; // different name
+ std::string cookie03 = "cookieA=value; domain=foo.example.com; path=/"; // different domain
+ std::string cookie04 = "cookieA=value; domain=example.com; path=/bar/"; // different path
+ std::string cookie05 = "cookieC; domain=example.com; path=/"; // empty value
+ std::string cookie06 = "cookieD=value; domain=example.com; path=/; expires="; // different name, persistent cookie
+ cookie06 += mFutureString;
+
+ mCookieStore.setCookies(cookie01);
+ mCookieStore.setCookies(cookie02);
+ mCookieStore.setCookies(cookie03);
+ mCookieStore.setCookies(cookie04);
+ mCookieStore.setCookies(cookie05);
+ mCookieStore.setCookies(cookie06);
+
+ // Invalid cookies (these will get parse errors and not be added to the store)
+
+ std::string badcookie01 = "cookieD=value; domain=example.com; path=/; foo=bar"; // invalid field name
+ std::string badcookie02 = "cookieE=value; path=/"; // no domain
+
+ mCookieStore.setCookies(badcookie01);
+ mCookieStore.setCookies(badcookie02);
+
+ // All cookies added so far should have been marked as "changed"
+ setCookies(mCookieStore.getChangedCookies());
+ ensureSize("count of changed cookies", 6);
+ ensureCookie(cookie01);
+ ensureCookie(cookie02);
+ ensureCookie(cookie03);
+ ensureCookie(cookie04);
+ ensureCookie(cookie05);
+ ensureCookie(cookie06);
+
+ // Save off the current state of the cookie store (we'll restore it later)
+ std::string savedCookies = mCookieStore.getAllCookies();
+
+ // Test replacing cookies
+ std::string cookie01a = "cookieA=newvalue; domain=example.com; path=/"; // updated value
+ std::string cookie02a = "cookieB=newvalue; domain=example.com; path=/; expires="; // remove cookie (by setting an expire date in the past)
+ cookie02a += mPastString;
+
+ mCookieStore.setCookies(cookie01a);
+ mCookieStore.setCookies(cookie02a);
+
+ // test for getting changed cookies
+ setCookies(mCookieStore.getChangedCookies());
+ ensureSize("count of updated cookies", 2);
+ ensureCookie(cookie01a);
+ ensureCookie(cookie02a);
+
+ // and for the state of the store after getting changed cookies
+ setCookies(mCookieStore.getAllCookies());
+ ensureSize("count of valid cookies", 5);
+ ensureCookie(cookie01a);
+ ensureCookie(cookie03);
+ ensureCookie(cookie04);
+ ensureCookie(cookie05);
+ ensureCookie(cookie06);
+
+ // Check that only the persistent cookie is returned here
+ setCookies(mCookieStore.getPersistentCookies());
+ ensureSize("count of persistent cookies", 1);
+ ensureCookie(cookie06);
+
+ // Restore the cookie store to a previous state and verify
+ mCookieStore.setAllCookies(savedCookies);
+
+ // Since setAllCookies defaults to not marking cookies as changed, this list should be empty.
+ setCookies(mCookieStore.getChangedCookies());
+ ensureSize("count of changed cookies after restore", 0);
+
+ // Verify that the restore worked as it should have.
+ setCookies(mCookieStore.getAllCookies());
+ ensureSize("count of restored cookies", 6);
+ ensureCookie(cookie01);
+ ensureCookie(cookie02);
+ ensureCookie(cookie03);
+ ensureCookie(cookie04);
+ ensureCookie(cookie05);
+ ensureCookie(cookie06);
+ }
+
+}
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index 4d25b4134e..399d284402 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -2074,6 +2074,9 @@ void LLWindowSDL::initCursors()
mSDLCursors[UI_CURSOR_TOOLPAUSE] = makeSDLCursorFromBMP("toolpause.BMP",0,0);
mSDLCursors[UI_CURSOR_TOOLMEDIAOPEN] = makeSDLCursorFromBMP("toolmediaopen.BMP",0,0);
mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
+ mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",20,15);
+ mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",20,15);
+ mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",20,15);
if (getenv("LL_ATI_MOUSE_CURSOR_BUG") != NULL) {
llinfos << "Disabling cursor updating due to LL_ATI_MOUSE_CURSOR_BUG" << llendl;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 5c7e9c8db2..bc838e20e4 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -74,6 +74,7 @@ set(viewer_SOURCE_FILES
llagentpilot.cpp
llagentui.cpp
llagentwearables.cpp
+ llagentwearablesfetch.cpp
llanimstatelabels.cpp
llappearancemgr.cpp
llappviewer.cpp
@@ -574,6 +575,7 @@ set(viewer_HEADER_FILES
llagentpilot.h
llagentui.h
llagentwearables.h
+ llagentwearablesfetch.h
llanimstatelabels.h
llappearance.h
llappearancemgr.h
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
index 879408d6e4..02c3dfc6e0 100644
--- a/indra/newview/English.lproj/InfoPlist.strings
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -2,6 +2,6 @@
CFBundleName = "Second Life";
-CFBundleShortVersionString = "Second Life version 2.0.0.200030";
-CFBundleGetInfoString = "Second Life version 2.0.0.200030, Copyright 2004-2009 Linden Research, Inc.";
+CFBundleShortVersionString = "Second Life version 2.0.0.203110";
+CFBundleGetInfoString = "Second Life version 2.0.0.203110, Copyright 2004-2009 Linden Research, Inc.";
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index 38ebb22b84..4cb01a0f33 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
- <string>2.0.0.200030</string>
+ <string>2.0.0.203110</string>
<key>CSResourcesFileMapped</key>
<true/>
</dict>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 73fb24e4eb..92226f4148 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -4609,7 +4609,7 @@
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
- <integer>1</integer>
+ <integer>0</integer>
</map>
<key>MediaShowOutsideParcel</key>
<map>
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 6e983747b2..0542e73bfd 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -36,6 +36,7 @@
#include "llaccordionctrltab.h"
#include "llagent.h"
#include "llagentcamera.h"
+#include "llagentwearablesfetch.h"
#include "llappearancemgr.h"
#include "llcallbacklist.h"
#include "llfloatercustomize.h"
@@ -57,77 +58,6 @@
#include <boost/scoped_ptr.hpp>
-//--------------------------------------------------------------------
-// Classes for fetching initial wearables data
-//--------------------------------------------------------------------
-// Outfit folder fetching callback structure.
-class LLInitialWearablesFetch : public LLInventoryFetchDescendentsObserver
-{
-public:
- LLInitialWearablesFetch() {}
- ~LLInitialWearablesFetch();
- virtual void done();
-
- struct InitialWearableData
- {
- EWearableType mType;
- LLUUID mItemID;
- LLUUID mAssetID;
- InitialWearableData(EWearableType type, LLUUID& itemID, LLUUID& assetID) :
- mType(type),
- mItemID(itemID),
- mAssetID(assetID)
- {}
- };
-
- typedef std::vector<InitialWearableData> initial_wearable_data_vec_t;
- initial_wearable_data_vec_t mCOFInitialWearables; // Wearables from the Current Outfit Folder
- initial_wearable_data_vec_t mAgentInitialWearables; // Wearables from the old agent wearables msg
-
-protected:
- void processWearablesMessage();
- void processContents();
-};
-
-class LLLibraryOutfitsFetch : public LLInventoryFetchDescendentsObserver
-{
-public:
- enum ELibraryOutfitFetchStep {
- LOFS_FOLDER = 0,
- LOFS_OUTFITS,
- LOFS_LIBRARY,
- LOFS_IMPORTED,
- LOFS_CONTENTS
- };
- LLLibraryOutfitsFetch() : mCurrFetchStep(LOFS_FOLDER), mOutfitsPopulated(false)
- {
- mMyOutfitsID = LLUUID::null;
- mClothingID = LLUUID::null;
- mLibraryClothingID = LLUUID::null;
- mImportedClothingID = LLUUID::null;
- mImportedClothingName = "Imported Library Clothing";
- }
- ~LLLibraryOutfitsFetch() {}
- virtual void done();
- void doneIdle();
- LLUUID mMyOutfitsID;
- void importedFolderFetch();
-protected:
- void folderDone(void);
- void outfitsDone(void);
- void libraryDone(void);
- void importedFolderDone(void);
- void contentsDone(void);
- enum ELibraryOutfitFetchStep mCurrFetchStep;
- uuid_vec_t mLibraryClothingFolders;
- uuid_vec_t mImportedClothingFolders;
- bool mOutfitsPopulated;
- LLUUID mClothingID;
- LLUUID mLibraryClothingID;
- LLUUID mImportedClothingID;
- std::string mImportedClothingName;
-};
-
LLAgentWearables gAgentWearables;
BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE;
@@ -1014,8 +944,7 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs
// Store initial wearables data until we know whether we have the current outfit folder or need to use the data.
LLInitialWearablesFetch::InitialWearableData wearable_data(type, item_id, asset_id); // MULTI-WEARABLE: update
- outfit->mAgentInitialWearables.push_back(wearable_data);
-
+ outfit->add(wearable_data);
}
lldebugs << " " << LLWearableDictionary::getTypeLabel(type) << llendl;
@@ -2147,463 +2076,3 @@ void LLAgentWearables::populateMyOutfitsFolder(void)
outfits->done();
}
}
-
-void LLLibraryOutfitsFetch::done()
-{
- // Delay this until idle() routine, since it's a heavy operation and
- // we also can't have it run within notifyObservers.
- doOnIdle(boost::bind(&LLLibraryOutfitsFetch::doneIdle,this));
- gInventory.removeObserver(this); // Prevent doOnIdle from being added twice.
-}
-
-void LLLibraryOutfitsFetch::doneIdle()
-{
- gInventory.addObserver(this); // Add this back in since it was taken out during ::done()
-
- switch (mCurrFetchStep)
- {
- case LOFS_FOLDER:
- folderDone();
- mCurrFetchStep = LOFS_OUTFITS;
- break;
- case LOFS_OUTFITS:
- outfitsDone();
- mCurrFetchStep = LOFS_LIBRARY;
- break;
- case LOFS_LIBRARY:
- libraryDone();
- mCurrFetchStep = LOFS_IMPORTED;
- break;
- case LOFS_IMPORTED:
- importedFolderDone();
- mCurrFetchStep = LOFS_CONTENTS;
- break;
- case LOFS_CONTENTS:
- contentsDone();
- break;
- default:
- llwarns << "Got invalid state for outfit fetch: " << mCurrFetchStep << llendl;
- mOutfitsPopulated = TRUE;
- break;
- }
-
- // We're completely done. Cleanup.
- if (mOutfitsPopulated)
- {
- gInventory.removeObserver(this);
- delete this;
- return;
- }
-}
-
-void LLLibraryOutfitsFetch::folderDone(void)
-{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t wearable_array;
- gInventory.collectDescendents(mMyOutfitsID, cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH);
- // Early out if we already have items in My Outfits.
- if (cat_array.count() > 0 || wearable_array.count() > 0)
- {
- mOutfitsPopulated = true;
- return;
- }
-
- mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING);
- mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true);
-
- // If Library->Clothing->Initial Outfits exists, use that.
- LLNameCategoryCollector matchFolderFunctor("Initial Outfits");
- gInventory.collectDescendentsIf(mLibraryClothingID,
- cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH,
- matchFolderFunctor);
- if (cat_array.count() > 0)
- {
- const LLViewerInventoryCategory *cat = cat_array.get(0);
- mLibraryClothingID = cat->getUUID();
- }
-
- mCompleteFolders.clear();
-
- // Get the complete information on the items in the inventory.
- uuid_vec_t folders;
- folders.push_back(mClothingID);
- folders.push_back(mLibraryClothingID);
- fetchDescendents(folders);
- if (isEverythingComplete())
- {
- done();
- }
-}
-
-void LLLibraryOutfitsFetch::outfitsDone(void)
-{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t wearable_array;
- uuid_vec_t folders;
-
- // Collect the contents of the Library's Clothing folder
- gInventory.collectDescendents(mLibraryClothingID, cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH);
-
- llassert(cat_array.count() > 0);
- for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
- iter != cat_array.end();
- ++iter)
- {
- const LLViewerInventoryCategory *cat = iter->get();
-
- // Get the names and id's of every outfit in the library, skip "Ruth"
- // because it's a low quality legacy outfit
- if (cat->getName() != "Ruth")
- {
- // Get the name of every outfit in the library
- folders.push_back(cat->getUUID());
- mLibraryClothingFolders.push_back(cat->getUUID());
- }
- }
- cat_array.clear();
- wearable_array.clear();
-
- // Check if you already have an "Imported Library Clothing" folder
- LLNameCategoryCollector matchFolderFunctor(mImportedClothingName);
- gInventory.collectDescendentsIf(mClothingID,
- cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH,
- matchFolderFunctor);
- if (cat_array.size() > 0)
- {
- const LLViewerInventoryCategory *cat = cat_array.get(0);
- mImportedClothingID = cat->getUUID();
- }
-
- mCompleteFolders.clear();
-
- fetchDescendents(folders);
- if (isEverythingComplete())
- {
- done();
- }
-}
-
-class LLLibraryOutfitsCopyDone: public LLInventoryCallback
-{
-public:
- LLLibraryOutfitsCopyDone(LLLibraryOutfitsFetch * fetcher):
- mFireCount(0), mLibraryOutfitsFetcher(fetcher)
- {
- }
-
- virtual ~LLLibraryOutfitsCopyDone()
- {
- if (!LLApp::isExiting() && mLibraryOutfitsFetcher)
- {
- gInventory.addObserver(mLibraryOutfitsFetcher);
- mLibraryOutfitsFetcher->done();
- }
- }
-
- /* virtual */ void fire(const LLUUID& inv_item)
- {
- mFireCount++;
- }
-private:
- U32 mFireCount;
- LLLibraryOutfitsFetch * mLibraryOutfitsFetcher;
-};
-
-// Copy the clothing folders from the library into the imported clothing folder
-void LLLibraryOutfitsFetch::libraryDone(void)
-{
- if (mImportedClothingID != LLUUID::null)
- {
- // Skip straight to fetching the contents of the imported folder
- importedFolderFetch();
- return;
- }
-
- // Remove observer; next autopopulation step will be triggered externally by LLLibraryOutfitsCopyDone.
- gInventory.removeObserver(this);
-
- LLPointer<LLInventoryCallback> copy_waiter = new LLLibraryOutfitsCopyDone(this);
- mImportedClothingID = gInventory.createNewCategory(mClothingID,
- LLFolderType::FT_NONE,
- mImportedClothingName);
- // Copy each folder from library into clothing unless it already exists.
- for (uuid_vec_t::const_iterator iter = mLibraryClothingFolders.begin();
- iter != mLibraryClothingFolders.end();
- ++iter)
- {
- const LLUUID& src_folder_id = (*iter); // Library clothing folder ID
- const LLViewerInventoryCategory *cat = gInventory.getCategory(src_folder_id);
- if (!cat)
- {
- llwarns << "Library folder import for uuid:" << src_folder_id << " failed to find folder." << llendl;
- continue;
- }
-
- if (!LLAppearanceMgr::getInstance()->getCanMakeFolderIntoOutfit(src_folder_id))
- {
- llinfos << "Skipping non-outfit folder name:" << cat->getName() << llendl;
- continue;
- }
-
- // Don't copy the category if it already exists.
- LLNameCategoryCollector matchFolderFunctor(cat->getName());
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t wearable_array;
- gInventory.collectDescendentsIf(mImportedClothingID,
- cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH,
- matchFolderFunctor);
- if (cat_array.size() > 0)
- {
- continue;
- }
-
- LLUUID dst_folder_id = gInventory.createNewCategory(mImportedClothingID,
- LLFolderType::FT_NONE,
- cat->getName());
- LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_folder_id, dst_folder_id, copy_waiter);
- }
-}
-
-void LLLibraryOutfitsFetch::importedFolderFetch(void)
-{
- // Fetch the contents of the Imported Clothing Folder
- uuid_vec_t folders;
- folders.push_back(mImportedClothingID);
-
- mCompleteFolders.clear();
-
- fetchDescendents(folders);
- if (isEverythingComplete())
- {
- done();
- }
-}
-
-void LLLibraryOutfitsFetch::importedFolderDone(void)
-{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t wearable_array;
- uuid_vec_t folders;
-
- // Collect the contents of the Imported Clothing folder
- gInventory.collectDescendents(mImportedClothingID, cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH);
-
- for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
- iter != cat_array.end();
- ++iter)
- {
- const LLViewerInventoryCategory *cat = iter->get();
-
- // Get the name of every imported outfit
- folders.push_back(cat->getUUID());
- mImportedClothingFolders.push_back(cat->getUUID());
- }
-
- mCompleteFolders.clear();
- fetchDescendents(folders);
- if (isEverythingComplete())
- {
- done();
- }
-}
-
-void LLLibraryOutfitsFetch::contentsDone(void)
-{
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t wearable_array;
-
- for (uuid_vec_t::const_iterator folder_iter = mImportedClothingFolders.begin();
- folder_iter != mImportedClothingFolders.end();
- ++folder_iter)
- {
- const LLUUID &folder_id = (*folder_iter);
- const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id);
- if (!cat)
- {
- llwarns << "Library folder import for uuid:" << folder_id << " failed to find folder." << llendl;
- continue;
- }
-
- // First, make a folder in the My Outfits directory.
- LLUUID new_outfit_folder_id = gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, cat->getName());
-
- cat_array.clear();
- wearable_array.clear();
- // Collect the contents of each imported clothing folder, so we can create new outfit links for it
- gInventory.collectDescendents(folder_id, cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH);
-
- for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin();
- wearable_iter != wearable_array.end();
- ++wearable_iter)
- {
- const LLViewerInventoryItem *item = wearable_iter->get();
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- new_outfit_folder_id,
- item->getName(),
- LLAssetType::AT_LINK,
- NULL);
- }
- }
-
- mOutfitsPopulated = true;
-}
-
-//--------------------------------------------------------------------
-// InitialWearablesFetch
-//
-// This grabs contents from the COF and processes them.
-// The processing is handled in idle(), i.e. outside of done(),
-// to avoid gInventory.notifyObservers recursion.
-//--------------------------------------------------------------------
-
-LLInitialWearablesFetch::~LLInitialWearablesFetch()
-{
-}
-
-// virtual
-void LLInitialWearablesFetch::done()
-{
- // Delay processing the actual results of this so it's not handled within
- // gInventory.notifyObservers. The results will be handled in the next
- // idle tick instead.
- gInventory.removeObserver(this);
- doOnIdle(boost::bind(&LLInitialWearablesFetch::processContents,this));
-}
-
-void LLInitialWearablesFetch::processContents()
-{
- // Fetch the wearable items from the Current Outfit Folder
- LLInventoryModel::cat_array_t cat_array;
- LLInventoryModel::item_array_t wearable_array;
- LLFindWearables is_wearable;
- gInventory.collectDescendentsIf(mCompleteFolders.front(), cat_array, wearable_array,
- LLInventoryModel::EXCLUDE_TRASH, is_wearable);
-
- LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
- if (wearable_array.count() > 0)
- {
- LLAppearanceMgr::instance().updateAppearanceFromCOF();
- }
- else
- {
- // if we're constructing the COF from the wearables message, we don't have a proper outfit link
- LLAppearanceMgr::instance().setOutfitDirty(true);
- processWearablesMessage();
- }
- delete this;
-}
-
-class LLFetchAndLinkObserver: public LLInventoryFetchObserver
-{
-public:
- LLFetchAndLinkObserver(LLInventoryFetchObserver::item_ref_t& ids):
- m_ids(ids),
- LLInventoryFetchObserver(true) // retry for missing items
- {
- }
- ~LLFetchAndLinkObserver()
- {
- }
- virtual void done()
- {
- gInventory.removeObserver(this);
-
- // Link to all fetched items in COF.
- LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;
- for (LLInventoryFetchObserver::item_ref_t::iterator it = m_ids.begin();
- it != m_ids.end();
- ++it)
- {
- LLUUID id = *it;
- LLViewerInventoryItem *item = gInventory.getItem(*it);
- if (!item)
- {
- llwarns << "fetch failed!" << llendl;
- continue;
- }
-
- link_inventory_item(gAgent.getID(),
- item->getLinkedUUID(),
- LLAppearanceMgr::instance().getCOF(),
- item->getName(),
- LLAssetType::AT_LINK,
- link_waiter);
- }
- }
-private:
- LLInventoryFetchObserver::item_ref_t m_ids;
-};
-
-void LLInitialWearablesFetch::processWearablesMessage()
-{
- if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead.
- {
- const LLUUID current_outfit_id = LLAppearanceMgr::instance().getCOF();
- LLInventoryFetchObserver::item_ref_t ids;
- for (U8 i = 0; i < mAgentInitialWearables.size(); ++i)
- {
- // Populate the current outfit folder with links to the wearables passed in the message
- InitialWearableData *wearable_data = new InitialWearableData(mAgentInitialWearables[i]); // This will be deleted in the callback.
-
- if (wearable_data->mAssetID.notNull())
- {
- ids.push_back(wearable_data->mItemID);
- }
- else
- {
- llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID "
- << wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl;
- delete wearable_data;
- }
- }
-
- // Add all current attachments to the requested items as well.
- if (isAgentAvatarValid())
- {
- for (LLVOAvatar::attachment_map_t::const_iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
- iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter)
- {
- LLViewerJointAttachment* attachment = iter->second;
- if (!attachment) continue;
- for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
- attachment_iter != attachment->mAttachedObjects.end();
- ++attachment_iter)
- {
- LLViewerObject* attached_object = (*attachment_iter);
- if (!attached_object) continue;
- const LLUUID& item_id = attached_object->getItemID();
- if (item_id.isNull()) continue;
- ids.push_back(item_id);
- }
- }
- }
-
- // Need to fetch the inventory items for ids, then create links to them after they arrive.
- LLFetchAndLinkObserver *fetcher = new LLFetchAndLinkObserver(ids);
- fetcher->fetchItems(ids);
- // If no items to be fetched, done will never be triggered.
- // TODO: Change LLInventoryFetchObserver::fetchItems to trigger done() on this condition.
- if (fetcher->isEverythingComplete())
- {
- fetcher->done();
- }
- else
- {
- gInventory.addObserver(fetcher);
- }
- }
- else
- {
- LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL;
- }
-}
-
-
diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp
new file mode 100644
index 0000000000..45274a8e2c
--- /dev/null
+++ b/indra/newview/llagentwearablesfetch.cpp
@@ -0,0 +1,516 @@
+/**
+ * @file llagentwearablesfetch.cpp
+ * @brief LLAgentWearblesFetch class implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llagentwearablesfetch.h"
+
+#include "llagent.h"
+#include "llagentwearables.h"
+#include "llappearancemgr.h"
+#include "llinventoryfunctions.h"
+#include "llvoavatarself.h"
+
+LLInitialWearablesFetch::LLInitialWearablesFetch()
+{
+}
+
+LLInitialWearablesFetch::~LLInitialWearablesFetch()
+{
+}
+
+// virtual
+void LLInitialWearablesFetch::done()
+{
+ // Delay processing the actual results of this so it's not handled within
+ // gInventory.notifyObservers. The results will be handled in the next
+ // idle tick instead.
+ gInventory.removeObserver(this);
+ doOnIdle(boost::bind(&LLInitialWearablesFetch::processContents,this));
+}
+
+void LLInitialWearablesFetch::add(InitialWearableData &data)
+
+{
+ mAgentInitialWearables.push_back(data);
+}
+
+void LLInitialWearablesFetch::processContents()
+{
+ // Fetch the wearable items from the Current Outfit Folder
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t wearable_array;
+ LLFindWearables is_wearable;
+ gInventory.collectDescendentsIf(mCompleteFolders.front(), cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH, is_wearable);
+
+ LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
+ if (wearable_array.count() > 0)
+ {
+ LLAppearanceMgr::instance().updateAppearanceFromCOF();
+ }
+ else
+ {
+ // if we're constructing the COF from the wearables message, we don't have a proper outfit link
+ LLAppearanceMgr::instance().setOutfitDirty(true);
+ processWearablesMessage();
+ }
+ delete this;
+}
+
+class LLFetchAndLinkObserver: public LLInventoryFetchObserver
+{
+public:
+ LLFetchAndLinkObserver(LLInventoryFetchObserver::item_ref_t& ids):
+ m_ids(ids),
+ LLInventoryFetchObserver(true) // retry for missing items
+ {
+ }
+ ~LLFetchAndLinkObserver()
+ {
+ }
+ virtual void done()
+ {
+ gInventory.removeObserver(this);
+
+ // Link to all fetched items in COF.
+ LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;
+ for (LLInventoryFetchObserver::item_ref_t::iterator it = m_ids.begin();
+ it != m_ids.end();
+ ++it)
+ {
+ LLUUID id = *it;
+ LLViewerInventoryItem *item = gInventory.getItem(*it);
+ if (!item)
+ {
+ llwarns << "fetch failed!" << llendl;
+ continue;
+ }
+
+ link_inventory_item(gAgent.getID(),
+ item->getLinkedUUID(),
+ LLAppearanceMgr::instance().getCOF(),
+ item->getName(),
+ LLAssetType::AT_LINK,
+ link_waiter);
+ }
+ }
+private:
+ LLInventoryFetchObserver::item_ref_t m_ids;
+};
+
+void LLInitialWearablesFetch::processWearablesMessage()
+{
+ if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead.
+ {
+ const LLUUID current_outfit_id = LLAppearanceMgr::instance().getCOF();
+ LLInventoryFetchObserver::item_ref_t ids;
+ for (U8 i = 0; i < mAgentInitialWearables.size(); ++i)
+ {
+ // Populate the current outfit folder with links to the wearables passed in the message
+ InitialWearableData *wearable_data = new InitialWearableData(mAgentInitialWearables[i]); // This will be deleted in the callback.
+
+ if (wearable_data->mAssetID.notNull())
+ {
+ ids.push_back(wearable_data->mItemID);
+ }
+ else
+ {
+ llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID "
+ << wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl;
+ delete wearable_data;
+ }
+ }
+
+ // Add all current attachments to the requested items as well.
+ if (isAgentAvatarValid())
+ {
+ for (LLVOAvatar::attachment_map_t::const_iterator iter = gAgentAvatarp->mAttachmentPoints.begin();
+ iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter)
+ {
+ LLViewerJointAttachment* attachment = iter->second;
+ if (!attachment) continue;
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+ attachment_iter != attachment->mAttachedObjects.end();
+ ++attachment_iter)
+ {
+ LLViewerObject* attached_object = (*attachment_iter);
+ if (!attached_object) continue;
+ const LLUUID& item_id = attached_object->getItemID();
+ if (item_id.isNull()) continue;
+ ids.push_back(item_id);
+ }
+ }
+ }
+
+ // Need to fetch the inventory items for ids, then create links to them after they arrive.
+ LLFetchAndLinkObserver *fetcher = new LLFetchAndLinkObserver(ids);
+ fetcher->fetchItems(ids);
+ // If no items to be fetched, done will never be triggered.
+ // TODO: Change LLInventoryFetchObserver::fetchItems to trigger done() on this condition.
+ if (fetcher->isEverythingComplete())
+ {
+ fetcher->done();
+ }
+ else
+ {
+ gInventory.addObserver(fetcher);
+ }
+ }
+ else
+ {
+ LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL;
+ }
+}
+
+LLLibraryOutfitsFetch::LLLibraryOutfitsFetch() :
+ mCurrFetchStep(LOFS_FOLDER),
+ mOutfitsPopulated(false)
+{
+ mMyOutfitsID = LLUUID::null;
+ mClothingID = LLUUID::null;
+ mLibraryClothingID = LLUUID::null;
+ mImportedClothingID = LLUUID::null;
+ mImportedClothingName = "Imported Library Clothing";
+}
+
+LLLibraryOutfitsFetch::~LLLibraryOutfitsFetch()
+{
+}
+
+void LLLibraryOutfitsFetch::done()
+{
+ // Delay this until idle() routine, since it's a heavy operation and
+ // we also can't have it run within notifyObservers.
+ doOnIdle(boost::bind(&LLLibraryOutfitsFetch::doneIdle,this));
+ gInventory.removeObserver(this); // Prevent doOnIdle from being added twice.
+}
+
+void LLLibraryOutfitsFetch::doneIdle()
+{
+ gInventory.addObserver(this); // Add this back in since it was taken out during ::done()
+
+ switch (mCurrFetchStep)
+ {
+ case LOFS_FOLDER:
+ folderDone();
+ mCurrFetchStep = LOFS_OUTFITS;
+ break;
+ case LOFS_OUTFITS:
+ outfitsDone();
+ mCurrFetchStep = LOFS_LIBRARY;
+ break;
+ case LOFS_LIBRARY:
+ libraryDone();
+ mCurrFetchStep = LOFS_IMPORTED;
+ break;
+ case LOFS_IMPORTED:
+ importedFolderDone();
+ mCurrFetchStep = LOFS_CONTENTS;
+ break;
+ case LOFS_CONTENTS:
+ contentsDone();
+ break;
+ default:
+ llwarns << "Got invalid state for outfit fetch: " << mCurrFetchStep << llendl;
+ mOutfitsPopulated = TRUE;
+ break;
+ }
+
+ // We're completely done. Cleanup.
+ if (mOutfitsPopulated)
+ {
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+}
+
+void LLLibraryOutfitsFetch::folderDone(void)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t wearable_array;
+ gInventory.collectDescendents(mMyOutfitsID, cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ // Early out if we already have items in My Outfits.
+ if (cat_array.count() > 0 || wearable_array.count() > 0)
+ {
+ mOutfitsPopulated = true;
+ return;
+ }
+
+ mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING);
+ mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true);
+
+ // If Library->Clothing->Initial Outfits exists, use that.
+ LLNameCategoryCollector matchFolderFunctor("Initial Outfits");
+ gInventory.collectDescendentsIf(mLibraryClothingID,
+ cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ matchFolderFunctor);
+ if (cat_array.count() > 0)
+ {
+ const LLViewerInventoryCategory *cat = cat_array.get(0);
+ mLibraryClothingID = cat->getUUID();
+ }
+
+ mCompleteFolders.clear();
+
+ // Get the complete information on the items in the inventory.
+ uuid_vec_t folders;
+ folders.push_back(mClothingID);
+ folders.push_back(mLibraryClothingID);
+ fetchDescendents(folders);
+ if (isEverythingComplete())
+ {
+ done();
+ }
+}
+
+void LLLibraryOutfitsFetch::outfitsDone(void)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t wearable_array;
+ uuid_vec_t folders;
+
+ // Collect the contents of the Library's Clothing folder
+ gInventory.collectDescendents(mLibraryClothingID, cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+
+ llassert(cat_array.count() > 0);
+ for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
+ iter != cat_array.end();
+ ++iter)
+ {
+ const LLViewerInventoryCategory *cat = iter->get();
+
+ // Get the names and id's of every outfit in the library, skip "Ruth"
+ // because it's a low quality legacy outfit
+ if (cat->getName() != "Ruth")
+ {
+ // Get the name of every outfit in the library
+ folders.push_back(cat->getUUID());
+ mLibraryClothingFolders.push_back(cat->getUUID());
+ }
+ }
+ cat_array.clear();
+ wearable_array.clear();
+
+ // Check if you already have an "Imported Library Clothing" folder
+ LLNameCategoryCollector matchFolderFunctor(mImportedClothingName);
+ gInventory.collectDescendentsIf(mClothingID,
+ cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ matchFolderFunctor);
+ if (cat_array.size() > 0)
+ {
+ const LLViewerInventoryCategory *cat = cat_array.get(0);
+ mImportedClothingID = cat->getUUID();
+ }
+
+ mCompleteFolders.clear();
+
+ fetchDescendents(folders);
+ if (isEverythingComplete())
+ {
+ done();
+ }
+}
+
+class LLLibraryOutfitsCopyDone: public LLInventoryCallback
+{
+public:
+ LLLibraryOutfitsCopyDone(LLLibraryOutfitsFetch * fetcher):
+ mFireCount(0), mLibraryOutfitsFetcher(fetcher)
+ {
+ }
+
+ virtual ~LLLibraryOutfitsCopyDone()
+ {
+ if (!LLApp::isExiting() && mLibraryOutfitsFetcher)
+ {
+ gInventory.addObserver(mLibraryOutfitsFetcher);
+ mLibraryOutfitsFetcher->done();
+ }
+ }
+
+ /* virtual */ void fire(const LLUUID& inv_item)
+ {
+ mFireCount++;
+ }
+private:
+ U32 mFireCount;
+ LLLibraryOutfitsFetch * mLibraryOutfitsFetcher;
+};
+
+// Copy the clothing folders from the library into the imported clothing folder
+void LLLibraryOutfitsFetch::libraryDone(void)
+{
+ if (mImportedClothingID != LLUUID::null)
+ {
+ // Skip straight to fetching the contents of the imported folder
+ importedFolderFetch();
+ return;
+ }
+
+ // Remove observer; next autopopulation step will be triggered externally by LLLibraryOutfitsCopyDone.
+ gInventory.removeObserver(this);
+
+ LLPointer<LLInventoryCallback> copy_waiter = new LLLibraryOutfitsCopyDone(this);
+ mImportedClothingID = gInventory.createNewCategory(mClothingID,
+ LLFolderType::FT_NONE,
+ mImportedClothingName);
+ // Copy each folder from library into clothing unless it already exists.
+ for (uuid_vec_t::const_iterator iter = mLibraryClothingFolders.begin();
+ iter != mLibraryClothingFolders.end();
+ ++iter)
+ {
+ const LLUUID& src_folder_id = (*iter); // Library clothing folder ID
+ const LLViewerInventoryCategory *cat = gInventory.getCategory(src_folder_id);
+ if (!cat)
+ {
+ llwarns << "Library folder import for uuid:" << src_folder_id << " failed to find folder." << llendl;
+ continue;
+ }
+
+ if (!LLAppearanceMgr::getInstance()->getCanMakeFolderIntoOutfit(src_folder_id))
+ {
+ llinfos << "Skipping non-outfit folder name:" << cat->getName() << llendl;
+ continue;
+ }
+
+ // Don't copy the category if it already exists.
+ LLNameCategoryCollector matchFolderFunctor(cat->getName());
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t wearable_array;
+ gInventory.collectDescendentsIf(mImportedClothingID,
+ cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ matchFolderFunctor);
+ if (cat_array.size() > 0)
+ {
+ continue;
+ }
+
+ LLUUID dst_folder_id = gInventory.createNewCategory(mImportedClothingID,
+ LLFolderType::FT_NONE,
+ cat->getName());
+ LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_folder_id, dst_folder_id, copy_waiter);
+ }
+}
+
+void LLLibraryOutfitsFetch::importedFolderFetch(void)
+{
+ // Fetch the contents of the Imported Clothing Folder
+ uuid_vec_t folders;
+ folders.push_back(mImportedClothingID);
+
+ mCompleteFolders.clear();
+
+ fetchDescendents(folders);
+ if (isEverythingComplete())
+ {
+ done();
+ }
+}
+
+void LLLibraryOutfitsFetch::importedFolderDone(void)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t wearable_array;
+ uuid_vec_t folders;
+
+ // Collect the contents of the Imported Clothing folder
+ gInventory.collectDescendents(mImportedClothingID, cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+
+ for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
+ iter != cat_array.end();
+ ++iter)
+ {
+ const LLViewerInventoryCategory *cat = iter->get();
+
+ // Get the name of every imported outfit
+ folders.push_back(cat->getUUID());
+ mImportedClothingFolders.push_back(cat->getUUID());
+ }
+
+ mCompleteFolders.clear();
+ fetchDescendents(folders);
+ if (isEverythingComplete())
+ {
+ done();
+ }
+}
+
+void LLLibraryOutfitsFetch::contentsDone(void)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t wearable_array;
+
+ for (uuid_vec_t::const_iterator folder_iter = mImportedClothingFolders.begin();
+ folder_iter != mImportedClothingFolders.end();
+ ++folder_iter)
+ {
+ const LLUUID &folder_id = (*folder_iter);
+ const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id);
+ if (!cat)
+ {
+ llwarns << "Library folder import for uuid:" << folder_id << " failed to find folder." << llendl;
+ continue;
+ }
+
+ // First, make a folder in the My Outfits directory.
+ LLUUID new_outfit_folder_id = gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, cat->getName());
+
+ cat_array.clear();
+ wearable_array.clear();
+ // Collect the contents of each imported clothing folder, so we can create new outfit links for it
+ gInventory.collectDescendents(folder_id, cat_array, wearable_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+
+ for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin();
+ wearable_iter != wearable_array.end();
+ ++wearable_iter)
+ {
+ const LLViewerInventoryItem *item = wearable_iter->get();
+ link_inventory_item(gAgent.getID(),
+ item->getLinkedUUID(),
+ new_outfit_folder_id,
+ item->getName(),
+ LLAssetType::AT_LINK,
+ NULL);
+ }
+ }
+
+ mOutfitsPopulated = true;
+}
+
diff --git a/indra/newview/llagentwearablesfetch.h b/indra/newview/llagentwearablesfetch.h
new file mode 100644
index 0000000000..72063114b8
--- /dev/null
+++ b/indra/newview/llagentwearablesfetch.h
@@ -0,0 +1,118 @@
+/**
+ * @file llagentwearablesinitialfetch.h
+ * @brief LLAgentWearablesInitialFetch class header file
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAGENTWEARABLESINITIALFETCH_H
+#define LL_LLAGENTWEARABLESINITIALFETCH_H
+
+#include "llinventoryobserver.h"
+#include "llwearabledictionary.h"
+#include "lluuid.h"
+
+//--------------------------------------------------------------------
+// InitialWearablesFetch
+//
+// This grabs contents from the COF and processes them.
+// The processing is handled in idle(), i.e. outside of done(),
+// to avoid gInventory.notifyObservers recursion.
+//--------------------------------------------------------------------
+class LLInitialWearablesFetch : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLInitialWearablesFetch();
+ ~LLInitialWearablesFetch();
+ virtual void done();
+
+ struct InitialWearableData
+ {
+ EWearableType mType;
+ LLUUID mItemID;
+ LLUUID mAssetID;
+ InitialWearableData(EWearableType type, LLUUID& itemID, LLUUID& assetID) :
+ mType(type),
+ mItemID(itemID),
+ mAssetID(assetID)
+ {}
+ };
+
+ void add(InitialWearableData &data);
+
+protected:
+ void processWearablesMessage();
+ void processContents();
+
+private:
+ typedef std::vector<InitialWearableData> initial_wearable_data_vec_t;
+ initial_wearable_data_vec_t mAgentInitialWearables; // Wearables from the old agent wearables msg
+};
+
+//--------------------------------------------------------------------
+// InitialWearablesFetch
+//
+// This grabs outfits from the Library and copies those over to the user's
+// outfits folder, typically during first-ever login.
+//--------------------------------------------------------------------
+class LLLibraryOutfitsFetch : public LLInventoryFetchDescendentsObserver
+{
+public:
+ enum ELibraryOutfitFetchStep
+ {
+ LOFS_FOLDER = 0,
+ LOFS_OUTFITS,
+ LOFS_LIBRARY,
+ LOFS_IMPORTED,
+ LOFS_CONTENTS
+ };
+
+ LLLibraryOutfitsFetch();
+ ~LLLibraryOutfitsFetch();
+
+ virtual void done();
+ void doneIdle();
+ LLUUID mMyOutfitsID;
+ void importedFolderFetch();
+protected:
+ void folderDone(void);
+ void outfitsDone(void);
+ void libraryDone(void);
+ void importedFolderDone(void);
+ void contentsDone(void);
+ enum ELibraryOutfitFetchStep mCurrFetchStep;
+ uuid_vec_t mLibraryClothingFolders;
+ uuid_vec_t mImportedClothingFolders;
+ bool mOutfitsPopulated;
+ LLUUID mClothingID;
+ LLUUID mLibraryClothingID;
+ LLUUID mImportedClothingID;
+ std::string mImportedClothingName;
+};
+
+#endif // LL_AGENTWEARABLESINITIALFETCH_H
diff --git a/indra/newview/llfloateravatartextures.cpp b/indra/newview/llfloateravatartextures.cpp
index 8c7899af3e..18db60705b 100644
--- a/indra/newview/llfloateravatartextures.cpp
+++ b/indra/newview/llfloateravatartextures.cpp
@@ -31,15 +31,14 @@
*/
#include "llviewerprecompiledheaders.h"
-
#include "llfloateravatartextures.h"
+#include "llagent.h"
+#include "llagentwearables.h"
#include "lltexturectrl.h"
-
#include "lluictrlfactory.h"
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
-#include "llagentwearables.h"
using namespace LLVOAvatarDefines;
@@ -75,7 +74,6 @@ void LLFloaterAvatarTextures::draw()
LLFloater::draw();
}
-#if !LL_RELEASE_FOR_DOWNLOAD
static void update_texture_ctrl(LLVOAvatar* avatarp,
LLTextureCtrl* ctrl,
ETextureIndex te)
@@ -132,72 +130,68 @@ static LLVOAvatar* find_avatar(const LLUUID& id)
void LLFloaterAvatarTextures::refresh()
{
- LLVOAvatar *avatarp = find_avatar(mID);
- if (avatarp)
+ if (gAgent.isGodlike())
{
- std::string fullname;
- if (gCacheName->getFullName(avatarp->getID(), fullname))
+ LLVOAvatar *avatarp = find_avatar(mID);
+ if (avatarp)
{
- setTitle(mTitle + ": " + fullname);
+ std::string fullname;
+ if (gCacheName->getFullName(avatarp->getID(), fullname))
+ {
+ setTitle(mTitle + ": " + fullname);
+ }
+ for (U32 i=0; i < TEX_NUM_INDICES; i++)
+ {
+ update_texture_ctrl(avatarp, mTextures[i], ETextureIndex(i));
+ }
}
- for (U32 i=0; i < TEX_NUM_INDICES; i++)
+ else
{
- update_texture_ctrl(avatarp, mTextures[i], ETextureIndex(i));
+ setTitle(mTitle + ": " + getString("InvalidAvatar") + " (" + mID.asString() + ")");
}
}
- else
- {
- setTitle(mTitle + ": " + getString("InvalidAvatar") + " (" + mID.asString() + ")");
- }
}
-#else
-
-void LLFloaterAvatarTextures::refresh()
-{
-}
-
-#endif
-
// static
void LLFloaterAvatarTextures::onClickDump(void* data)
{
-#if !LL_RELEASE_FOR_DOWNLOAD
- LLFloaterAvatarTextures* self = (LLFloaterAvatarTextures*)data;
- LLVOAvatar* avatarp = find_avatar(self->mID);
- if (!avatarp) return;
-
- for (S32 i = 0; i < avatarp->getNumTEs(); i++)
+ if (gAgent.isGodlike())
{
- const LLTextureEntry* te = avatarp->getTE(i);
- if (!te) continue;
+ LLFloaterAvatarTextures* self = (LLFloaterAvatarTextures*)data;
+ LLVOAvatar* avatarp = find_avatar(self->mID);
+ if (!avatarp) return;
- if (LLVOAvatar::isIndexLocalTexture((ETextureIndex)i))
+ for (S32 i = 0; i < avatarp->getNumTEs(); i++)
{
- LLUUID id = IMG_DEFAULT_AVATAR;
- EWearableType wearable_type = LLVOAvatarDictionary::getInstance()->getTEWearableType((ETextureIndex)i);
- LLWearable *wearable = gAgentWearables.getWearable(wearable_type, 0);
- if (wearable)
+ const LLTextureEntry* te = avatarp->getTE(i);
+ if (!te) continue;
+
+ if (LLVOAvatar::isIndexLocalTexture((ETextureIndex)i))
{
- LLLocalTextureObject *lto = wearable->getLocalTextureObject(i);
- if (lto)
+ LLUUID id = IMG_DEFAULT_AVATAR;
+ EWearableType wearable_type = LLVOAvatarDictionary::getInstance()->getTEWearableType((ETextureIndex)i);
+ LLWearable *wearable = gAgentWearables.getWearable(wearable_type, 0);
+ if (wearable)
{
- id = lto->getID();
+ LLLocalTextureObject *lto = wearable->getLocalTextureObject(i);
+ if (lto)
+ {
+ id = lto->getID();
+ }
+ }
+ if (id != IMG_DEFAULT_AVATAR)
+ {
+ llinfos << "Avatar TE " << i << " id " << id << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar TE " << i << " id " << "<DEFAULT>" << llendl;
}
- }
- if (id != IMG_DEFAULT_AVATAR)
- {
- llinfos << "Avatar TE " << i << " id " << id << llendl;
}
else
{
- llinfos << "Avatar TE " << i << " id " << "<DEFAULT>" << llendl;
+ llinfos << "Avatar TE " << i << " id " << te->getID() << llendl;
}
}
- else
- {
- llinfos << "Avatar TE " << i << " id " << te->getID() << llendl;
- }
}
-#endif
}
diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp
index ecbaac5743..c916e4b98c 100644
--- a/indra/newview/llfolderviewitem.cpp
+++ b/indra/newview/llfolderviewitem.cpp
@@ -437,11 +437,8 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation)
S32 LLFolderViewItem::getItemHeight()
{
- if (mHidden) return 0;
+ if (getHidden()) return 0;
- //S32 icon_height = mIcon->getHeight();
- //S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight());
- //return llmax( icon_height, label_height ) + ICON_PAD;
return mItemHeight;
}
@@ -824,32 +821,34 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
return handled;
}
-
void LLFolderViewItem::draw()
{
- if (mHidden) return;
+ if (getHidden())
+ {
+ return;
+ }
static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
- static LLUIColor sFocusOutlineColor =
- LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
+ static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);
static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE);
static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemSuffixColor", DEFAULT_WHITE);
static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
-
const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
const S32 TOP_PAD = default_params.item_top_pad;
+ const S32 FOCUS_LEFT = 1;
+ const LLFontGL* font = getLabelFontForStyle(mLabelStyle);
- bool possibly_has_children = false;
- bool up_to_date = mListener && mListener->isUpToDate();
- if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter...
- (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter)
- {
- possibly_has_children = true;
- }
- if(/*mControlLabel[0] != '\0' && */possibly_has_children)
+
+ //--------------------------------------------------------------------------------//
+ // Draw open folder arrow
+ //
+ const bool up_to_date = mListener && mListener->isUpToDate();
+ const bool possibly_has_children = ((up_to_date && hasVisibleChildren()) || // we fetched our children and some of them have passed the filter...
+ (!up_to_date && mListener && mListener->hasChildren())); // ...or we know we have children but haven't fetched them (doesn't obey filter)
+ if (possibly_has_children)
{
LLUIImage* arrow_image = default_params.folder_arrow_image;
gl_draw_scaled_rotated_image(
@@ -857,22 +856,16 @@ void LLFolderViewItem::draw()
ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor);
}
- // See also LLFolderView::updateRenamerPosition()
- F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
-
- LLFontGL* font = getLabelFontForStyle(mLabelStyle);
-
- // If we have keyboard focus, draw selection filled
- BOOL show_context = getRoot()->getShowSelectionContext();
- BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus());
- const S32 FOCUS_LEFT = 1;
- S32 focus_top = getRect().getHeight();
- S32 focus_bottom = getRect().getHeight() - mItemHeight;
- bool folder_open = (getRect().getHeight() > mItemHeight + 4);
- // always render "current" item, only render other selected items if
- // mShowSingleSelection is FALSE
- if( mIsSelected )
+ //--------------------------------------------------------------------------------//
+ // Draw highlight for selected items
+ //
+ const BOOL show_context = getRoot()->getShowSelectionContext();
+ const BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); // If we have keyboard focus, draw selection filled
+ const S32 focus_top = getRect().getHeight();
+ const S32 focus_bottom = getRect().getHeight() - mItemHeight;
+ const bool folder_open = (getRect().getHeight() > mItemHeight + 4);
+ if (mIsSelected) // always render "current" item. Only render other selected items if mShowSingleSelection is FALSE
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
LLColor4 bg_color = sHighlightBgColor;
@@ -891,152 +884,169 @@ void LLFolderViewItem::draw()
bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
}
}
-
- gl_rect_2d(
- FOCUS_LEFT,
- focus_top,
- getRect().getWidth() - 2,
- focus_bottom,
- bg_color, filled);
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ bg_color, filled);
if (mIsCurSelection)
{
- gl_rect_2d(
- FOCUS_LEFT,
- focus_top,
- getRect().getWidth() - 2,
- focus_bottom,
- sFocusOutlineColor, FALSE);
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ sFocusOutlineColor, FALSE);
}
if (folder_open)
{
- gl_rect_2d(
- FOCUS_LEFT,
- focus_bottom + 1, // overlap with bottom edge of above rect
- getRect().getWidth() - 2,
- 0,
- sFocusOutlineColor, FALSE);
+ gl_rect_2d(FOCUS_LEFT,
+ focus_bottom + 1, // overlap with bottom edge of above rect
+ getRect().getWidth() - 2,
+ 0,
+ sFocusOutlineColor, FALSE);
if (show_context)
{
- gl_rect_2d(
- FOCUS_LEFT,
- focus_bottom + 1,
- getRect().getWidth() - 2,
- 0,
- sHighlightBgColor, TRUE);
+ gl_rect_2d(FOCUS_LEFT,
+ focus_bottom + 1,
+ getRect().getWidth() - 2,
+ 0,
+ sHighlightBgColor, TRUE);
}
}
}
+
+ //--------------------------------------------------------------------------------//
+ // Draw DragNDrop highlight
+ //
if (mDragAndDropTarget)
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gl_rect_2d(
- FOCUS_LEFT,
- focus_top,
- getRect().getWidth() - 2,
- focus_bottom,
- sHighlightBgColor, FALSE);
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ sHighlightBgColor, FALSE);
if (folder_open)
{
- gl_rect_2d(
- FOCUS_LEFT,
- focus_bottom + 1, // overlap with bottom edge of above rect
- getRect().getWidth() - 2,
- 0,
- sHighlightBgColor, FALSE);
+ gl_rect_2d(FOCUS_LEFT,
+ focus_bottom + 1, // overlap with bottom edge of above rect
+ getRect().getWidth() - 2,
+ 0,
+ sHighlightBgColor, FALSE);
}
mDragAndDropTarget = FALSE;
}
- S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD;
- // First case is used for open folders
- if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80))
+
+ //--------------------------------------------------------------------------------//
+ // Draw open icon
+ //
+ const S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD;
+ if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders
{
mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1);
}
- else if(mIcon)
+ else if (mIcon)
{
mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1);
}
- if (!mLabel.empty())
+
+ //--------------------------------------------------------------------------------//
+ // Exit if no label to draw
+ //
+ if (mLabel.empty())
{
- // highlight filtered text
- BOOL debug_filters = getRoot()->getDebugFilters();
- LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor );
- F32 right_x;
- F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
+ return;
+ }
- if (debug_filters)
- {
- if (!getFiltered() && !possibly_has_children)
- {
- color.mV[VALPHA] *= 0.5f;
- }
+ LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor;
+ F32 right_x = 0;
+ F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
+ F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
- LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f);
- LLFontGL::getFontMonospace()->renderUTF8(
- mStatusText, 0, text_left, y, filter_color,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- S32_MAX, S32_MAX, &right_x, FALSE );
- text_left = right_x;
+ //--------------------------------------------------------------------------------//
+ // Highlight filtered text
+ //
+ if (getRoot()->getDebugFilters())
+ {
+ if (!getFiltered() && !possibly_has_children)
+ {
+ color.mV[VALPHA] *= 0.5f;
}
+ LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ?
+ LLColor4(0.5f, 0.8f, 0.5f, 1.f) :
+ LLColor4(0.8f, 0.5f, 0.5f, 1.f);
+ LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ text_left = right_x;
+ }
- font->renderUTF8( mLabel, 0, text_left, y, color,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE);
+ //--------------------------------------------------------------------------------//
+ // Draw the actual label text
+ //
+ font->renderUTF8(mLabel, 0, text_left, y, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE);
-// LLViewerInventoryCategory *item = 0;
-// if (getListener())
-// item = gInventory.getCategory(getListener()->getUUID());
- bool root_is_loading = false;
- if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),gInventory.getRootFolderID()))
- {
- // Descendent of my inventory.
- root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress();
- }
- if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),gInventory.getLibraryRootFolderID()))
- {
- // Descendent of library
- root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress();
- }
-
- if ( (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime"))
- || (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() && root_is_loading && mShowLoadStatus) )
- {
- std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) ";
- font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE);
- }
+ //--------------------------------------------------------------------------------//
+ // Draw "Loading..." text
+ //
+ bool root_is_loading = false;
+ if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),
+ gInventory.getRootFolderID())) // Descendent of my inventory
+ {
+ root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress();
+ }
+ if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),
+ gInventory.getLibraryRootFolderID())) // Descendent of library
+ {
+ root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress();
+ }
+ if ((mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) ||
+ (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() && root_is_loading && (mShowLoadStatus || mHidden)))
+ {
+ std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) ";
+ font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, S32_MAX, &right_x, FALSE);
+ }
- if (!mLabelSuffix.empty())
- {
- font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- S32_MAX, S32_MAX, &right_x, FALSE );
- }
+ //--------------------------------------------------------------------------------//
+ // Draw label suffix
+ //
+ if (!mLabelSuffix.empty())
+ {
+ font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ }
- if (mStringMatchOffset != std::string::npos)
+ //--------------------------------------------------------------------------------//
+ // Highlight string match
+ //
+ if (mStringMatchOffset != std::string::npos)
+ {
+ // don't draw backgrounds for zero-length strings
+ S32 filter_string_length = getRoot()->getFilterSubString().size();
+ if (filter_string_length > 0)
{
- // don't draw backgrounds for zero-length strings
- S32 filter_string_length = getRoot()->getFilterSubString().size();
- if (filter_string_length > 0)
- {
- std::string combined_string = mLabel + mLabelSuffix;
- S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1;
- S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2;
- S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
- S32 top = getRect().getHeight() - TOP_PAD;
+ std::string combined_string = mLabel + mLabelSuffix;
+ S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1;
+ S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2;
+ S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
+ S32 top = getRect().getHeight() - TOP_PAD;
- LLUIImage* box_image = default_params.selection_image;
- LLRect box_rect(left, top, right, bottom);
- box_image->draw(box_rect, sFilterBGColor);
- F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset);
- F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
- font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy,
- sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- filter_string_length, S32_MAX, &right_x, FALSE );
- }
+ LLUIImage* box_image = default_params.selection_image;
+ LLRect box_rect(left, top, right, bottom);
+ box_image->draw(box_rect, sFilterBGColor);
+ F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset);
+ F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
+ font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ filter_string_length, S32_MAX, &right_x, FALSE );
}
}
}
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 604af3d074..0fbf3148ac 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -31,10 +31,12 @@
*/
#include "llviewerprecompiledheaders.h"
+#include "llinventorybridge.h"
+
// external projects
#include "lltransfersourceasset.h"
-#include "llinventorybridge.h"
+
#include "llagent.h"
#include "llagentcamera.h"
@@ -594,7 +596,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
LLViewerInventoryItem* inv_item = gInventory.getItem(mUUID);
if (inv_item)
{
- is_asset_knowable = is_asset_id_knowable(inv_item->getType());
+ is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(inv_item->getType());
}
if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308
|| (! ( isItemPermissive() || gAgent.isGodlike() ) )
@@ -1923,50 +1925,6 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
return accept;
}
-bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
-{
- // Valid COF items are:
- // - links to wearables (body parts or clothing)
- // - links to attachments
- // - links to gestures
- // - links to ensemble folders
- LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem();
- if (linked_item)
- {
- LLAssetType::EType type = linked_item->getType();
- return (type == LLAssetType::AT_CLOTHING ||
- type == LLAssetType::AT_BODYPART ||
- type == LLAssetType::AT_GESTURE ||
- type == LLAssetType::AT_OBJECT);
- }
- else
- {
- LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory();
- // BAP remove AT_NONE support after ensembles are fully working?
- return (linked_category &&
- ((linked_category->getPreferredType() == LLFolderType::FT_NONE) ||
- (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType()))));
- }
-}
-
-
-bool LLFindWearables::operator()(LLInventoryCategory* cat,
- LLInventoryItem* item)
-{
- if(item)
- {
- if((item->getType() == LLAssetType::AT_CLOTHING)
- || (item->getType() == LLAssetType::AT_BODYPART))
- {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-
-
//Used by LLFolderBridge as callback for directory recursion.
class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver
{
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index ab2133d779..3e16dfea5f 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -239,6 +239,47 @@ bool LLNameCategoryCollector::operator()(
return false;
}
+bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ // Valid COF items are:
+ // - links to wearables (body parts or clothing)
+ // - links to attachments
+ // - links to gestures
+ // - links to ensemble folders
+ LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem();
+ if (linked_item)
+ {
+ LLAssetType::EType type = linked_item->getType();
+ return (type == LLAssetType::AT_CLOTHING ||
+ type == LLAssetType::AT_BODYPART ||
+ type == LLAssetType::AT_GESTURE ||
+ type == LLAssetType::AT_OBJECT);
+ }
+ else
+ {
+ LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory();
+ // BAP remove AT_NONE support after ensembles are fully working?
+ return (linked_category &&
+ ((linked_category->getPreferredType() == LLFolderType::FT_NONE) ||
+ (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType()))));
+ }
+}
+
+bool LLFindWearables::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((item->getType() == LLAssetType::AT_CLOTHING)
+ || (item->getType() == LLAssetType::AT_BODYPART))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
///----------------------------------------------------------------------------
/// LLAssetIDMatches
///----------------------------------------------------------------------------
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 1a1dffe85c..b5a73a3143 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -3087,6 +3087,13 @@ bool process_login_success_response()
}
}
+ // Start the process of fetching the OpenID session cookie for this user login
+ std::string openid_url = response["openid_url"];
+ if(!openid_url.empty())
+ {
+ std::string openid_token = response["openid_token"];
+ LLViewerMedia::openIDSetup(openid_url, openid_token);
+ }
bool success = false;
// JC: gesture loading done below, when we have an asset system
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 6f0d9cdd95..d9fabc7d64 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -257,7 +257,43 @@ public:
LLViewerMediaImpl *mMediaImpl;
bool mInitialized;
};
+
+class LLViewerMediaOpenIDResponder : public LLHTTPClient::Responder
+{
+LOG_CLASS(LLViewerMediaOpenIDResponder);
+public:
+ LLViewerMediaOpenIDResponder( )
+ {
+ }
+
+ ~LLViewerMediaOpenIDResponder()
+ {
+ }
+
+ /* virtual */ void completedHeader(U32 status, const std::string& reason, const LLSD& content)
+ {
+ LL_DEBUGS("MediaAuth") << "status = " << status << ", reason = " << reason << LL_ENDL;
+ LL_DEBUGS("MediaAuth") << content << LL_ENDL;
+ std::string cookie = content["set-cookie"].asString();
+
+ LLViewerMedia::openIDCookieResponse(cookie);
+ }
+
+ /* virtual */ void completedRaw(
+ U32 status,
+ const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ // This is just here to disable the default behavior (attempting to parse the response as llsd).
+ // We don't care about the content of the response, only the set-cookie header.
+ }
+
+};
+
LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL;
+LLURL LLViewerMedia::sOpenIDURL;
+std::string LLViewerMedia::sOpenIDCookie;
static LLViewerMedia::impl_list sViewerMediaImplList;
static LLViewerMedia::impl_id_map sViewerMediaTextureIDMap;
static LLTimer sMediaCreateTimer;
@@ -1067,7 +1103,8 @@ void LLViewerMedia::clearAllCookies()
}
}
-
+ // If we have an OpenID cookie, re-add it to the cookie store.
+ setOpenIDCookie();
}
/////////////////////////////////////////////////////////////////////////////////////////
@@ -1168,7 +1205,9 @@ void LLViewerMedia::loadCookieFile()
pimpl->mMediaSource->clear_cookies();
}
}
-
+
+ // If we have an OpenID cookie, re-add it to the cookie store.
+ setOpenIDCookie();
}
@@ -1241,6 +1280,62 @@ void LLViewerMedia::removeCookie(const std::string &name, const std::string &dom
}
+/////////////////////////////////////////////////////////////////////////////////////////
+// static
+void LLViewerMedia::setOpenIDCookie()
+{
+ if(!sOpenIDCookie.empty())
+ {
+ getCookieStore()->setCookiesFromHost(sOpenIDCookie, sOpenIDURL.mAuthority);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// static
+void LLViewerMedia::openIDSetup(const std::string &openid_url, const std::string &openid_token)
+{
+ LL_DEBUGS("MediaAuth") << "url = \"" << openid_url << "\", token = \"" << openid_token << "\"" << LL_ENDL;
+
+ // post the token to the url
+ // the responder will need to extract the cookie(s).
+
+ // Save the OpenID URL for later -- we may need the host when adding the cookie.
+ sOpenIDURL.init(openid_url.c_str());
+
+ // We shouldn't ever do this twice, but just in case this code gets repurposed later, clear existing cookies.
+ sOpenIDCookie.clear();
+
+ LLSD headers = LLSD::emptyMap();
+ // Keep LLHTTPClient from adding an "Accept: application/llsd+xml" header
+ headers["Accept"] = "*/*";
+ // and use the expected content-type for a post, instead of the LLHTTPClient::postRaw() default of "application/octet-stream"
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
+
+ // postRaw() takes ownership of the buffer and releases it later, so we need to allocate a new buffer here.
+ size_t size = openid_token.size();
+ U8 *data = new U8[size];
+ memcpy(data, openid_token.data(), size);
+
+ LLHTTPClient::postRaw(
+ openid_url,
+ data,
+ size,
+ new LLViewerMediaOpenIDResponder(),
+ headers);
+
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// static
+void LLViewerMedia::openIDCookieResponse(const std::string &cookie)
+{
+ LL_DEBUGS("MediaAuth") << "Cookie received: \"" << cookie << "\"" << LL_ENDL;
+
+ sOpenIDCookie += cookie;
+
+ setOpenIDCookie();
+}
+
bool LLViewerMedia::hasInWorldMedia()
{
if (sInWorldMediaDisabled) return false;
diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h
index 10dacf9532..e829d7a5b4 100644
--- a/indra/newview/llviewermedia.h
+++ b/indra/newview/llviewermedia.h
@@ -44,6 +44,8 @@
#include "llpluginclassmedia.h"
#include "v4color.h"
+#include "llurl.h"
+
class LLViewerMediaImpl;
class LLUUID;
class LLViewerMediaTexture;
@@ -152,11 +154,17 @@ public:
static void addCookie(const std::string &name, const std::string &value, const std::string &domain, const LLDate &expires, const std::string &path = std::string("/"), bool secure = false );
static void addSessionCookie(const std::string &name, const std::string &value, const std::string &domain, const std::string &path = std::string("/"), bool secure = false );
static void removeCookie(const std::string &name, const std::string &domain, const std::string &path = std::string("/") );
+
+ static void openIDSetup(const std::string &openid_url, const std::string &openid_token);
+ static void openIDCookieResponse(const std::string &cookie);
private:
+ static void setOpenIDCookie();
static void onTeleportFinished();
static LLPluginCookieStore *sCookieStore;
+ static LLURL sOpenIDURL;
+ static std::string sOpenIDCookie;
};
// Implementation functions not exported into header file
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 3a6aed01ce..b54305f021 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -1802,9 +1802,10 @@ class LLAdvancedDebugAvatarTextures : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- handle_debug_avatar_textures(NULL);
-#endif
+ if (gAgent.isGodlike())
+ {
+ handle_debug_avatar_textures(NULL);
+ }
return true;
}
};
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 6dc0983f10..5f8c03b545 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -4031,7 +4031,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
//clear animation flags
avatarp = (LLVOAvatar *)gObjectList.findObject(uuid);
- if (!isAgentAvatarValid())
+ if (!avatarp)
{
// no agent by this ID...error?
LL_WARNS("Messaging") << "Received animation state for unknown avatar" << uuid << LL_ENDL;
diff --git a/indra/newview/res-sdl/toolbuy.BMP b/indra/newview/res-sdl/toolbuy.BMP
new file mode 100644
index 0000000000..07e9273721
--- /dev/null
+++ b/indra/newview/res-sdl/toolbuy.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolopen.BMP b/indra/newview/res-sdl/toolopen.BMP
new file mode 100644
index 0000000000..5b87979304
--- /dev/null
+++ b/indra/newview/res-sdl/toolopen.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolsit.BMP b/indra/newview/res-sdl/toolsit.BMP
new file mode 100644
index 0000000000..8ce59ae97a
--- /dev/null
+++ b/indra/newview/res-sdl/toolsit.BMP
Binary files differ
diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc
index 7a965cf57e..ecdcacec46 100644
--- a/indra/newview/res/viewerRes.rc
+++ b/indra/newview/res/viewerRes.rc
@@ -129,8 +129,8 @@ TOOLBUY CURSOR "toolbuy.cur"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,0,0,3422
- PRODUCTVERSION 2,0,0,3422
+ FILEVERSION 2,0,0,203110
+ PRODUCTVERSION 2,0,0,203110
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -147,12 +147,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Linden Lab"
VALUE "FileDescription", "Second Life"
- VALUE "FileVersion", "2.0.0.200030"
+ VALUE "FileVersion", "2.0.0.203110"
VALUE "InternalName", "Second Life"
VALUE "LegalCopyright", "Copyright © 2001-2008, Linden Research, Inc."
VALUE "OriginalFilename", "SecondLife.exe"
VALUE "ProductName", "Second Life"
- VALUE "ProductVersion", "2.0.0.200030"
+ VALUE "ProductVersion", "2.0.0.203110"
END
END
BLOCK "VarFileInfo"
diff --git a/indra/newview/skins/default/xui/de/panel_status_bar.xml b/indra/newview/skins/default/xui/de/panel_status_bar.xml
index 0e182fa417..803bd1b5ab 100644
--- a/indra/newview/skins/default/xui/de/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/de/panel_status_bar.xml
@@ -22,7 +22,7 @@
[AMT] L$
</panel.string>
<button label="" label_selected="" name="buycurrency" tool_tip="Mein Kontostand"/>
- <button label="Kaufen" name="buyL" tool_tip="Hier klicken, um mehr L$ zu kaufen"/>
+ <button label=" " name="buyL" tool_tip="Hier klicken, um mehr L$ zu kaufen"/>
<text name="TimeText" tool_tip="Aktuelle Zeit (Pazifik)">
24:00 H PST
</text>
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index fd046c3695..957ab240ee 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -2842,7 +2842,7 @@ even though the user gets a free copy.
left_delta="0"
name="button about land"
top_pad="4"
- width="112" />
+ width="125" />
<check_box
control_name="ShowParcelOwners"
height="19"
@@ -2863,7 +2863,7 @@ even though the user gets a free copy.
left="20"
name="label_parcel_modify"
top="152"
- width="150">
+ width="240">
Modify Parcel
</text>
<button
@@ -2875,7 +2875,7 @@ even though the user gets a free copy.
left="30"
name="button subdivide land"
top="172"
- width="112" />
+ width="125" />
<button
follows="left|top"
height="23"
@@ -2885,7 +2885,7 @@ even though the user gets a free copy.
left_delta="0"
name="button join land"
top_pad="4"
- width="112" />
+ width="125" />
<text
type="string"
length="1"
@@ -2896,7 +2896,7 @@ even though the user gets a free copy.
left="20"
name="label_parcel_trans"
top="256"
- width="150">
+ width="240">
Land Transactions
</text>
<button
@@ -2908,7 +2908,7 @@ even though the user gets a free copy.
left="30"
name="button buy land"
top="276"
- width="112" />
+ width="125" />
<button
follows="left|top"
height="23"
@@ -2918,6 +2918,6 @@ even though the user gets a free copy.
left_delta="0"
name="button abandon land"
top_pad="4"
- width="112" />
+ width="125" />
</panel>
</floater>
diff --git a/indra/newview/skins/default/xui/es/floater_tools.xml b/indra/newview/skins/default/xui/es/floater_tools.xml
index ac3a949821..9c5fea9267 100644
--- a/indra/newview/skins/default/xui/es/floater_tools.xml
+++ b/indra/newview/skins/default/xui/es/floater_tools.xml
@@ -399,7 +399,7 @@ máximo" name="checkbox fullbright"/>
<text name="tex gen">
Detallado
</text>
- <combo_box name="combobox texgen" width="86">
+ <combo_box name="combobox texgen">
<combo_box.item label="Por defecto" name="Default"/>
<combo_box.item label="Plano" name="Planar"/>
</combo_box>
@@ -442,9 +442,9 @@ máximo" name="checkbox fullbright"/>
<check_box label="Voltear" name="checkbox flip s"/>
<spinner label="Vertical (V)" name="TexScaleV"/>
<check_box label="Voltear" name="checkbox flip t"/>
- <spinner label="Rotación" left="118" name="TexRot" width="62"/>
- <spinner label="Repeticiones / Metro" left="118" name="rptctrl" width="62"/>
- <button label="Aplicar" label_selected="Aplicar" left_delta="72" name="button apply"/>
+ <spinner label="Rotación" name="TexRot"/>
+ <spinner label="Repeticiones / Metro" name="rptctrl"/>
+ <button label="Aplicar" label_selected="Aplicar" name="button apply"/>
<text name="tex offset">
Desplazar
</text>
@@ -487,8 +487,8 @@ Gris = Público"/>
<text name="label_parcel_modify">
Modificar la parcela
</text>
- <button label="Dividir" label_selected="Dividir" name="button subdivide land" width="140"/>
- <button label="Inscribirse" label_selected="Inscribirse" name="button join land" width="140"/>
+ <button label="Dividir" label_selected="Dividir" name="button subdivide land"/>
+ <button label="Inscribirse" label_selected="Inscribirse" name="button join land"/>
<text name="label_parcel_trans">
Transacciones de terreno
</text>