summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llimage/llimage.cpp4
-rw-r--r--indra/llimage/llimagej2c.cpp14
-rw-r--r--indra/llkdu/llimagej2ckdu.cpp9
-rw-r--r--indra/llprimitive/llgltfmaterial.cpp109
-rw-r--r--indra/llprimitive/llgltfmaterial.h62
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/llinventorybridge.cpp6
-rw-r--r--indra/newview/llinventorygallery.cpp33
-rw-r--r--indra/newview/llinventorygallery.h1
-rw-r--r--indra/newview/llinventorygallerymenu.cpp288
-rw-r--r--indra/newview/llpanelavatar.cpp5
-rw-r--r--indra/newview/llpanelavatar.h2
-rw-r--r--indra/newview/llpanelgroupnotices.cpp7
-rw-r--r--indra/newview/llpanelmaininventory.cpp1
-rw-r--r--indra/newview/llpanelprofile.cpp46
-rw-r--r--indra/newview/llpanelprofile.h6
-rw-r--r--indra/newview/skins/default/xui/en/menu_gallery_inventory.xml228
17 files changed, 751 insertions, 72 deletions
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 031471d1fe..7ac80825b5 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -2130,6 +2130,10 @@ U8* LLImageFormatted::reallocateData(S32 size)
// virtual
void LLImageFormatted::deleteData()
{
+ if (mDecoding)
+ {
+ LL_ERRS() << "LLImageFormatted::deleteData() is called during decoding" << LL_ENDL;
+ }
sGlobalFormattedMemory -= getDataSize();
LLImageBase::deleteData();
}
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 8dba1641a6..a4957466d4 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -157,10 +157,10 @@ bool LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 fir
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
LLTimer elapsed;
- bool res = true;
-
resetLastError();
+ mDecoding = true;
+ bool res;
// Check to make sure that this instance has been initialized with data
if (!getData() || (getDataSize() < 16))
{
@@ -171,7 +171,6 @@ bool LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 fir
{
// Update the raw discard level
updateRawDiscardLevel();
- mDecoding = true;
res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count);
}
@@ -181,12 +180,21 @@ bool LLImageJ2C::decodeChannels(LLImageRaw *raw_imagep, F32 decode_time, S32 fir
{
// Failed
raw_imagep->deleteData();
+ res = false;
}
else
{
mDecoding = false;
}
}
+ else
+ {
+ if (mDecoding)
+ {
+ LL_WARNS() << "decodeImpl failed but mDecoding is TRUE" << LL_ENDL;
+ mDecoding = false;
+ }
+ }
if (!mLastError.empty())
{
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 2ad42d6b87..d96cd105dd 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -514,6 +514,7 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
ECodeStreamMode mode = MODE_FAST;
+ bool limit_time = decode_time > 0.0f;
LLTimer decode_timer;
if (!mCodeStreamp->exists())
@@ -578,16 +579,18 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
mCodeStreamp.get()));
}
// Do the actual processing
- F32 remaining_time = decode_time - decode_timer.getElapsedTimeF32();
+ F32 remaining_time = limit_time ? static_cast<F32>(decode_time - decode_timer.getElapsedTimeF32()) : 0.0f;
// This is where we do the actual decode. If we run out of time, return false.
- if (mDecodeState->processTileDecode(remaining_time, (decode_time > 0.0f)))
+ if (mDecodeState->processTileDecode(remaining_time, limit_time))
{
mDecodeState.reset();
}
else
{
// Not finished decoding yet.
- // setLastError("Ran out of time while decoding");
+ base.setLastError("Ran out of time while decoding");
+ base.decodeFailed();
+ cleanupCodeStream();
return false;
}
}
diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp
index ae165f7fa4..e9d3350ee9 100644
--- a/indra/llprimitive/llgltfmaterial.cpp
+++ b/indra/llprimitive/llgltfmaterial.cpp
@@ -47,16 +47,49 @@ const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM_ROTATION = "rota
// special UUID that indicates a null UUID in override data
const LLUUID LLGLTFMaterial::GLTF_OVERRIDE_NULL_UUID = LLUUID("ffffffff-ffff-ffff-ffff-ffffffffffff");
+LLGLTFMaterial::LLGLTFMaterial()
+{
+ // IMPORTANT: since we use the hash of the member variables memory block of
+ // this class to detect changes, we must ensure that all its padding bytes
+ // have been zeroed out. But of course, we must leave the LLRefCount member
+ // variable untouched (and skip it when hashing), and we cannot either
+ // touch the local texture overrides map (else we destroy pointers, and
+ // sundry private data, which would lead to a crash when using that map).
+ // The variable members have therefore been arranged so that anything,
+ // starting at mLocalTexDataDigest and up to the end of the members, can be
+ // safely zeroed. HB
+ const size_t offset = intptr_t(&mLocalTexDataDigest) - intptr_t(this);
+ memset((void*)((const char*)this + offset), 0, sizeof(*this) - offset);
+
+ // Now that we zeroed out our member variables, we can set the ones that
+ // should not be zero to their default value. HB
+ mBaseColor.set(1.f, 1.f, 1.f, 1.f);
+ mMetallicFactor = mRoughnessFactor = 1.f;
+ mAlphaCutoff = 0.5f;
+ for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ mTextureTransform[i].mScale.set(1.f, 1.f);
+#if 0
+ mTextureTransform[i].mOffset.clear();
+ mTextureTransform[i].mRotation = 0.f;
+#endif
+ }
+#if 0
+ mLocalTexDataDigest = 0;
+ mAlphaMode = ALPHA_MODE_OPAQUE; // This is 0
+ mOverrideDoubleSided = mOverrideAlphaMode = false;
+#endif
+}
+
void LLGLTFMaterial::TextureTransform::getPacked(F32 (&packed)[8]) const
{
packed[0] = mScale.mV[VX];
packed[1] = mScale.mV[VY];
packed[2] = mRotation;
- // packed[3] = unused
packed[4] = mOffset.mV[VX];
packed[5] = mOffset.mV[VY];
- // packed[6] = unused
- // packed[7] = unused
+ // Not used but nonetheless zeroed for proper hashing. HB
+ packed[3] = packed[6] = packed[7] = 0.f;
}
bool LLGLTFMaterial::TextureTransform::operator==(const TextureTransform& other) const
@@ -89,13 +122,37 @@ LLGLTFMaterial& LLGLTFMaterial::operator=(const LLGLTFMaterial& rhs)
mOverrideDoubleSided = rhs.mOverrideDoubleSided;
mOverrideAlphaMode = rhs.mOverrideAlphaMode;
- mTrackingIdToLocalTexture = rhs.mTrackingIdToLocalTexture;
-
- updateTextureTracking();
+ if (rhs.mTrackingIdToLocalTexture.empty())
+ {
+ mTrackingIdToLocalTexture.clear();
+ mLocalTexDataDigest = 0;
+ }
+ else
+ {
+ mTrackingIdToLocalTexture = rhs.mTrackingIdToLocalTexture;
+ updateLocalTexDataDigest();
+ updateTextureTracking();
+ }
return *this;
}
+void LLGLTFMaterial::updateLocalTexDataDigest()
+{
+ mLocalTexDataDigest = 0;
+ if (!mTrackingIdToLocalTexture.empty())
+ {
+ for (local_tex_map_t::const_iterator
+ it = mTrackingIdToLocalTexture.begin(),
+ end = mTrackingIdToLocalTexture.end();
+ it != end; ++it)
+ {
+ mLocalTexDataDigest ^= it->first.getDigest64() ^
+ it->second.getDigest64();
+ }
+ }
+}
+
bool LLGLTFMaterial::operator==(const LLGLTFMaterial& rhs) const
{
return mTextureId == rhs.mTextureId &&
@@ -547,7 +604,7 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat)
{
LL_PROFILE_ZONE_SCOPED;
- for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLUUID& texture_id = mTextureId[i];
const LLUUID& override_texture_id = override_mat.mTextureId[i];
@@ -588,7 +645,7 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat)
mDoubleSided = override_mat.mDoubleSided;
}
- for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
if (override_mat.mTextureTransform[i].mOffset != getDefaultTextureOffset())
{
@@ -606,9 +663,13 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat)
}
}
- mTrackingIdToLocalTexture.insert(override_mat.mTrackingIdToLocalTexture.begin(), override_mat.mTrackingIdToLocalTexture.begin());
-
- updateTextureTracking();
+ if (!override_mat.mTrackingIdToLocalTexture.empty())
+ {
+ auto it = override_mat.mTrackingIdToLocalTexture.begin();
+ mTrackingIdToLocalTexture.insert(it, it);
+ updateLocalTexDataDigest();
+ updateTextureTracking();
+ }
}
void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data)
@@ -618,7 +679,7 @@ void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& d
// make every effort to shave bytes here
- for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLUUID& texture_id = mTextureId[i];
const LLUUID& override_texture_id = override_mat.mTextureId[i];
@@ -663,7 +724,7 @@ void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& d
data["ds"] = override_mat.mDoubleSided;
}
- for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
if (override_mat.mTextureTransform[i].mOffset != getDefaultTextureOffset())
{
@@ -742,7 +803,7 @@ void LLGLTFMaterial::applyOverrideLLSD(const LLSD& data)
const LLSD& ti = data["ti"];
if (ti.isArray())
{
- for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
const LLSD& o = ti[i]["o"];
if (o.isDefined())
@@ -768,27 +829,36 @@ void LLGLTFMaterial::applyOverrideLLSD(const LLSD& data)
LLUUID LLGLTFMaterial::getHash() const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- // HACK - hash the bytes of this object but don't include the ref count
- LLUUID hash;
- HBXXH128::digest(hash, (unsigned char*)this + sizeof(LLRefCount), sizeof(*this) - sizeof(LLRefCount));
- return hash;
+ // *HACK: hash the bytes of this object but do not include the ref count
+ // neither the local texture overrides (which is a map, with pointers to
+ // key/value pairs that would change from one LLGLTFMaterial instance to
+ // the other, even though the key/value pairs could be the same, and stored
+ // elsewhere in the memory heap or on the stack).
+ // Note: this does work properly to compare two LLGLTFMaterial instances
+ // only because the padding bytes between their member variables have been
+ // dutifully zeroed in the constructor. HB
+ const size_t offset = intptr_t(&mLocalTexDataDigest) - intptr_t(this);
+ return HBXXH128::digest((const void*)((const char*)this + offset),
+ sizeof(*this) - offset);
}
void LLGLTFMaterial::addLocalTextureTracking(const LLUUID& tracking_id, const LLUUID& tex_id)
{
mTrackingIdToLocalTexture[tracking_id] = tex_id;
+ updateLocalTexDataDigest();
}
void LLGLTFMaterial::removeLocalTextureTracking(const LLUUID& tracking_id)
{
mTrackingIdToLocalTexture.erase(tracking_id);
+ updateLocalTexDataDigest();
}
bool LLGLTFMaterial::replaceLocalTexture(const LLUUID& tracking_id, const LLUUID& old_id, const LLUUID& new_id)
{
bool res = false;
- for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
+ for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
if (mTextureId[i] == old_id)
{
@@ -809,6 +879,7 @@ bool LLGLTFMaterial::replaceLocalTexture(const LLUUID& tracking_id, const LLUUID
{
mTrackingIdToLocalTexture.erase(tracking_id);
}
+ updateLocalTexDataDigest();
return res;
}
diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h
index 02f62fb08c..9f817d6a19 100644
--- a/indra/llprimitive/llgltfmaterial.h
+++ b/indra/llprimitive/llgltfmaterial.h
@@ -81,7 +81,7 @@ public:
ALPHA_MODE_MASK
};
- LLGLTFMaterial() {}
+ LLGLTFMaterial();
LLGLTFMaterial(const LLGLTFMaterial& rhs);
LLGLTFMaterial& operator=(const LLGLTFMaterial& rhs);
@@ -110,25 +110,6 @@ public:
static const char* const GLTF_FILE_EXTENSION_TRANSFORM_ROTATION;
static const LLUUID GLTF_OVERRIDE_NULL_UUID;
- std::array<LLUUID, GLTF_TEXTURE_INFO_COUNT> mTextureId;
- std::array<TextureTransform, GLTF_TEXTURE_INFO_COUNT> mTextureTransform;
-
- // NOTE: initialize values to defaults according to the GLTF spec
- // NOTE: these values should be in linear color space
- LLColor4 mBaseColor = LLColor4(1, 1, 1, 1);
- LLColor3 mEmissiveColor = LLColor3(0, 0, 0);
-
- F32 mMetallicFactor = 1.f;
- F32 mRoughnessFactor = 1.f;
- F32 mAlphaCutoff = 0.5f;
-
- bool mDoubleSided = false;
- AlphaMode mAlphaMode = ALPHA_MODE_OPAQUE;
-
- // override specific flags for state that can't use off-by-epsilon or UUID hack
- bool mOverrideDoubleSided = false;
- bool mOverrideAlphaMode = false;
-
// get a UUID based on a hash of this LLGLTFMaterial
LLUUID getHash() const;
@@ -229,10 +210,6 @@ public:
virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID &old_id, const LLUUID& new_id);
virtual void updateTextureTracking();
- // These fields are local to viewer and are a part of local bitmap support
- typedef std::map<LLUUID, LLUUID> local_tex_map_t;
- local_tex_map_t mTrackingIdToLocalTexture;
-
protected:
static LLVector2 vec2FromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const LLVector2& default_value);
static F32 floatFromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const F32 default_value);
@@ -249,4 +226,41 @@ protected:
void writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write = false) const;
template<typename T>
static void writeToTexture(tinygltf::Model& model, T& texture_info, const LLUUID& texture_id, const TextureTransform& transform, bool force_write = false);
+
+ // Used to update the digest of the mTrackingIdToLocalTexture map each time
+ // it is changed; this way, that digest can be used by the fast getHash()
+ // method intsead of having to hash all individual keys and values. HB
+ void updateLocalTexDataDigest();
+
+public:
+ // These fields are local to viewer and are a part of local bitmap support
+ // IMPORTANT: do not move this member down (and do not move
+ // mLocalTexDataDigest either): the getHash() method does rely on the
+ // current ordering. HB
+ typedef std::map<LLUUID, LLUUID> local_tex_map_t;
+ local_tex_map_t mTrackingIdToLocalTexture;
+
+ // Used to store a digest of mTrackingIdToLocalTexture when the latter is
+ // not empty, or zero otherwise. HB
+ U64 mLocalTexDataDigest;
+
+ std::array<LLUUID, GLTF_TEXTURE_INFO_COUNT> mTextureId;
+ std::array<TextureTransform, GLTF_TEXTURE_INFO_COUNT> mTextureTransform;
+
+ // NOTE: initialize values to defaults according to the GLTF spec
+ // NOTE: these values should be in linear color space
+ LLColor4 mBaseColor;
+ LLColor3 mEmissiveColor;
+
+ F32 mMetallicFactor;
+ F32 mRoughnessFactor;
+ F32 mAlphaCutoff;
+
+ AlphaMode mAlphaMode;
+ bool mDoubleSided;
+
+ // Override specific flags for state that can't use off-by-epsilon or UUID
+ // hack
+ bool mOverrideDoubleSided;
+ bool mOverrideAlphaMode;
};
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 0e7b60da8a..a8a1887568 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-7.1.2 \ No newline at end of file
+7.1.2
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 932a0316dd..b44fcf3e03 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -4360,12 +4360,10 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
if (!isInboxFolder() // don't allow creation in inbox
&& outfits_id != mUUID)
{
- bool menu_items_added = false;
// Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
{
items.push_back(std::string("New Folder"));
- menu_items_added = true;
}
if (!isMarketplaceListingsFolder())
{
@@ -4383,10 +4381,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
disabled_items.push_back("New Settings");
}
}
- if (menu_items_added)
- {
- items.push_back(std::string("Create Separator"));
- }
}
getClipboardEntries(false, items, disabled_items, flags);
}
diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
index 68581d04eb..7ded32cf19 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -2022,6 +2022,39 @@ void LLInventoryGallery::pasteAsLink(const LLUUID& dest,
}
}
+void LLInventoryGallery::doCreate(const LLUUID& dest, const LLSD& userdata)
+{
+
+ LLViewerInventoryCategory* cat = gInventory.getCategory(dest);
+ if (cat && mFolderID != dest)
+ {
+ menu_create_inventory_item(NULL, dest, userdata, LLUUID::null);
+ }
+ else
+ {
+ // todo: needs to reset current floater's filter,
+ // like reset_inventory_filter()
+
+ LLHandle<LLPanel> handle = getHandle();
+ std::function<void(const LLUUID&)> callback_cat_created =
+ [handle](const LLUUID& new_id)
+ {
+ gInventory.notifyObservers();
+ LLInventoryGallery* panel = static_cast<LLInventoryGallery*>(handle.get());
+ if (panel && new_id.notNull())
+ {
+ panel->clearSelection();
+ if (panel->mItemMap.count(new_id) != 0)
+ {
+ panel->addItemSelection(new_id, true);
+ }
+ }
+ };
+
+ menu_create_inventory_item(NULL, mFolderID, userdata, LLUUID::null, callback_cat_created);
+ }
+}
+
void LLInventoryGallery::claimEditHandler()
{
gEditMenuHandler = this;
diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h
index 0c52e7b713..bc4ac7db2a 100644
--- a/indra/newview/llinventorygallery.h
+++ b/indra/newview/llinventorygallery.h
@@ -166,6 +166,7 @@ public:
void deleteSelection();
bool canDeleteSelection();
void pasteAsLink();
+ void doCreate(const LLUUID& dest, const LLSD& userdata);
void setSortOrder(U32 order, bool update = false);
U32 getSortOrder() { return mSortOrder; };
diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp
index 5f4b816b99..c8ac73b838 100644
--- a/indra/newview/llinventorygallerymenu.cpp
+++ b/indra/newview/llinventorygallerymenu.cpp
@@ -32,9 +32,11 @@
#include "llappearancemgr.h"
#include "llavataractions.h"
#include "llclipboard.h"
+#include "llenvironment.h"
#include "llfloaterreg.h"
#include "llfloatersidepanelcontainer.h"
#include "llfloaterworldmap.h"
+#include "llfriendcard.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
@@ -48,6 +50,41 @@
#include "llviewerwindow.h"
#include "llvoavatarself.h"
+
+void modify_outfit(BOOL append, const LLUUID& cat_id, LLInventoryModel* model)
+{
+ LLViewerInventoryCategory* cat = model->getCategory(cat_id);
+ if (!cat) return;
+
+ // checking amount of items to wear
+ static LLCachedControl<U32> max_items(gSavedSettings, "WearFolderLimit", 125);
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
+ model->collectDescendentsIf(cat_id,
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ not_worn);
+
+ if (items.size() > max_items())
+ {
+ LLSD args;
+ args["AMOUNT"] = llformat("%d", static_cast<U32>(max_items));
+ LLNotificationsUtil::add("TooManyWearables", args);
+ return;
+ }
+ if (model->isObjectDescendentOf(cat_id, gInventory.getRootFolderID()))
+ {
+ LLAppearanceMgr::instance().wearInventoryCategory(cat, FALSE, append);
+ }
+ else
+ {
+ // Library, we need to copy content first
+ LLAppearanceMgr::instance().wearInventoryCategory(cat, TRUE, append);
+ }
+}
+
LLContextMenu* LLInventoryGalleryContextMenu::createMenu()
{
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
@@ -57,11 +94,33 @@ LLContextMenu* LLInventoryGalleryContextMenu::createMenu()
registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2));
registrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH));
registrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND));
+ registrar.add("Inventory.DoCreate", [this](LLUICtrl*, const LLSD& data)
+ {
+ if (mRootFolder)
+ {
+ mGallery->doCreate(mGallery->getRootFolder(), data);
+ }
+ else
+ {
+ mGallery->doCreate(mUUIDs.front(), data);
+ }
+ });
std::set<LLUUID> uuids(mUUIDs.begin(), mUUIDs.end());
registrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, uuids, gFloaterView->getParentFloater(mGallery)));
enable_registrar.add("Inventory.CanSetUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::canSetUploadLocation, this, _2));
+ enable_registrar.add("Inventory.EnvironmentEnabled", [](LLUICtrl*, const LLSD&)
+ {
+ return LLEnvironment::instance().isInventoryEnabled();
+ });
+ enable_registrar.add("Inventory.MaterialsEnabled", [](LLUICtrl*, const LLSD&)
+ {
+ std::string agent_url = gAgent.getRegionCapability("UpdateMaterialAgentInventory");
+ std::string task_url = gAgent.getRegionCapability("UpdateMaterialTaskInventory");
+
+ return (!agent_url.empty() && !task_url.empty());
+ });
LLContextMenu* menu = createFromFile("menu_gallery_inventory.xml");
@@ -186,6 +245,22 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata)
{
ungroup_folder_items(mUUIDs.front());
}
+ else if ("replaceoutfit" == action)
+ {
+ modify_outfit(FALSE, mUUIDs.front(), &gInventory);
+ }
+ else if ("addtooutfit" == action)
+ {
+ modify_outfit(TRUE, mUUIDs.front(), &gInventory);
+ }
+ else if ("removefromoutfit" == action)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(mUUIDs.front());
+ if (cat)
+ {
+ LLAppearanceMgr::instance().takeOffOutfit(cat->getLinkedUUID());
+ }
+ }
else if ("take_off" == action || "detach" == action)
{
for (LLUUID& selected_id : mUUIDs)
@@ -295,6 +370,54 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata)
preview_texture->saveAs();
}
}
+ else if (("copy_to_marketplace_listings" == action)
+ || ("move_to_marketplace_listings" == action))
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(mUUIDs.front());
+ bool copy_operation = "copy_to_marketplace_listings" == action;
+ bool can_copy = itemp ? itemp->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) : false;
+
+
+ if (can_copy)
+ {
+ const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ if (itemp)
+ {
+ move_item_to_marketplacelistings(itemp, marketplacelistings_id, copy_operation);
+ }
+ }
+ else
+ {
+ uuid_vec_t lamdba_list = mUUIDs;
+ LLNotificationsUtil::add(
+ "ConfirmCopyToMarketplace",
+ LLSD(),
+ LLSD(),
+ [lamdba_list](const LLSD& notification, const LLSD& response)
+ {
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ // option == 0 Move no copy item(s)
+ // option == 1 Don't move no copy item(s) (leave them behind)
+ bool copy_and_move = option == 0;
+ const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+
+ // main inventory only allows one item?
+ LLViewerInventoryItem* itemp = gInventory.getItem(lamdba_list.front());
+ if (itemp)
+ {
+ if (itemp->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()))
+ {
+ move_item_to_marketplacelistings(itemp, marketplacelistings_id, true);
+ }
+ else if (copy_and_move)
+ {
+ move_item_to_marketplacelistings(itemp, marketplacelistings_id, false);
+ }
+ }
+ }
+ );
+ }
+ }
}
void LLInventoryGalleryContextMenu::rename(const LLUUID& item_id)
@@ -388,6 +511,56 @@ bool is_inbox_folder(LLUUID item_id)
return gInventory.isObjectDescendentOf(item_id, inbox_id);
}
+bool can_list_on_marketplace(const LLUUID &id)
+{
+ const LLInventoryObject* obj = gInventory.getObject(id);
+ bool can_list = (obj != NULL);
+
+ if (can_list)
+ {
+ const LLUUID& object_id = obj->getLinkedUUID();
+ can_list = object_id.notNull();
+
+ if (can_list)
+ {
+ std::string error_msg;
+ const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ if (marketplacelistings_id.notNull())
+ {
+ LLViewerInventoryCategory* master_folder = gInventory.getCategory(marketplacelistings_id);
+ LLInventoryCategory* cat = gInventory.getCategory(id);
+ if (cat)
+ {
+ can_list = can_move_folder_to_marketplace(master_folder, master_folder, cat, error_msg);
+ }
+ else
+ {
+ LLInventoryItem* item = gInventory.getItem(id);
+ can_list = (item ? can_move_item_to_marketplace(master_folder, master_folder, item, error_msg) : false);
+ }
+ }
+ else
+ {
+ can_list = false;
+ }
+ }
+ }
+
+ return can_list;
+}
+
+bool check_folder_for_contents_of_type(const LLUUID &id, LLInventoryModel* model, LLInventoryCollectFunctor& is_type)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ model->collectDescendentsIf(id,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_type);
+ return item_array.size() > 0;
+}
+
void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu)
{
LLUUID selected_id = mUUIDs.front();
@@ -409,6 +582,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
bool is_in_trash = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH));
bool is_lost_and_found = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
bool is_outfits= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
+ bool is_in_favorites = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE));
//bool is_favorites= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE));
bool is_system_folder = false;
@@ -456,6 +630,49 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
items.push_back(std::string("open_in_new_window"));
items.push_back(std::string("Open Folder Separator"));
}
+
+ // wearables related functionality for folders.
+ LLFindWearables is_wearable;
+ LLIsType is_object(LLAssetType::AT_OBJECT);
+ LLIsType is_gesture(LLAssetType::AT_GESTURE);
+
+ if (check_folder_for_contents_of_type(selected_id, &gInventory, is_wearable)
+ || check_folder_for_contents_of_type(selected_id, &gInventory, is_object)
+ || check_folder_for_contents_of_type(selected_id, &gInventory, is_gesture))
+ {
+ // Only enable add/replace outfit for non-system folders.
+ if (!is_system_folder)
+ {
+ // Adding an outfit onto another (versus replacing) doesn't make sense.
+ if (folder_type != LLFolderType::FT_OUTFIT)
+ {
+ items.push_back(std::string("Add To Outfit"));
+ if (!LLAppearanceMgr::instance().getCanAddToCOF(selected_id))
+ {
+ disabled_items.push_back(std::string("Add To Outfit"));
+ }
+ }
+
+ items.push_back(std::string("Replace Outfit"));
+ if (!LLAppearanceMgr::instance().getCanReplaceCOF(selected_id))
+ {
+ disabled_items.push_back(std::string("Replace Outfit"));
+ }
+ }
+ if (is_agent_inventory)
+ {
+ items.push_back(std::string("Folder Wearables Separator"));
+ // Note: If user tries to unwear "My Inventory", it's going to deactivate everything including gestures
+ // Might be safer to disable this for "My Inventory"
+ items.push_back(std::string("Remove From Outfit"));
+ if (folder_type != LLFolderType::FT_ROOT_INVENTORY // Unless COF is empty, whih shouldn't be, warrantied to have worn items
+ && !LLAppearanceMgr::getCanRemoveFromCOF(selected_id)) // expensive from root!
+ {
+ disabled_items.push_back(std::string("Remove From Outfit"));
+ }
+ }
+ items.push_back(std::string("Outfit Separator"));
+ }
}
else
{
@@ -503,11 +720,30 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
}
else
{
+ if (is_agent_inventory && !is_inbox && !is_cof && !is_in_favorites && !is_outfits)
+ {
+ LLViewerInventoryCategory* category = gInventory.getCategory(selected_id);
+ if (!category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(category))
+ {
+ items.push_back(std::string("New Folder"));
+ }
+
+ items.push_back(std::string("create_new"));
+ items.push_back(std::string("New Script"));
+ items.push_back(std::string("New Note"));
+ items.push_back(std::string("New Gesture"));
+ items.push_back(std::string("New Material"));
+ items.push_back(std::string("New Clothes"));
+ items.push_back(std::string("New Body Parts"));
+ items.push_back(std::string("New Settings"));
+ }
+
if(can_share_item(selected_id))
{
items.push_back(std::string("Share"));
}
- if (LLClipboard::instance().hasContents() && is_agent_inventory && !is_cof && !is_inbox_folder(selected_id))
+
+ if (LLClipboard::instance().hasContents() && is_agent_inventory && !is_cof && !is_inbox)
{
items.push_back(std::string("Paste"));
@@ -519,7 +755,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
}
if (is_folder && is_agent_inventory)
{
- if (!is_cof && (folder_type != LLFolderType::FT_OUTFIT) && !is_outfits && !is_inbox_folder(selected_id))
+ if (!is_cof && (folder_type != LLFolderType::FT_OUTFIT) && !is_outfits && !is_inbox)
{
if (!gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD)) && !isRootFolder())
{
@@ -706,6 +942,54 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
disabled_items.push_back(std::string("New Folder"));
disabled_items.push_back(std::string("upload_def"));
+ disabled_items.push_back(std::string("create_new"));
+ }
+
+ if (is_agent_inventory && !mRootFolder)
+ {
+ items.push_back(std::string("New folder from selected"));
+ items.push_back(std::string("Subfolder Separator"));
+ if (!is_only_items_selected(mUUIDs) && !is_only_cats_selected(mUUIDs))
+ {
+ disabled_items.push_back(std::string("New folder from selected"));
+ }
+ }
+
+ // Marketplace
+ bool can_list = false;
+ const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
+ if (marketplacelistings_id.notNull() && !is_inbox && !obj->getIsLinkType())
+ {
+ if (is_folder)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id);
+ if (cat
+ && !LLFolderType::lookupIsProtectedType(cat->getPreferredType())
+ && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID()))
+ {
+ can_list = true;
+ }
+ }
+ else if (selected_item
+ && selected_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())
+ && selected_item->getPermissions().getOwner() != ALEXANDRIA_LINDEN_ID
+ && LLAssetType::AT_CALLINGCARD != selected_item->getType())
+ {
+ can_list = true;
+ }
+ }
+
+ if (can_list)
+ {
+ items.push_back(std::string("Marketplace Separator"));
+ items.push_back(std::string("Marketplace Copy"));
+ items.push_back(std::string("Marketplace Move"));
+
+ if (!can_list_on_marketplace(selected_id))
+ {
+ disabled_items.push_back(std::string("Marketplace Copy"));
+ disabled_items.push_back(std::string("Marketplace Move"));
+ }
}
}
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index ff33efe4aa..e3d887d943 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -39,11 +39,6 @@ LLProfileDropTarget::LLProfileDropTarget(const LLProfileDropTarget::Params& p)
mAgentID(p.agent_id)
{}
-void LLProfileDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data)
-{
- LL_INFOS() << "LLProfileDropTarget::doDrop()" << LL_ENDL;
-}
-
BOOL LLProfileDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h
index f182660c8e..bfde921df0 100644
--- a/indra/newview/llpanelavatar.h
+++ b/indra/newview/llpanelavatar.h
@@ -59,8 +59,6 @@ public:
LLProfileDropTarget(const Params&);
~LLProfileDropTarget() {}
- void doDrop(EDragAndDropType cargo_type, void* cargo_data);
-
//
// LLView functionality
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp
index 82f880c9ee..54121c2656 100644
--- a/indra/newview/llpanelgroupnotices.cpp
+++ b/indra/newview/llpanelgroupnotices.cpp
@@ -89,8 +89,6 @@ public:
LLGroupDropTarget(const Params&);
~LLGroupDropTarget() {};
- void doDrop(EDragAndDropType cargo_type, void* cargo_data);
-
//
// LLView functionality
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
@@ -114,11 +112,6 @@ LLGroupDropTarget::LLGroupDropTarget(const LLGroupDropTarget::Params& p)
mGroupID(p.group_id)
{}
-void LLGroupDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data)
-{
- LL_INFOS() << "LLGroupDropTarget::doDrop()" << LL_ENDL;
-}
-
BOOL LLGroupDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 20241aac24..a5a768776a 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -56,6 +56,7 @@
#include "lltrans.h"
#include "llviewermenu.h"
#include "llviewertexturelist.h"
+#include "llviewerinventory.h"
#include "llsidepanelinventory.h"
#include "llfolderview.h"
#include "llradiogroup.h"
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index ffbed778c1..00fa3853f7 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -46,6 +46,7 @@
#include "lltexteditor.h"
#include "lltexturectrl.h"
#include "lltoggleablemenu.h"
+#include "lltooldraganddrop.h"
#include "llgrouplist.h"
#include "llurlaction.h"
@@ -1012,6 +1013,51 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key)
mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2));
}
+
+BOOL LLPanelProfileSecondLife::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Try children first
+ if (LLPanelProfileTab::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg)
+ && *accept != ACCEPT_NO)
+ {
+ return TRUE;
+ }
+
+ // No point sharing with own profile
+ if (getSelfProfile())
+ {
+ return FALSE;
+ }
+
+ // Exclude fields that look like they are editable.
+ S32 child_x = 0;
+ S32 child_y = 0;
+ if (localPointToOtherView(x, y, &child_x, &child_y, mDescriptionEdit)
+ && mDescriptionEdit->pointInView(child_x, child_y))
+ {
+ return FALSE;
+ }
+
+ if (localPointToOtherView(x, y, &child_x, &child_y, mGroupList)
+ && mGroupList->pointInView(child_x, child_y))
+ {
+ return FALSE;
+ }
+
+ // Share
+ LLToolDragAndDrop::handleGiveDragAndDrop(getAvatarId(),
+ LLUUID::null,
+ drop,
+ cargo_type,
+ cargo_data,
+ accept);
+ return TRUE;
+}
+
void LLPanelProfileSecondLife::updateData()
{
LLUUID avatar_id = getAvatarId();
diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h
index 11632a10ae..45655cd67b 100644
--- a/indra/newview/llpanelprofile.h
+++ b/indra/newview/llpanelprofile.h
@@ -78,6 +78,12 @@ public:
void onOpen(const LLSD& key) override;
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg) override;
+
/**
* LLFriendObserver trigger
*/
diff --git a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
index d82c453e5f..feaba45cf8 100644
--- a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml
@@ -426,6 +426,234 @@
<menu_item_separator
layout="topleft"
name="Subfolder Separator" />
+ <menu
+ label="Create new"
+ layout="topleft"
+ name="create_new">
+ <menu_item_call
+ label="New Folder"
+ layout="topleft"
+ name="New Folder">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="category" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Script"
+ layout="topleft"
+ name="New Script">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="lsl" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Notecard"
+ layout="topleft"
+ name="New Note">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="notecard" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Gesture"
+ layout="topleft"
+ name="New Gesture">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="gesture" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Material"
+ layout="topleft"
+ name="New Material">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="material" />
+ <menu_item_call.on_enable
+ function="Inventory.MaterialsEnabled" />
+ </menu_item_call>
+ <menu
+ label="New Clothes"
+ layout="topleft"
+ name="New Clothes">
+ <menu_item_call
+ label="New Shirt"
+ layout="topleft"
+ name="New Shirt">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="shirt" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Pants"
+ layout="topleft"
+ name="New Pants">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="pants" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Shoes"
+ layout="topleft"
+ name="New Shoes">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="shoes" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Socks"
+ layout="topleft"
+ name="New Socks">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="socks" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Jacket"
+ layout="topleft"
+ name="New Jacket">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="jacket" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Skirt"
+ layout="topleft"
+ name="New Skirt">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="skirt" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Gloves"
+ layout="topleft"
+ name="New Gloves">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="gloves" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Undershirt"
+ layout="topleft"
+ name="New Undershirt">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="undershirt" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Underpants"
+ layout="topleft"
+ name="New Underpants">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="underpants" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Alpha Mask"
+ layout="topleft"
+ name="New Alpha Mask">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="alpha" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Tattoo"
+ layout="topleft"
+ name="New Tattoo">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="tattoo" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Universal"
+ layout="topleft"
+ name="New Universal">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="universal" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Physics"
+ layout="topleft"
+ name="New Physics">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="physics" />
+ </menu_item_call>
+ </menu>
+ <menu
+ label="New Body Parts"
+ layout="topleft"
+ name="New Body Parts">
+ <menu_item_call
+ label="New Shape"
+ layout="topleft"
+ name="New Shape">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="shape" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Skin"
+ layout="topleft"
+ name="New Skin">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="skin" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Hair"
+ layout="topleft"
+ name="New Hair">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="hair" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Eyes"
+ layout="topleft"
+ name="New Eyes">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="eyes" />
+ </menu_item_call>
+ </menu>
+ <menu
+ label="New Settings"
+ layout="topleft"
+ name="New Settings">
+ <menu_item_call
+ label="New Sky"
+ layout="topleft"
+ name="New Sky">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="sky"/>
+ <menu_item_call.on_enable
+ function="Inventory.EnvironmentEnabled" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Water"
+ layout="topleft"
+ name="New Water">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="water"/>
+ <menu_item_call.on_enable
+ function="Inventory.EnvironmentEnabled" />
+ </menu_item_call>
+ <menu_item_call
+ label="New Day Cycle"
+ layout="topleft"
+ name="New Day Cycle">
+ <menu_item_call.on_click
+ function="Inventory.DoCreate"
+ parameter="daycycle"/>
+ <menu_item_call.on_enable
+ function="Inventory.EnvironmentEnabled" />
+ </menu_item_call>
+ </menu>
+ </menu>
<menu_item_call
label="Create folder from selected"
layout="topleft"