Merge pull request #505 from secondlife/SL-20553

SL-20553: Save material button in build floater now depends on agent inventory rather than object inventory
meow-7.2.2
cosmic-linden 2023-11-13 10:04:50 -08:00 committed by GitHub
commit f45d26c4cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 190 additions and 156 deletions

View File

@ -1465,6 +1465,10 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)
{
mask |= LLInventoryObserver::LABEL;
}
if (old_item->getPermissions() != item->getPermissions())
{
mask |= LLInventoryObserver::INTERNAL;
}
old_item->copyViewerItem(item);
if (update_parent_on_server)
{

View File

@ -38,6 +38,7 @@
#include "llgltfmateriallist.h"
#include "llinventorymodel.h"
#include "llinventoryobserver.h"
#include "llinventoryfunctions.h"
#include "lllocalgltfmaterials.h"
#include "llnotificationsutil.h"
#include "lltexturectrl.h"
@ -1312,16 +1313,22 @@ public:
return;
}
// Name may or may not have already been applied
const bool changed_name = item->getName() != mNewName;
// create_inventory_item/copy_inventory_item don't allow presetting some permissions, fix it now
item->setPermissions(mPermissions);
item->updateServer(FALSE);
gInventory.updateItem(item);
gInventory.notifyObservers();
if (item->getName() != mNewName)
const bool changed_permissions = item->getPermissions() != mPermissions;
const bool changed = changed_name || changed_permissions;
LLSD updates;
if (changed)
{
LLSD updates;
updates["name"] = mNewName;
if (changed_name)
{
updates["name"] = mNewName;
}
if (changed_permissions)
{
updates["permissions"] = ll_create_sd_from_permissions(mPermissions);
}
update_inventory_item(inv_item_id, updates, NULL);
}
@ -1331,10 +1338,16 @@ public:
inv_item_id,
LLAssetType::AT_MATERIAL,
mAssetData,
[](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response)
[changed, updates](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response)
{
// done callback
LL_INFOS("Material") << "inventory item uploaded. item: " << item_id << " new_item_id: " << new_item_id << " response: " << response << LL_ENDL;
// *HACK: Sometimes permissions do not stick in the UI. They are correct on the server-side, though.
if (changed)
{
update_inventory_item(new_item_id, updates, NULL);
}
},
nullptr // failure callback, floater already closed
);
@ -1796,8 +1809,49 @@ void LLMaterialEditor::loadLive()
}
}
namespace
{
// Which inventory to consult for item permissions
enum class ItemSource
{
// Consult the permissions of the item in the object's inventory. If
// the item is not present, then usage of the asset is allowed.
OBJECT,
// Consult the permissions of the item in the agent's inventory. If
// the item is not present, then usage of the asset is not allowed.
AGENT
};
class LLAssetIDMatchesWithPerms : public LLInventoryCollectFunctor
{
public:
LLAssetIDMatchesWithPerms(const LLUUID& asset_id, const std::vector<PermissionBit>& ops) : mAssetID(asset_id), mOps(ops) {}
virtual ~LLAssetIDMatchesWithPerms() {}
bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
{
if (!item || item->getAssetUUID() != mAssetID)
{
return false;
}
LLPermissions item_permissions = item->getPermissions();
for (PermissionBit op : mOps)
{
if (!gAgent.allowOperation(op, item_permissions, GP_OBJECT_MANIPULATE))
{
return false;
}
}
return true;
}
protected:
LLUUID mAssetID;
std::vector<PermissionBit> mOps;
};
};
// *NOTE: permissions_out includes user preferences for new item creation (LLFloaterPerms)
bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<PermissionBit>& ops, LLPermissions& permissions_out, LLViewerInventoryItem*& item_out)
bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<PermissionBit>& ops, const ItemSource item_source, LLPermissions& permissions_out, LLViewerInventoryItem*& item_out)
{
if (!LLMaterialEditor::capabilitiesAvailable())
{
@ -1810,6 +1864,10 @@ bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<Pe
llassert(func.mIsOverride);
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func, true /*first applicable*/);
if (item_source == ItemSource::AGENT)
{
func.mObjectId = LLUUID::null;
}
LLViewerObject* selected_object = func.mObject;
if (!selected_object)
{
@ -1830,19 +1888,47 @@ bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<Pe
}
}
item_out = selected_object->getInventoryItemByAsset(func.mMaterialId);
// Look for the item to base permissions off of
item_out = nullptr;
const bool blank_material = func.mMaterialId == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
if (!blank_material)
{
LLAssetIDMatchesWithPerms item_has_perms(func.mMaterialId, ops);
if (item_source == ItemSource::OBJECT)
{
LLViewerInventoryItem* item = selected_object->getInventoryItemByAsset(func.mMaterialId);
if (item && !item_has_perms(nullptr, item))
{
return false;
}
item_out = item;
}
else
{
llassert(item_source == ItemSource::AGENT);
LLViewerInventoryCategory::cat_array_t cats;
LLViewerInventoryItem::item_array_t items;
gInventory.collectDescendentsIf(LLUUID::null,
cats,
items,
// *NOTE: PBRPickerAgentListener will need
// to be changed if checking the trash is
// disabled
LLInventoryModel::INCLUDE_TRASH,
item_has_perms);
if (items.empty())
{
return false;
}
item_out = items[0];
}
}
LLPermissions item_permissions;
if (item_out)
{
item_permissions.set(item_out->getPermissions());
for (PermissionBit op : ops)
{
if (!gAgent.allowOperation(op, item_permissions, GP_OBJECT_MANIPULATE))
{
return false;
}
}
item_permissions = item_out->getPermissions();
// Update flags for new owner
if (!item_permissions.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true))
{
@ -1895,13 +1981,24 @@ bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<Pe
// creation history when there's no material item present. In that case,
// the agent who saved the material will be considered the creator.
// -Cosmic,2023-08-07
if (item_out)
if (item_source == ItemSource::AGENT)
{
llassert(blank_material || item_out); // See comment at ItemSource::AGENT definition
permissions_out.set(item_permissions);
}
else
{
permissions_out.set(object_permissions);
llassert(item_source == ItemSource::OBJECT);
if (item_out)
{
permissions_out.set(item_permissions);
}
else
{
permissions_out.set(object_permissions);
}
}
permissions_out.accumulate(floater_perm);
@ -1913,7 +2010,7 @@ bool LLMaterialEditor::canModifyObjectsMaterial()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item_out;
return can_use_objects_material(func, std::vector({PERM_MODIFY}), permissions, item_out);
return can_use_objects_material(func, std::vector({PERM_MODIFY}), ItemSource::OBJECT, permissions, item_out);
}
bool LLMaterialEditor::canSaveObjectsMaterial()
@ -1921,7 +2018,7 @@ bool LLMaterialEditor::canSaveObjectsMaterial()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item_out;
return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), permissions, item_out);
return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), ItemSource::AGENT, permissions, item_out);
}
bool LLMaterialEditor::canClipboardObjectsMaterial()
@ -1947,7 +2044,7 @@ bool LLMaterialEditor::canClipboardObjectsMaterial()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item_out;
return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY, PERM_TRANSFER}), permissions, item_out);
return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY, PERM_TRANSFER}), ItemSource::OBJECT, permissions, item_out);
}
void LLMaterialEditor::saveObjectsMaterialAs()
@ -1955,7 +2052,7 @@ void LLMaterialEditor::saveObjectsMaterialAs()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item = nullptr;
bool allowed = can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), permissions, item);
bool allowed = can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), ItemSource::AGENT, permissions, item);
if (!allowed)
{
LL_WARNS("MaterialEditor") << "Failed to save GLTF material from object" << LL_ENDL;
@ -2050,64 +2147,11 @@ void LLMaterialEditor::saveObjectsMaterialAs(const LLGLTFMaterial* render_materi
}
else
{
if (item_id.notNull())
{
// Copy existing item from object inventory, and create new composite asset on top of it
LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onCopyObjectsMaterialAsMsgCallback, _1, _2, permissions, object_id, item_id));
}
else
{
LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2, permissions));
}
llassert(object_id.isNull()); // Case for copying item from object inventory is no longer implemented
LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2, permissions));
}
}
// static
void LLMaterialEditor::onCopyObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id)
{
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (0 != option)
{
return;
}
LLSD asset;
asset["version"] = LLGLTFMaterial::ASSET_VERSION;
asset["type"] = LLGLTFMaterial::ASSET_TYPE;
// This is the string serialized from LLGLTFMaterial::asJSON
asset["data"] = notification["payload"]["data"];
std::ostringstream str;
LLSDSerialize::serialize(asset, str, LLSDSerialize::LLSD_BINARY);
LLViewerObject* object = gObjectList.findObject(object_id);
if (!object)
{
return;
}
const LLInventoryItem* item = object->getInventoryItem(item_id);
if (!item)
{
return;
}
std::string new_name = response["message"].asString();
LLInventoryObject::correctInventoryName(new_name);
if (new_name.empty())
{
return;
}
const LLUUID destination_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL);
LLPointer<LLInventoryCallback> cb = new LLObjectsMaterialItemCallback(permissions, str.str(), new_name);
// NOTE: This should be an item copy. Saving a material to an inventory should be disabled when the associated material is no-copy.
move_or_copy_inventory_from_object(destination_id,
object_id,
item_id,
cb);
}
// static
void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions)
{

View File

@ -116,7 +116,6 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
static bool canSaveObjectsMaterial();
static bool canClipboardObjectsMaterial();
static void saveObjectsMaterialAs();
static void onCopyObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id);
static void onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions);
static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status);

