SL-19832 Inventory gallery should support CTRL+X, C and V

master
Andrey Kleshchev 2023-06-08 22:50:12 +03:00
parent 29f52ad1a7
commit 53fc42fe03
3 changed files with 254 additions and 28 deletions

View File

@ -29,6 +29,7 @@
#include "llinventorygallery.h"
#include "llinventorygallerymenu.h"
#include "llclipboard.h"
#include "llcommonutils.h"
#include "lliconctrl.h"
#include "llinventorybridge.h"
@ -133,6 +134,11 @@ BOOL LLInventoryGallery::postBuild()
LLInventoryGallery::~LLInventoryGallery()
{
if (gEditMenuHandler == this)
{
gEditMenuHandler = NULL;
}
delete mInventoryGalleryMenu;
delete mRootGalleryMenu;
delete mFilter;
@ -1009,6 +1015,7 @@ void LLInventoryGallery::moveUp()
LLUUID item_id = item->getUUID();
changeItemSelection(item_id, true);
item->setFocus(TRUE);
claimEditHandler();
}
}
}
@ -1031,6 +1038,7 @@ void LLInventoryGallery::moveDown()
LLUUID item_id = item->getUUID();
changeItemSelection(item_id, true);
item->setFocus(TRUE);
claimEditHandler();
}
}
}
@ -1056,6 +1064,7 @@ void LLInventoryGallery::moveLeft()
LLUUID item_id = item->getUUID();
changeItemSelection(item_id, true);
item->setFocus(TRUE);
claimEditHandler();
}
}
}
@ -1079,6 +1088,7 @@ void LLInventoryGallery::moveRight()
LLUUID item_id = item->getUUID();
changeItemSelection(item_id, true);
item->setFocus(TRUE);
claimEditHandler();
}
}
}
@ -1154,6 +1164,227 @@ LLInventoryGalleryItem* LLInventoryGallery::getSelectedItem()
return mItemMap[mSelectedItemID];
}
void LLInventoryGallery::copy()
{
LLClipboard::instance().reset();
if (getVisible() && getEnabled() && mSelectedItemID.notNull())
{
LLClipboard::instance().addToClipboard(mSelectedItemID);
}
mFilterSubString.clear();
}
BOOL LLInventoryGallery::canCopy() const
{
if (!getVisible() || !getEnabled() || mSelectedItemID.isNull())
{
return FALSE;
}
if (!isItemCopyable(mSelectedItemID))
{
return FALSE;
}
return TRUE;
}
void LLInventoryGallery::cut()
{
// clear the inventory clipboard
LLClipboard::instance().reset();
if (getVisible() && getEnabled() && mSelectedItemID.notNull())
{
// todo: fade out selected item
LLClipboard::instance().setCutMode(true);
LLClipboard::instance().addToClipboard(mSelectedItemID);
}
mFilterSubString.clear();
}
BOOL LLInventoryGallery::canCut() const
{
if (!getVisible() || !getEnabled() || mSelectedItemID.isNull())
{
return FALSE;
}
LLViewerInventoryCategory* cat = gInventory.getCategory(mSelectedItemID);
if (cat)
{
if (!get_is_category_removable(&gInventory, mSelectedItemID))
{
return FALSE;
}
}
else if (!get_is_item_removable(&gInventory, mSelectedItemID))
{
return FALSE;
}
return TRUE;
}
void LLInventoryGallery::paste()
{
const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
if (gInventory.isObjectDescendentOf(mSelectedItemID, marketplacelistings_id))
{
return;
}
LLInventoryObject* obj = gInventory.getObject(mSelectedItemID);
bool is_folder = obj && (obj->getType() == LLAssetType::AT_CATEGORY);
LLUUID dest = is_folder ? mSelectedItemID : mFolderID;
bool is_cut_mode = LLClipboard::instance().isCutMode();
std::vector<LLUUID> objects;
LLClipboard::instance().pasteFromClipboard(objects);
for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter)
{
const LLUUID& item_id = (*iter);
if (gInventory.isObjectDescendentOf(item_id, marketplacelistings_id) && (LLMarketplaceData::instance().isInActiveFolder(item_id) ||
LLMarketplaceData::instance().isListedAndActive(item_id)))
{
return;
}
LLViewerInventoryCategory* cat = gInventory.getCategory(item_id);
if (cat)
{
if (is_cut_mode)
{
gInventory.changeCategoryParent(cat, dest, false);
}
else
{
copy_inventory_category(&gInventory, cat, dest);
}
}
else
{
LLViewerInventoryItem* item = gInventory.getItem(item_id);
if (item)
{
if (is_cut_mode)
{
gInventory.changeItemParent(item, dest, false);
}
else
{
if (item->getIsLinkType())
{
link_inventory_object(dest, item_id,
LLPointer<LLInventoryCallback>(NULL));
}
else
{
copy_inventory_item(
gAgent.getID(),
item->getPermissions().getOwner(),
item->getUUID(),
dest,
std::string(),
LLPointer<LLInventoryCallback>(NULL));
}
}
}
}
}
// todo: scroll to item on arrival
LLClipboard::instance().setCutMode(false);
}
BOOL LLInventoryGallery::canPaste() const
{
// Return FALSE on degenerated cases: empty clipboard, no inventory, no agent
if (!LLClipboard::instance().hasContents())
{
return FALSE;
}
// In cut mode, whatever is on the clipboard is always pastable
if (LLClipboard::instance().isCutMode())
{
return TRUE;
}
// In normal mode, we need to check each element of the clipboard to know if we can paste or not
std::vector<LLUUID> objects;
LLClipboard::instance().pasteFromClipboard(objects);
S32 count = objects.size();
for (S32 i = 0; i < count; i++)
{
const LLUUID& item_id = objects.at(i);
// Each item must be copyable to be pastable
if (!isItemCopyable(item_id))
{
return FALSE;
}
}
return TRUE;
}
void LLInventoryGallery::claimEditHandler()
{
gEditMenuHandler = this;
}
bool LLInventoryGallery::isItemCopyable(const LLUUID & item_id)
{
const LLInventoryCategory* cat = gInventory.getCategory(item_id);
if (cat)
{
// Folders are copyable if items in them are, recursively, copyable.
// Get the content of the folder
LLInventoryModel::cat_array_t* cat_array;
LLInventoryModel::item_array_t* item_array;
gInventory.getDirectDescendentsOf(item_id, cat_array, item_array);
// Check the items
LLInventoryModel::item_array_t item_array_copy = *item_array;
for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
{
LLInventoryItem* item = *iter;
if (!isItemCopyable(item->getUUID()))
{
return false;
}
}
// Check the folders
LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
{
LLViewerInventoryCategory* category = *iter;
if (!isItemCopyable(category->getUUID()))
{
return false;
}
}
return true;
}
LLViewerInventoryItem* item = gInventory.getItem(item_id);
if (item)
{
// Can't copy worn objects.
// Worn objects are tied to their inworld conterparts
// Copy of modified worn object will return object with obsolete asset and inventory
if (get_is_item_worn(item_id))
{
return false;
}
static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
return (item->getIsLinkType() && inventory_linking)
|| item->getPermissions().allowCopyBy(gAgent.getID());
}
return false;
}
void LLInventoryGallery::updateMessageVisibility()
{
@ -1666,6 +1897,7 @@ void LLInventoryGalleryItem::setSelected(bool value)
BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask)
{
setFocus(TRUE);
mGallery->claimEditHandler();
gFocusMgr.setMouseCapture(this);
S32 screen_x;
@ -1678,6 +1910,8 @@ BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLInventoryGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
setFocus(TRUE);
mGallery->claimEditHandler();
return LLUICtrl::handleRightMouseDown(x, y, mask);
}
@ -1763,7 +1997,6 @@ BOOL LLInventoryGalleryItem::handleKeyHere(KEY key, MASK mask)
BOOL handled = FALSE;
switch (key)
{
case KEY_LEFT:
mGallery->moveLeft();
handled = true;

View File

@ -45,7 +45,7 @@ class LLInventoryGalleryContextMenu;
typedef boost::function<void()> callback_t;
class LLInventoryGallery : public LLPanel
class LLInventoryGallery : public LLPanel, public LLEditMenuHandler
{
public:
@ -139,6 +139,19 @@ public:
LLScrollContainer* getScrollableContainer() { return mScrollPanel; }
LLInventoryGalleryItem* getSelectedItem();
// Copy & paste (LLEditMenuHandler)
void copy() override;
BOOL canCopy() const override;
void cut() override;
BOOL canCut() const override;
void paste() override;
BOOL canPaste() const override;
void claimEditHandler();
static bool isItemCopyable(const LLUUID & item_id);
protected:
void showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id);

View File

@ -134,20 +134,10 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU
}
else if ("cut" == action)
{
bool allow = false;
if(is_folder)
{
allow = get_is_category_removable(&gInventory, selected_id);
}
else
{
allow = get_is_item_removable(&gInventory, selected_id);
}
if(allow)
{
LLClipboard::instance().setCutMode(true);
LLClipboard::instance().addToClipboard(selected_id);
}
if (mGallery->canCut())
{
mGallery->cut();
}
}
else if ("paste" == action)
{
@ -227,19 +217,9 @@ void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata, const LLU
}
else if ("copy" == action)
{
if(is_folder)
if (mGallery->canCopy())
{
LLClipboard::instance().reset();
LLClipboard::instance().addToClipboard(selected_id);
}
else
{
LLViewerInventoryItem* inv_item = gInventory.getItem(selected_id);
if (inv_item && inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) && !get_is_item_worn(selected_id))
{
LLClipboard::instance().reset();
LLClipboard::instance().addToClipboard(selected_id);
}
mGallery->copy();
}
}
else if ("paste_link" == action)