/** * @file llsidepanelinventory.cpp * @brief LLPanelObjectInventory class implementation * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ //***************************************************************************** // // Implementation of the panel inventory - used to view and control a // task's inventory. // //***************************************************************************** #include "llviewerprecompiledheaders.h" #include "llpanelobjectinventory.h" #include "llmenugl.h" #include "llnotificationsutil.h" #include "roles_constants.h" #include "llagent.h" #include "llavataractions.h" #include "llcallbacklist.h" #include "llbuycurrencyhtml.h" #include "llfloaterreg.h" #include "llfolderview.h" #include "llinventorybridge.h" #include "llinventorydefines.h" #include "llinventoryicon.h" #include "llinventoryfilter.h" #include "llinventoryfunctions.h" #include "llmaterialeditor.h" #include "llpreviewanim.h" #include "llpreviewgesture.h" #include "llpreviewnotecard.h" #include "llpreviewscript.h" #include "llpreviewsound.h" #include "llpreviewtexture.h" #include "llscrollcontainer.h" #include "llselectmgr.h" #include "llstatusbar.h" #include "lltooldraganddrop.h" #include "lltrans.h" #include "llviewerassettype.h" #include "llviewerinventory.h" #include "llviewerregion.h" #include "llviewerobjectlist.h" #include "llviewermessage.h" const LLColor4U DEFAULT_WHITE(255, 255, 255); ///---------------------------------------------------------------------------- /// Class LLTaskInvFVBridge ///---------------------------------------------------------------------------- class LLTaskInvFVBridge : public LLFolderViewModelItemInventory { protected: LLUUID mUUID; std::string mName; mutable std::string mDisplayName; mutable std::string mSearchableName; LLPanelObjectInventory* mPanel; U32 mFlags; LLAssetType::EType mAssetType; LLInventoryType::EType mInventoryType; LLInventoryObject* findInvObject() const; LLInventoryItem* findItem() const; public: LLTaskInvFVBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, U32 flags=0); virtual ~LLTaskInvFVBridge() {} virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } static LLTaskInvFVBridge* createObjectBridge(LLPanelObjectInventory* panel, LLInventoryObject* object); void showProperties(); S32 getPrice(); // LLFolderViewModelItemInventory functionality virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; virtual const std::string& getSearchableName() const; virtual std::string getSearchableDescription() const {return LLStringUtil::null;} virtual std::string getSearchableCreatorName() const {return LLStringUtil::null;} virtual std::string getSearchableUUIDString() const {return LLStringUtil::null;} virtual PermissionMask getPermissionMask() const { return PERM_NONE; } /*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; } virtual const LLUUID& getUUID() const { return mUUID; } virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null;} virtual time_t getCreationDate() const; virtual void setCreationDate(time_t creation_date_utc); virtual LLUIImagePtr getIcon() const; virtual void openItem(); virtual bool canOpenItem() const { return false; } virtual void closeItem() {} virtual void selectItem() {} virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {} virtual bool isItemRenameable() const; virtual bool renameItem(const std::string& new_name); virtual bool isItemMovable() const; virtual bool isItemRemovable(bool check_worn = true) const; virtual bool removeItem(); virtual void removeBatch(std::vector& batch); virtual void move(LLFolderViewModelItem* parent_listener); virtual bool isItemCopyable(bool can_copy_as_link = true) const; virtual bool copyToClipboard() const; virtual bool cutToClipboard(); virtual bool isClipboardPasteable() const; virtual void pasteFromClipboard(); virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLInventoryModel* model, std::string action); virtual bool isUpToDate() const { return true; } virtual bool hasChildren() const { return false; } virtual LLInventoryType::EType getInventoryType() const { return LLInventoryType::IT_NONE; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } virtual LLSettingsType::type_e getSettingsType() const { return LLSettingsType::ST_NONE; } virtual EInventorySortGroup getSortGroup() const { return SG_ITEM; } virtual LLInventoryObject* getInventoryObject() const { return findInvObject(); } // LLDragAndDropBridge functionality virtual LLToolDragAndDrop::ESource getDragSource() const { return LLToolDragAndDrop::SOURCE_WORLD; } virtual bool startDrag(EDragAndDropType* type, LLUUID* id) const; virtual bool dragOrDrop(MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, std::string& tooltip_msg); }; LLTaskInvFVBridge::LLTaskInvFVBridge( LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, U32 flags) : LLFolderViewModelItemInventory(panel->getRootViewModel()), mUUID(uuid), mName(name), mPanel(panel), mFlags(flags), mAssetType(LLAssetType::AT_NONE), mInventoryType(LLInventoryType::IT_NONE) { if (const LLInventoryItem* item = findItem()) { mAssetType = item->getType(); mInventoryType = item->getInventoryType(); } } LLInventoryObject* LLTaskInvFVBridge::findInvObject() const { const LLUUID& id = mPanel->getTaskUUID(); if (LLViewerObject* object = gObjectList.findObject(id)) { return object->getInventoryObject(mUUID); } return NULL; } LLInventoryItem* LLTaskInvFVBridge::findItem() const { return dynamic_cast(findInvObject()); } void LLTaskInvFVBridge::showProperties() { show_task_item_profile(mUUID, mPanel->getTaskUUID()); } S32 LLTaskInvFVBridge::getPrice() { if (LLInventoryItem* item = findItem()) { return item->getSaleInfo().getSalePrice(); } return -1; } // virtual const std::string& LLTaskInvFVBridge::getName() const { return mName; } // virtual const std::string& LLTaskInvFVBridge::getDisplayName() const { if (LLInventoryItem* item = findItem()) { mDisplayName.assign(item->getName()); // Localize "New Script", "New Script 1", "New Script 2", etc. if (item->getType() == LLAssetType::AT_LSL_TEXT && LLStringUtil::startsWith(item->getName(), "New Script")) { LLStringUtil::replaceString(mDisplayName, "New Script", LLTrans::getString("PanelContentsNewScript")); } const LLPermissions& perm(item->getPermissions()); bool copy = gAgent.allowOperation(PERM_COPY, perm, GP_OBJECT_MANIPULATE); bool mod = gAgent.allowOperation(PERM_MODIFY, perm, GP_OBJECT_MANIPULATE); bool xfer = gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE); if (!copy) { mDisplayName.append(LLTrans::getString("no_copy")); } if (!mod) { mDisplayName.append(LLTrans::getString("no_modify")); } if (!xfer) { mDisplayName.append(LLTrans::getString("no_transfer")); } } mSearchableName.assign(mDisplayName + getLabelSuffix()); LLStringUtil::toUpper(mSearchableName); return mDisplayName; } // virtual const std::string& LLTaskInvFVBridge::getSearchableName() const { return mSearchableName; } // BUG: No creation dates for task inventory time_t LLTaskInvFVBridge::getCreationDate() const { return 0; } void LLTaskInvFVBridge::setCreationDate(time_t creation_date_utc) { } LLUIImagePtr LLTaskInvFVBridge::getIcon() const { const bool item_is_multi = mFlags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS; return LLInventoryIcon::getIcon(mAssetType, mInventoryType, 0, item_is_multi); } void LLTaskInvFVBridge::openItem() { // no-op. LL_DEBUGS() << "LLTaskInvFVBridge::openItem()" << LL_ENDL; } bool LLTaskInvFVBridge::isItemRenameable() const { if (gAgent.isGodlike()) return true; if (LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID())) { if (LLInventoryItem* item = (LLInventoryItem*)(object->getInventoryObject(mUUID))) { if (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE, GOD_LIKE)) { return true; } } } return false; } bool LLTaskInvFVBridge::renameItem(const std::string& new_name) { if (LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID())) { if (LLViewerInventoryItem* item = (LLViewerInventoryItem*)object->getInventoryObject(mUUID)) { if (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE, GOD_LIKE)) { LLPointer new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); object->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, false); } } } return true; } bool LLTaskInvFVBridge::isItemMovable() const { //LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); //if(object && (object->permModify() || gAgent.isGodlike())) //{ // return true; //} //return false; return true; } bool LLTaskInvFVBridge::isItemRemovable(bool check_worn) const { if (const LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID())) { return object->permModify() || object->permYouOwner(); } return false; } bool remove_task_inventory_callback(const LLSD& notification, const LLSD& response, LLPanelObjectInventory* panel) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0) { if (LLViewerObject* object = gObjectList.findObject(notification["payload"]["task_id"].asUUID())) { // yes const LLSD& inventory_ids = notification["payload"]["inventory_ids"]; LLSD::array_const_iterator list_it = inventory_ids.beginArray(); LLSD::array_const_iterator list_end = inventory_ids.endArray(); for (; list_it != list_end; ++list_it) { object->removeInventory(list_it->asUUID()); } // refresh the UI. panel->refresh(); } } return false; } // helper for remove // ! REFACTOR ! two_uuids_list_t is also defined in llinventorybridge.h, but differently. typedef std::pair > panel_two_uuids_list_t; typedef std::pair remove_data_t; bool LLTaskInvFVBridge::removeItem() { if (isItemRemovable() && mPanel) { if (LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID())) { if (object->permModify()) { // just do it. object->removeInventory(mUUID); return true; } LLSD payload; payload["task_id"] = mPanel->getTaskUUID(); payload["inventory_ids"].append(mUUID); LLNotificationsUtil::add("RemoveItemWarn", LLSD(), payload, boost::bind(&remove_task_inventory_callback, _1, _2, mPanel)); return false; } } return false; } void LLTaskInvFVBridge::removeBatch(std::vector& batch) { if (!mPanel) { return; } LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if (!object) { return; } if (!object->permModify()) { LLSD payload; payload["task_id"] = mPanel->getTaskUUID(); for (LLFolderViewModelItem* item : batch) { payload["inventory_ids"].append(((LLTaskInvFVBridge*)item)->getUUID()); } LLNotificationsUtil::add("RemoveItemWarn", LLSD(), payload, boost::bind(&remove_task_inventory_callback, _1, _2, mPanel)); } else { for (LLFolderViewModelItem* item : batch) { LLTaskInvFVBridge* bridge = (LLTaskInvFVBridge*)item; if (bridge->isItemRemovable()) { // Just do it. object->removeInventory(bridge->getUUID()); } } } } void LLTaskInvFVBridge::move(LLFolderViewModelItem* parent_listener) { } bool LLTaskInvFVBridge::isItemCopyable(bool can_link) const { if (LLInventoryItem* item = findItem()) { return gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE); } return false; } bool LLTaskInvFVBridge::copyToClipboard() const { return false; } bool LLTaskInvFVBridge::cutToClipboard() { return false; } bool LLTaskInvFVBridge::isClipboardPasteable() const { return false; } void LLTaskInvFVBridge::pasteFromClipboard() { } void LLTaskInvFVBridge::pasteLinkFromClipboard() { } bool LLTaskInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const { //LL_INFOS() << "LLTaskInvFVBridge::startDrag()" << LL_ENDL; if (!mPanel) return false; if (LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID())) { if (LLInventoryItem* inv = (LLInventoryItem*)object->getInventoryObject(mUUID)) { const LLPermissions& perm = inv->getPermissions(); bool can_copy = gAgent.allowOperation(PERM_COPY, perm, GP_OBJECT_MANIPULATE); if (!can_copy && object->isAttachment()) { //RN: no copy contents of attachments cannot be dragged out // due to a race condition and possible exploit where // attached objects do not update their inventory items // when their contents are manipulated return false; } if ((can_copy && perm.allowTransferTo(gAgent.getID())) || object->permYouOwner()) // || gAgent.isGodlike()) { *type = LLViewerAssetType::lookupDragAndDropType(inv->getType()); *id = inv->getUUID(); return true; } } } return false; } bool LLTaskInvFVBridge::dragOrDrop(MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, std::string& tooltip_msg) { //LL_INFOS() << "LLTaskInvFVBridge::dragOrDrop()" << LL_ENDL; return false; } // virtual void LLTaskInvFVBridge::performAction(LLInventoryModel* model, std::string action) { if (action == "task_open") { openItem(); } else if (action == "task_properties") { showProperties(); } } void LLTaskInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { LLInventoryItem* item = findItem(); std::vector items; std::vector disabled_items; if (!item) { hide_context_entries(menu, items, disabled_items); return; } if (canOpenItem()) { items.push_back(std::string("Task Open")); } items.push_back(std::string("Task Properties")); if ((flags & FIRST_SELECTED_ITEM) == 0) { disabled_items.push_back(std::string("Task Properties")); } if (isItemRenameable()) { items.push_back(std::string("Task Rename")); if ((flags & FIRST_SELECTED_ITEM) == 0) { disabled_items.push_back(std::string("Task Rename")); } } if (isItemRemovable()) { items.push_back(std::string("Task Remove")); } hide_context_entries(menu, items, disabled_items); } ///---------------------------------------------------------------------------- /// Class LLTaskFolderBridge ///---------------------------------------------------------------------------- class LLTaskCategoryBridge : public LLTaskInvFVBridge { public: LLTaskCategoryBridge( LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name); virtual LLUIImagePtr getIcon() const; virtual const std::string& getDisplayName() const; virtual bool isItemRenameable() const { return false; } // virtual bool isItemCopyable() const { return false; } virtual bool renameItem(const std::string& new_name) { return false; } virtual bool isItemRemovable(bool check_worn = true) const { return false; } virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual bool hasChildren() const; virtual bool startDrag(EDragAndDropType* type, LLUUID* id) const; virtual bool dragOrDrop(MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, std::string& tooltip_msg); virtual bool canOpenItem() const { return true; } virtual void openItem(); virtual EInventorySortGroup getSortGroup() const { return SG_NORMAL_FOLDER; } }; LLTaskCategoryBridge::LLTaskCategoryBridge( LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) { } // virtual LLUIImagePtr LLTaskCategoryBridge::getIcon() const { return LLUI::getUIImage("Inv_FolderClosed"); } // virtual const std::string& LLTaskCategoryBridge::getDisplayName() const { if (LLInventoryObject* cat = findInvObject()) { std::string name = cat->getName(); if (mChildren.size() > 0) { // Add item count // Normally we would be using getLabelSuffix for this // but object's inventory just uses displaynames LLStringUtil::format_map_t args; args["[ITEMS_COUNT]"] = llformat("%d", mChildren.size()); name.append(" " + LLTrans::getString("InventoryItemsCount", args)); } mDisplayName.assign(name); LLStringUtil::toUpper(name); mSearchableName.assign(name); } return mDisplayName; } void LLTaskCategoryBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { std::vector items; std::vector disabled_items; hide_context_entries(menu, items, disabled_items); } // virtual bool LLTaskCategoryBridge::hasChildren() const { // return true if we have or do know know if we have children. // *FIX: For now, return false - we will know for sure soon enough. return false; } // virtual void LLTaskCategoryBridge::openItem() { } // virtual bool LLTaskCategoryBridge::startDrag(EDragAndDropType* type, LLUUID* id) const { //LL_INFOS() << "LLTaskInvFVBridge::startDrag()" << LL_ENDL; if (mPanel && mUUID.notNull()) { if (LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID())) { const LLInventoryObject* cat = object->getInventoryObject(mUUID); if (cat && move_inv_category_world_to_agent(mUUID, LLUUID::null, false)) { *type = LLViewerAssetType::lookupDragAndDropType(cat->getType()); *id = mUUID; return true; } } } return false; } // virtual bool LLTaskCategoryBridge::dragOrDrop(MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, std::string& tooltip_msg) { //LL_INFOS() << "LLTaskCategoryBridge::dragOrDrop()" << LL_ENDL; bool accept = false; LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(object) { switch(cargo_type) { case DAD_CATEGORY: accept = LLToolDragAndDrop::getInstance()->dadUpdateInventoryCategory(object,drop); break; case DAD_TEXTURE: case DAD_SOUND: case DAD_LANDMARK: case DAD_OBJECT: case DAD_NOTECARD: case DAD_CLOTHING: case DAD_BODYPART: case DAD_ANIMATION: case DAD_GESTURE: case DAD_CALLINGCARD: case DAD_MESH: case DAD_SETTINGS: case DAD_MATERIAL: accept = LLToolDragAndDrop::isInventoryDropAcceptable(object, (LLViewerInventoryItem*)cargo_data); if(accept && drop) { LLToolDragAndDrop::dropInventory(object, (LLViewerInventoryItem*)cargo_data, LLToolDragAndDrop::getInstance()->getSource(), LLToolDragAndDrop::getInstance()->getSourceID()); } break; case DAD_SCRIPT: // *HACK: In order to resolve SL-22177, we need to block // drags from notecards and objects onto other // objects. uncomment the simpler version when we have // that right. //accept = LLToolDragAndDrop::isInventoryDropAcceptable(object, (LLViewerInventoryItem*)cargo_data); if(LLToolDragAndDrop::isInventoryDropAcceptable( object, (LLViewerInventoryItem*)cargo_data) && (LLToolDragAndDrop::SOURCE_WORLD != LLToolDragAndDrop::getInstance()->getSource()) && (LLToolDragAndDrop::SOURCE_NOTECARD != LLToolDragAndDrop::getInstance()->getSource())) { accept = true; } if(accept && drop) { LLViewerInventoryItem* item = (LLViewerInventoryItem*)cargo_data; // rez in the script active by default, rez in // inactive if the control key is being held down. bool active = ((mask & MASK_CONTROL) == 0); LLToolDragAndDrop::dropScript(object, item, active, LLToolDragAndDrop::getInstance()->getSource(), LLToolDragAndDrop::getInstance()->getSourceID()); } break; default: break; } } return accept; } ///---------------------------------------------------------------------------- /// Class LLTaskTextureBridge ///---------------------------------------------------------------------------- class LLTaskTextureBridge : public LLTaskInvFVBridge { public: LLTaskTextureBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} virtual bool canOpenItem() const { return true; } virtual void openItem(); }; // virtual void LLTaskTextureBridge::openItem() { LL_INFOS() << "LLTaskTextureBridge::openItem()" << LL_ENDL; if (LLPreviewTexture* preview = LLFloaterReg::showTypedInstance("preview_texture", LLSD(mUUID), TAKE_FOCUS_YES)) { if (LLInventoryItem* item = findItem()) { preview->setAuxItem(item); } preview->setObjectID(mPanel->getTaskUUID()); } } ///---------------------------------------------------------------------------- /// Class LLTaskSoundBridge ///---------------------------------------------------------------------------- class LLTaskSoundBridge : public LLTaskInvFVBridge { public: LLTaskSoundBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} virtual bool canOpenItem() const { return true; } virtual void openItem(); virtual void performAction(LLInventoryModel* model, std::string action); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); static void openSoundPreview(void* data); }; // virtual void LLTaskSoundBridge::openItem() { openSoundPreview((void*)this); } // static void LLTaskSoundBridge::openSoundPreview(void* data) { LLTaskSoundBridge* self = (LLTaskSoundBridge*)data; if(!self) return; LLPreviewSound* preview = LLFloaterReg::showTypedInstance("preview_sound", LLSD(self->mUUID), TAKE_FOCUS_YES); if (preview) { preview->setObjectID(self->mPanel->getTaskUUID()); } } // virtual void LLTaskSoundBridge::performAction(LLInventoryModel* model, std::string action) { if (action == "task_play") { LLInventoryItem* item = findItem(); if(item) { send_sound_trigger(item->getAssetUUID(), 1.0); } } LLTaskInvFVBridge::performAction(model, action); } // virtual void LLTaskSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { LLInventoryItem* item = findItem(); std::vector items; std::vector disabled_items; if (!item) { hide_context_entries(menu, items, disabled_items); return; } if (canOpenItem()) { if (!isItemCopyable()) { disabled_items.push_back(std::string("Task Open")); } } items.push_back(std::string("Task Properties")); if ((flags & FIRST_SELECTED_ITEM) == 0) { disabled_items.push_back(std::string("Task Properties")); } if (isItemRenameable()) { items.push_back(std::string("Task Rename")); } if (isItemRemovable()) { items.push_back(std::string("Task Remove")); } items.push_back(std::string("Task Play")); hide_context_entries(menu, items, disabled_items); } ///---------------------------------------------------------------------------- /// Class LLTaskLandmarkBridge ///---------------------------------------------------------------------------- class LLTaskLandmarkBridge : public LLTaskInvFVBridge { public: LLTaskLandmarkBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} }; ///---------------------------------------------------------------------------- /// Class LLTaskCallingCardBridge ///---------------------------------------------------------------------------- class LLTaskCallingCardBridge : public LLTaskInvFVBridge { public: LLTaskCallingCardBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} virtual bool isItemRenameable() const { return false; } virtual bool renameItem(const std::string& new_name) { return false; } }; ///---------------------------------------------------------------------------- /// Class LLTaskScriptBridge ///---------------------------------------------------------------------------- class LLTaskScriptBridge : public LLTaskInvFVBridge { public: LLTaskScriptBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} //static bool enableIfCopyable( void* userdata ); }; class LLTaskLSLBridge : public LLTaskScriptBridge { public: LLTaskLSLBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskScriptBridge(panel, uuid, name) {} virtual bool canOpenItem() const { return true; } virtual void openItem(); virtual bool removeItem(); //virtual void buildContextMenu(LLMenuGL& menu); //static void copyToInventory(void* userdata); }; // virtual void LLTaskLSLBridge::openItem() { LL_INFOS() << "LLTaskLSLBridge::openItem() " << mUUID << LL_ENDL; LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(!object || object->isInventoryPending()) { return; } if (object->permModify() || gAgent.isGodlike()) { LLSD floater_key; floater_key["taskid"] = mPanel->getTaskUUID(); floater_key["itemid"] = mUUID; LLLiveLSLEditor* preview = LLFloaterReg::showTypedInstance("preview_scriptedit", floater_key, TAKE_FOCUS_YES); if (preview) { LLSelectNode *node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(NULL, true); if (node && node->mValid) { preview->setObjectName(node->mName); } preview->setObjectID(mPanel->getTaskUUID()); } } else { LLNotificationsUtil::add("CannotOpenScriptObjectNoMod"); } } // virtual bool LLTaskLSLBridge::removeItem() { LLFloaterReg::hideInstance("preview_scriptedit", LLSD(mUUID)); return LLTaskInvFVBridge::removeItem(); } ///---------------------------------------------------------------------------- /// Class LLTaskObjectBridge ///---------------------------------------------------------------------------- class LLTaskObjectBridge : public LLTaskInvFVBridge { public: LLTaskObjectBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, U32 flags = 0) : LLTaskInvFVBridge(panel, uuid, name, flags) {} }; ///---------------------------------------------------------------------------- /// Class LLTaskNotecardBridge ///---------------------------------------------------------------------------- class LLTaskNotecardBridge : public LLTaskInvFVBridge { public: LLTaskNotecardBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} virtual bool canOpenItem() const { return true; } virtual void openItem(); virtual bool removeItem(); }; // virtual void LLTaskNotecardBridge::openItem() { LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(!object || object->isInventoryPending()) { return; } // Note: even if we are not allowed to modify copyable notecard, we should be able to view it LLInventoryItem *item = dynamic_cast(object->getInventoryObject(mUUID)); bool item_copy = item && gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE); if( item_copy || object->permModify() || gAgent.isGodlike()) { LLSD floater_key; floater_key["taskid"] = mPanel->getTaskUUID(); floater_key["itemid"] = mUUID; LLPreviewNotecard* preview = LLFloaterReg::showTypedInstance("preview_notecard", floater_key, TAKE_FOCUS_YES); if (preview) { preview->setObjectID(mPanel->getTaskUUID()); } } } // virtual bool LLTaskNotecardBridge::removeItem() { LLFloaterReg::hideInstance("preview_notecard", LLSD(mUUID)); return LLTaskInvFVBridge::removeItem(); } ///---------------------------------------------------------------------------- /// Class LLTaskGestureBridge ///---------------------------------------------------------------------------- class LLTaskGestureBridge : public LLTaskInvFVBridge { public: LLTaskGestureBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} virtual bool canOpenItem() const { return true; } virtual void openItem(); virtual bool removeItem(); }; // virtual void LLTaskGestureBridge::openItem() { LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(!object || object->isInventoryPending()) { return; } LLPreviewGesture::show(mUUID, mPanel->getTaskUUID()); } // virtual bool LLTaskGestureBridge::removeItem() { // Don't need to deactivate gesture because gestures inside objects can never be active. LLFloaterReg::hideInstance("preview_gesture", LLSD(mUUID)); return LLTaskInvFVBridge::removeItem(); } ///---------------------------------------------------------------------------- /// Class LLTaskAnimationBridge ///---------------------------------------------------------------------------- class LLTaskAnimationBridge : public LLTaskInvFVBridge { public: LLTaskAnimationBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} virtual bool canOpenItem() const { return true; } virtual void openItem(); virtual bool removeItem(); }; // virtual void LLTaskAnimationBridge::openItem() { const LLUUID& task_id = mPanel->getTaskUUID(); LLViewerObject* object = gObjectList.findObject(task_id); if (!object || object->isInventoryPending()) { return; } LLPreviewAnim* preview = LLFloaterReg::showTypedInstance("preview_anim", LLSD(mUUID), TAKE_FOCUS_YES); if (preview && (object->permModify() || gAgent.isGodlike())) { preview->setObjectID(mPanel->getTaskUUID()); } } // virtual bool LLTaskAnimationBridge::removeItem() { LLFloaterReg::hideInstance("preview_anim", LLSD(mUUID)); return LLTaskInvFVBridge::removeItem(); } ///---------------------------------------------------------------------------- /// Class LLTaskWearableBridge ///---------------------------------------------------------------------------- class LLTaskWearableBridge : public LLTaskInvFVBridge { public: LLTaskWearableBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, U32 flags) : LLTaskInvFVBridge(panel, uuid, name, flags) {} virtual LLUIImagePtr getIcon() const; }; // virtual LLUIImagePtr LLTaskWearableBridge::getIcon() const { return LLInventoryIcon::getIcon(mAssetType, mInventoryType, mFlags, false ); } ///---------------------------------------------------------------------------- /// Class LLTaskSettingsBridge ///---------------------------------------------------------------------------- class LLTaskSettingsBridge : public LLTaskInvFVBridge { public: LLTaskSettingsBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, U32 flags) : LLTaskInvFVBridge(panel, uuid, name, flags) {} virtual LLUIImagePtr getIcon() const; virtual LLSettingsType::type_e getSettingsType() const; }; // virtual LLUIImagePtr LLTaskSettingsBridge::getIcon() const { return LLInventoryIcon::getIcon(mAssetType, mInventoryType, mFlags, false); } // virtual LLSettingsType::type_e LLTaskSettingsBridge::getSettingsType() const { return LLSettingsType::ST_NONE; } ///---------------------------------------------------------------------------- /// Class LLTaskMaterialBridge ///---------------------------------------------------------------------------- class LLTaskMaterialBridge : public LLTaskInvFVBridge { public: LLTaskMaterialBridge(LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name) : LLTaskInvFVBridge(panel, uuid, name) {} bool canOpenItem() const override { return true; } void openItem() override; bool removeItem() override; }; // virtual void LLTaskMaterialBridge::openItem() { LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(!object || object->isInventoryPending()) { return; } // Note: even if we are not allowed to modify copyable notecard, we should be able to view it LLInventoryItem *item = dynamic_cast(object->getInventoryObject(mUUID)); bool item_copy = item && gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE); if( item_copy || object->permModify() || gAgent.isGodlike()) { LLSD floater_key; floater_key["taskid"] = mPanel->getTaskUUID(); floater_key["itemid"] = mUUID; LLMaterialEditor* mat = LLFloaterReg::getTypedInstance("material_editor", floater_key); if (mat) { mat->setObjectID(mPanel->getTaskUUID()); mat->openFloater(floater_key); mat->setFocus(true); } } } // virtual bool LLTaskMaterialBridge::removeItem() { LLFloaterReg::hideInstance("material_editor", LLSD(mUUID)); return LLTaskInvFVBridge::removeItem(); } ///---------------------------------------------------------------------------- /// LLTaskInvFVBridge impl //---------------------------------------------------------------------------- LLTaskInvFVBridge* LLTaskInvFVBridge::createObjectBridge(LLPanelObjectInventory* panel, LLInventoryObject* object) { LLTaskInvFVBridge* new_bridge = NULL; const LLInventoryItem* item = dynamic_cast(object); const U32 itemflags = ( NULL == item ? 0 : item->getFlags() ); LLAssetType::EType type = object ? object->getType() : LLAssetType::AT_CATEGORY; LLUUID object_id = object ? object->getUUID() : LLUUID::null; std::string object_name = object ? object->getName() : std::string(); switch(type) { case LLAssetType::AT_TEXTURE: new_bridge = new LLTaskTextureBridge(panel, object_id, object_name); break; case LLAssetType::AT_SOUND: new_bridge = new LLTaskSoundBridge(panel, object_id, object_name); break; case LLAssetType::AT_LANDMARK: new_bridge = new LLTaskLandmarkBridge(panel, object_id, object_name); break; case LLAssetType::AT_CALLINGCARD: new_bridge = new LLTaskCallingCardBridge(panel, object_id, object_name); break; case LLAssetType::AT_SCRIPT: // OLD SCRIPTS DEPRECATED - JC LL_WARNS() << "Old script" << LL_ENDL; //new_bridge = new LLTaskOldScriptBridge(panel, // object_id, // object_name); break; case LLAssetType::AT_OBJECT: new_bridge = new LLTaskObjectBridge(panel, object_id, object_name, itemflags); break; case LLAssetType::AT_NOTECARD: new_bridge = new LLTaskNotecardBridge(panel, object_id, object_name); break; case LLAssetType::AT_ANIMATION: new_bridge = new LLTaskAnimationBridge(panel, object_id, object_name); break; case LLAssetType::AT_GESTURE: new_bridge = new LLTaskGestureBridge(panel, object_id, object_name); break; case LLAssetType::AT_CLOTHING: case LLAssetType::AT_BODYPART: new_bridge = new LLTaskWearableBridge(panel, object_id, object_name, itemflags); break; case LLAssetType::AT_CATEGORY: new_bridge = new LLTaskCategoryBridge(panel, object_id, object_name); break; case LLAssetType::AT_LSL_TEXT: new_bridge = new LLTaskLSLBridge(panel, object_id, object_name); break; case LLAssetType::AT_SETTINGS: new_bridge = new LLTaskSettingsBridge(panel, object_id, object_name, itemflags); break; case LLAssetType::AT_MATERIAL: new_bridge = new LLTaskMaterialBridge(panel, object_id, object_name); break; default: LL_INFOS() << "Unhandled inventory type (llassetstorage.h): " << (S32)type << LL_ENDL; break; } return new_bridge; } ///---------------------------------------------------------------------------- /// Class LLPanelObjectInventory ///---------------------------------------------------------------------------- static LLDefaultChildRegistry::Register r("panel_inventory_object"); void do_nothing() { } // Default constructor LLPanelObjectInventory::LLPanelObjectInventory(const LLPanelObjectInventory::Params& p) : LLPanel(p), mScroller(NULL), mFolders(NULL), mHaveInventory(false), mIsInventoryEmpty(true), mInventoryNeedsUpdate(false), mInventoryViewModel(p.name), mShowRootFolder(p.show_root_folder) { // Setup context menu callbacks mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelObjectInventory::doToSelected, this, _2)); mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&do_nothing)); mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&do_nothing)); mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&do_nothing)); mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this)); mCommitCallbackRegistrar.add("Inventory.FileUploadLocation", boost::bind(&do_nothing)); } // Destroys the object LLPanelObjectInventory::~LLPanelObjectInventory() { if (!gIdleCallbacks.deleteFunction(idle, this)) { LL_WARNS() << "LLPanelObjectInventory::~LLPanelObjectInventory() failed to delete callback" << LL_ENDL; } } bool LLPanelObjectInventory::postBuild() { // clear contents and initialize menus, sets up mFolders reset(); // Register an idle update callback gIdleCallbacks.addFunction(idle, this); return true; } void LLPanelObjectInventory::doToSelected(const LLSD& userdata) { LLInventoryAction::doToSelected(&gInventory, mFolders, userdata.asString()); } void LLPanelObjectInventory::clearContents() { mHaveInventory = false; mIsInventoryEmpty = true; if (LLToolDragAndDrop::getInstance() && LLToolDragAndDrop::getInstance()->getSource() == LLToolDragAndDrop::SOURCE_WORLD) { LLToolDragAndDrop::getInstance()->endDrag(); } clearItemIDs(); if( mScroller ) { // removes mFolders removeChild( mScroller ); //*TODO: Really shouldn't do this during draw()/refresh() mScroller->die(); mScroller = NULL; mFolders = NULL; } } void LLPanelObjectInventory::reset() { clearContents(); mCommitCallbackRegistrar.pushScope(); // push local callbacks // Reset the inventory model to show all folders by default mInventoryViewModel.getFilter().setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); // Create a new folder view root LLRect dummy_rect(0, 1, 1, 0); LLFolderView::Params p; p.name = "task inventory"; p.title = "task inventory"; p.parent_panel = this; p.tool_tip= LLTrans::getString("PanelContentsTooltip"); p.listener = LLTaskInvFVBridge::createObjectBridge(this, NULL); p.folder_indentation = -14; // subtract space normally reserved for folder expanders p.view_model = &mInventoryViewModel; p.root = NULL; p.options_menu = "menu_inventory.xml"; mFolders = LLUICtrlFactory::create(p); mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar); mFolders->setEnableRegistrar(&mEnableCallbackRegistrar); if (hasFocus()) { LLEditMenuHandler::gEditMenuHandler = mFolders; } int offset = hasBorder() ? getBorder()->getBorderWidth() << 1 : 0; LLRect scroller_rect(0, getRect().getHeight() - offset, getRect().getWidth() - offset, 0); LLScrollContainer::Params scroll_p; scroll_p.name("task inventory scroller"); scroll_p.rect(scroller_rect); scroll_p.tab_stop(true); scroll_p.follows.flags(FOLLOWS_ALL); mScroller = LLUICtrlFactory::create(scroll_p); addChild(mScroller); mScroller->addChild(mFolders); mFolders->setScrollContainer( mScroller ); mCommitCallbackRegistrar.popScope(); } void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object, LLInventoryObject::object_list_t* inventory, S32 serial_num, void* data) { if (!object) return; //LL_INFOS() << "invetnory arrived: \n" // << " panel UUID: " << panel->mTaskUUID << "\n" // << " task UUID: " << object->mID << LL_ENDL; if (mTaskUUID == object->mID) { mInventoryNeedsUpdate = true; } } void LLPanelObjectInventory::updateInventory() { //LL_INFOS() << "inventory arrived: \n" // << " panel UUID: " << panel->mTaskUUID << "\n" // << " task UUID: " << object->mID << LL_ENDL; // We're still interested in this task's inventory. bool inventory_has_focus = mHaveInventory && mFolders && gFocusMgr.childHasKeyboardFocus(mFolders); std::vector selected_item_ids; if (mHaveInventory && mFolders) { std::set selected_items = mFolders->getSelectionList(); for (LLFolderViewItem* item : selected_items) { selected_item_ids.push_back(static_cast(item->getViewModelItem())->getUUID()); } } if (LLViewerObject* objectp = gObjectList.findObject(mTaskUUID)) { LLInventoryObject* inventory_root = objectp->getInventoryRoot(); LLInventoryObject::object_list_t contents; objectp->getInventoryContents(contents); if (inventory_root) { reset(); mIsInventoryEmpty = false; createFolderViews(inventory_root, contents); mFolders->setEnabled(true); } else { // TODO: create an empty inventory mIsInventoryEmpty = true; } mHaveInventory = !mIsInventoryEmpty || !objectp->isInventoryDirty(); if (objectp->isInventoryDirty()) { // Inventory is dirty, yet we received inventoryChanged() callback. // User changed something during ongoing request. // Rerequest. It will clear dirty flag and won't create dupplicate requests. objectp->requestInventory(); } } else { // TODO: create an empty inventory mIsInventoryEmpty = true; mHaveInventory = true; } // restore previous selection bool first_item = true; for (const LLUUID& item_id : selected_item_ids) { if (LLFolderViewItem* selected_item = getItemByID(item_id)) { // HACK: "set" first item then "change" each other one to get keyboard focus right if (first_item) { mFolders->setSelection(selected_item, true, inventory_has_focus); first_item = false; } else { mFolders->changeSelection(selected_item, true); } } } if (mFolders) { mFolders->requestArrange(); } mInventoryNeedsUpdate = false; // Edit menu handler is set in onFocusReceived } // *FIX: This is currently a very expensive operation, because we have // to iterate through the inventory one time for each category. This // leads to an N^2 based on the category count. This could be greatly // speeded with an efficient multimap implementation, but we don't // have that in our current arsenal. void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root, LLInventoryObject::object_list_t& contents) { if (!inventory_root) { return; } // Create a visible root category. if (LLTaskInvFVBridge* bridge = LLTaskInvFVBridge::createObjectBridge(this, inventory_root)) { LLUIColor item_color = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); LLFolderViewFolder::Params p; p.name = inventory_root->getName(); p.tool_tip = p.name; p.root = mFolders; p.listener = bridge; p.font_color = item_color; p.font_highlight_color = item_color; LLFolderViewFolder* new_folder = LLUICtrlFactory::create(p); if (mShowRootFolder) { new_folder->addToFolder(mFolders); new_folder->toggleOpen(); } if (!contents.empty()) { createViewsForCategory(&contents, inventory_root, mShowRootFolder ? new_folder : mFolders); } if (mShowRootFolder) { // Refresh for label to add item count new_folder->refresh(); } } } typedef std::pair obj_folder_pair; void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_list_t* inventory, LLInventoryObject* parent, LLFolderViewFolder* folder) { LLUIColor item_color = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); // Find all in the first pass std::vector child_categories; for (const LLPointer& obj : *inventory) { if (parent->getUUID() == obj->getParentUUID()) { if (LLTaskInvFVBridge* bridge = LLTaskInvFVBridge::createObjectBridge(this, obj)) { LLFolderViewItem* view; if (LLAssetType::AT_CATEGORY == obj->getType()) { LLFolderViewFolder::Params params; params.name = obj->getName(); params.root = mFolders; params.listener = bridge; params.tool_tip = params.name; params.font_color = item_color; params.font_highlight_color = item_color; view = LLUICtrlFactory::create(params); child_categories.push_back(new obj_folder_pair(obj, (LLFolderViewFolder*)view)); } else { LLFolderViewItem::Params params; params.name = obj->getName(); params.root = mFolders; params.listener = bridge; params.creation_date = bridge->getCreationDate(); params.rect = LLRect(); params.tool_tip = params.name; params.font_color = item_color; params.font_highlight_color = item_color; view = LLUICtrlFactory::create(params); } view->addToFolder(folder); addItemID(obj->getUUID(), view); } } } // now, for each category, do the second pass for (obj_folder_pair* pair : child_categories) { createViewsForCategory(inventory, pair->first, pair->second); delete pair; } folder->setChildrenInited(true); } void LLPanelObjectInventory::refresh() { //LL_INFOS() << "LLPanelObjectInventory::refresh()" << LL_ENDL; bool has_inventory = false; const bool non_root_ok = true; LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); LLSelectNode* node = selection->getFirstRootNode(NULL, non_root_ok); if (node && node->mValid) { LLViewerObject* object = node->getObject(); if (object && ((selection->getRootObjectCount() == 1) || (selection->getObjectCount() == 1))) { // determine if we need to make a request. Start with a // default based on if we have inventory at all. bool make_request = !mHaveInventory; // If the task id is different than what we've stored, // then make the request. if (mTaskUUID != object->mID) { mTaskUUID = object->mID; mAttachmentUUID = object->getAttachmentItemID(); make_request = true; // This is a new object so pre-emptively clear the contents // Otherwise we show the old stuff until the update comes in clearContents(); // Register for updates from this object, registerVOInventoryListener(object,NULL); } else if (mAttachmentUUID != object->getAttachmentItemID()) { mAttachmentUUID = object->getAttachmentItemID(); if (mAttachmentUUID.notNull()) { // Server unsubsribes viewer (deselects object) from property // updates after "ObjectAttach" so we need to resubscribe LLSelectMgr::getInstance()->sendSelect(); } } // Based on the node information, we may need to dirty the // object inventory and get it again. if (node->mValid) { if (node->mInventorySerial != object->getInventorySerial() || object->isInventoryDirty()) { make_request = true; } } // do the request if necessary. if (make_request) { requestVOInventory(); } has_inventory = true; } } if (!has_inventory) { clearInventoryTask(); } mInventoryViewModel.setTaskID(mTaskUUID); //LL_INFOS() << "LLPanelObjectInventory::refresh() " << mTaskUUID << LL_ENDL; } void LLPanelObjectInventory::clearInventoryTask() { mTaskUUID = LLUUID::null; mAttachmentUUID = LLUUID::null; removeVOInventoryListener(); clearContents(); } void LLPanelObjectInventory::removeSelectedItem() { if (mFolders) { mFolders->removeSelectedItems(); } } void LLPanelObjectInventory::startRenamingSelectedItem() { if (mFolders) { mFolders->startRenamingSelectedItem(); } } void LLPanelObjectInventory::draw() { LLPanel::draw(); if (mIsInventoryEmpty) { std::string text; if (!mHaveInventory && mTaskUUID.notNull()) { text = LLTrans::getString("LoadingContents"); } else if (mHaveInventory) { text = LLTrans::getString("NoContents"); } if (!text.empty()) { LLFontGL::getFontSansSerif()->renderUTF8(text, 0, (S32)(getRect().getWidth() * 0.5f), 10, LLColor4(1, 1, 1, 1), LLFontGL::HCENTER, LLFontGL::BOTTOM); } } } void LLPanelObjectInventory::deleteAllChildren() { mScroller = NULL; mFolders = NULL; LLView::deleteAllChildren(); } bool LLPanelObjectInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg) { LLFolderViewItem* folderp = mFolders ? mFolders->getNextFromChild(NULL) : NULL; if (!folderp) return false; // Try to pass on unmodified mouse coordinates S32 local_x = x - mFolders->getRect().mLeft; S32 local_y = y - mFolders->getRect().mBottom; if (mFolders->pointInView(local_x, local_y)) { return mFolders->handleDragAndDrop(local_x, local_y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } //force mouse coordinates to be inside folder rectangle return mFolders->handleDragAndDrop(5, 1, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } //static void LLPanelObjectInventory::idle(void* user_data) { LLPanelObjectInventory* self = (LLPanelObjectInventory*)user_data; if (self->mFolders) { self->mFolders->update(); } if (self->mInventoryNeedsUpdate) { self->updateInventory(); } } void LLPanelObjectInventory::onFocusLost() { // inventory no longer handles cut/copy/paste/delete if (LLEditMenuHandler::gEditMenuHandler == mFolders) { LLEditMenuHandler::gEditMenuHandler = NULL; } LLPanel::onFocusLost(); } void LLPanelObjectInventory::onFocusReceived() { // inventory now handles cut/copy/paste/delete LLEditMenuHandler::gEditMenuHandler = mFolders; LLPanel::onFocusReceived(); } LLFolderViewItem* LLPanelObjectInventory::getItemByID( const LLUUID& id ) { std::map::iterator map_it = mItemMap.find(id); if (map_it != mItemMap.end()) { return map_it->second; } return NULL; } void LLPanelObjectInventory::removeItemID( const LLUUID& id ) { mItemMap.erase(id); } void LLPanelObjectInventory::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) { mItemMap[id] = itemp; } void LLPanelObjectInventory::clearItemIDs() { mItemMap.clear(); } bool LLPanelObjectInventory::handleKeyHere( KEY key, MASK mask ) { bool handled = false; switch (key) { case KEY_DELETE: #if LL_DARWIN case KEY_BACKSPACE: #endif // Delete selected items if delete or backspace key hit on the inventory panel // Note: on Mac laptop keyboards, backspace and delete are one and the same if (isSelectionRemovable() && mask == MASK_NONE) { LLInventoryAction::doToSelected(&gInventory, mFolders, "delete"); handled = true; } break; } return handled; } bool LLPanelObjectInventory::isSelectionRemovable() { if (!mFolders || !mFolders->getRoot()) { return false; } std::set selection_set = mFolders->getRoot()->getSelectionList(); if (selection_set.empty()) { return false; } for (LLFolderViewItem* item : selection_set) { const LLFolderViewModelItemInventory *listener = dynamic_cast(item->getViewModelItem()); if (!listener || !listener->isItemRemovable() || listener->isItemInTrash()) { return false; } } return true; }