View File

@ -1880,15 +1880,55 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
}
}
// One-off listener that updates the build floater UI when the agent inventory adds or removes an item
class PBRPickerAgentListener : public LLInventoryObserver
{
protected:
bool mChangePending = true;
public:
PBRPickerAgentListener() : LLInventoryObserver()
{
gInventory.addObserver(this);
}
const bool isListening()
{
return mChangePending;
}
void changed(U32 mask) override
{
if (!(mask & (ADD | REMOVE)))
{
return;
}
if (gFloaterTools)
{
gFloaterTools->dirty();
}
gInventory.removeObserver(this);
mChangePending = false;
}
~PBRPickerAgentListener() override
{
gInventory.removeObserver(this);
mChangePending = false;
LLInventoryObserver::~LLInventoryObserver();
}
};
// One-off listener that updates the build floater UI when the prim inventory updates
class PBRPickerItemListener : public LLVOInventoryListener
class PBRPickerObjectListener : public LLVOInventoryListener
{
protected:
LLViewerObject* mObjectp;
bool mChangePending = true;
public:
PBRPickerItemListener(LLViewerObject* object)
PBRPickerObjectListener(LLViewerObject* object)
: mObjectp(object)
{
registerVOInventoryListener(mObjectp, nullptr);
@ -1912,7 +1952,7 @@ public:
mChangePending = false;
}
~PBRPickerItemListener()
~PBRPickerObjectListener()
{
removeVOInventoryListener();
mChangePending = false;
@ -1931,9 +1971,9 @@ void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material,
// pbr material
LLTextureCtrl* pbr_ctrl = findChild<LLTextureCtrl>("pbr_control");
LLUUID pbr_id;
if (pbr_ctrl)
{
LLUUID pbr_id;
LLSelectedTE::getPbrMaterialId(pbr_id, identical_pbr, has_pbr_material, has_faces_without_pbr);
pbr_ctrl->setTentative(identical_pbr ? FALSE : TRUE);
@ -1956,14 +1996,25 @@ void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material,
if (objectp->isInventoryPending())
{
// Reuse the same listener when possible
if (!mInventoryListener || !mInventoryListener->isListeningFor(objectp))
if (!mVOInventoryListener || !mVOInventoryListener->isListeningFor(objectp))
{
mInventoryListener = std::make_unique<PBRPickerItemListener>(objectp);
mVOInventoryListener = std::make_unique<PBRPickerObjectListener>(objectp);
}
}
else
{
mInventoryListener = nullptr;
mVOInventoryListener = nullptr;
}
if (!identical_pbr || pbr_id.isNull() || pbr_id == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID)
{
mAgentInventoryListener = nullptr;
}
else
{
if (!mAgentInventoryListener || !mAgentInventoryListener->isListening())
{
mAgentInventoryListener = std::make_unique<PBRPickerAgentListener>();
}
}
const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled();

View File

@ -53,7 +53,8 @@ class LLMaterialID;
class LLMediaCtrl;
class LLMenuButton;
class PBRPickerItemListener;
class PBRPickerAgentListener;
class PBRPickerObjectListener;
// Represents an edit for use in replicating the op across one or more materials in the selection set.
//
@ -508,7 +509,8 @@ private:
static Selection sMaterialOverrideSelection;
std::unique_ptr<PBRPickerItemListener> mInventoryListener;
std::unique_ptr<PBRPickerAgentListener> mAgentInventoryListener;
std::unique_ptr<PBRPickerObjectListener> mVOInventoryListener;
public:
#if defined(DEF_GET_MAT_STATE)

View File

@ -1658,67 +1658,6 @@ void copy_inventory_from_notecard(const LLUUID& destination_id,
}
}
void move_or_copy_inventory_from_object(const LLUUID& destination_id,
const LLUUID& object_id,
const LLUUID& item_id,
LLPointer<LLInventoryCallback> cb)
{
LLViewerObject* object = gObjectList.findObject(object_id);
if (!object)
{
return;
}
const LLInventoryItem* item = object->getInventoryItem(item_id);
if (!item)
{
return;
}
class LLItemAddedObserver : public LLInventoryObserver
{
public:
LLItemAddedObserver(const LLUUID& copied_asset_id, LLPointer<LLInventoryCallback> cb)
: LLInventoryObserver(),
mAssetId(copied_asset_id),
mCallback(cb)
{
}
void changed(U32 mask) override
{
if((mask & (LLInventoryObserver::ADD)) == 0)
{
return;
}
for (const LLUUID& changed_id : gInventory.getChangedIDs())
{
LLViewerInventoryItem* changed_item = gInventory.getItem(changed_id);
if (changed_item->getAssetUUID() == mAssetId)
{
changeComplete(changed_item->getUUID());
return;
}
}
}
private:
void changeComplete(const LLUUID& item_id)
{
mCallback->fire(item_id);
gInventory.removeObserver(this);
delete this;
}
LLUUID mAssetId;
LLPointer<LLInventoryCallback> mCallback;
};
const LLUUID& asset_id = item->getAssetUUID();
LLItemAddedObserver* observer = new LLItemAddedObserver(asset_id, cb);
gInventory.addObserver(observer);
object->moveInventory(destination_id, item_id);
}
void create_new_item(const std::string& name,
const LLUUID& parent_id,
LLAssetType::EType asset_type,

View File

@ -463,11 +463,6 @@ void copy_inventory_from_notecard(const LLUUID& destination_id,
const LLInventoryItem *src,
U32 callback_id = 0);
void move_or_copy_inventory_from_object(const LLUUID& destination_id,
const LLUUID& object_id,
const LLUUID& item_id,
LLPointer<LLInventoryCallback> cb);
void menu_create_inventory_item(LLInventoryPanel* root,
LLFolderBridge* bridge,
const LLSD& userdata,