From 64da5083d0b99d58140d84f9a44191b5c3d321b1 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Tue, 11 May 2010 13:01:27 -0400 Subject: EXT-7306 : FIXED : Add shortcut-style icons for inventory links EXT-7295 : FIXED : INFRASTRUCTURE : Cleaned up Inventory Icon code Inventory link icons were designed from scratch. Added LLInventoryIcon class and did some major refactoring for how it determines inventory icons from item bridges. --- indra/newview/llinventoryfunctions.cpp | 182 +++------------------------------ 1 file changed, 17 insertions(+), 165 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 8010d1f43d..35ad8b64da 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1,5 +1,5 @@ /** - * @file llfloaterinventory.cpp + * @file llinventoryfunctions.cpp * @brief Implementation of the inventory view and associated stuff. * * $LicenseInfo:firstyear=2001&license=viewergpl$ @@ -87,7 +87,6 @@ BOOL LLInventoryState::sWearNewClothing = FALSE; LLUUID LLInventoryState::sWearNewClothingTransactionID; - ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -407,152 +406,28 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) } } -static void assign_clothing_bodypart_icon(EInventoryIcon &idx, U32 attachment_point) -{ - const EWearableType wearable_type = EWearableType(LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK & attachment_point); - switch(wearable_type) - { - case WT_SHAPE: - idx = BODYPART_SHAPE_ICON_NAME; - break; - case WT_SKIN: - idx = BODYPART_SKIN_ICON_NAME; - break; - case WT_HAIR: - idx = BODYPART_HAIR_ICON_NAME; - break; - case WT_EYES: - idx = BODYPART_EYES_ICON_NAME; - break; - case WT_SHIRT: - idx = CLOTHING_SHIRT_ICON_NAME; - break; - case WT_PANTS: - idx = CLOTHING_PANTS_ICON_NAME; - break; - case WT_SHOES: - idx = CLOTHING_SHOES_ICON_NAME; - break; - case WT_SOCKS: - idx = CLOTHING_SOCKS_ICON_NAME; - break; - case WT_JACKET: - idx = CLOTHING_JACKET_ICON_NAME; - break; - case WT_GLOVES: - idx = CLOTHING_GLOVES_ICON_NAME; - break; - case WT_UNDERSHIRT: - idx = CLOTHING_UNDERSHIRT_ICON_NAME; - break; - case WT_UNDERPANTS: - idx = CLOTHING_UNDERPANTS_ICON_NAME; - break; - case WT_SKIRT: - idx = CLOTHING_SKIRT_ICON_NAME; - break; - case WT_ALPHA: - idx = CLOTHING_ALPHA_ICON_NAME; - break; - case WT_TATTOO: - idx = CLOTHING_TATTOO_ICON_NAME; - break; - default: - break; - } -} - - -const std::string& get_item_icon_name(LLAssetType::EType asset_type, - LLInventoryType::EType inventory_type, - U32 attachment_point, - BOOL item_is_multi ) +void change_item_parent(LLInventoryModel* model, + LLViewerInventoryItem* item, + const LLUUID& new_parent_id, + BOOL restamp) { - EInventoryIcon idx = OBJECT_ICON_NAME; - if ( item_is_multi ) - { - idx = OBJECT_MULTI_ICON_NAME; - } - - switch(asset_type) + if (item->getParentUUID() != new_parent_id) { - case LLAssetType::AT_TEXTURE: - if(LLInventoryType::IT_SNAPSHOT == inventory_type) - { - idx = SNAPSHOT_ICON_NAME; - } - else - { - idx = TEXTURE_ICON_NAME; - } - break; + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); - case LLAssetType::AT_SOUND: - idx = SOUND_ICON_NAME; - break; - case LLAssetType::AT_CALLINGCARD: - if(attachment_point!= 0) - { - idx = CALLINGCARD_ONLINE_ICON_NAME; - } - else - { - idx = CALLINGCARD_OFFLINE_ICON_NAME; - } - break; - case LLAssetType::AT_LANDMARK: - if(attachment_point!= 0) - { - idx = LANDMARK_VISITED_ICON_NAME; - } - else - { - idx = LANDMARK_ICON_NAME; - } - break; - case LLAssetType::AT_SCRIPT: - case LLAssetType::AT_LSL_TEXT: - case LLAssetType::AT_LSL_BYTECODE: - idx = SCRIPT_ICON_NAME; - break; - case LLAssetType::AT_CLOTHING: - idx = CLOTHING_ICON_NAME; - assign_clothing_bodypart_icon(idx, attachment_point); - break; - case LLAssetType::AT_BODYPART: - idx = BODYPART_ICON_NAME; - assign_clothing_bodypart_icon(idx, attachment_point); - break; - case LLAssetType::AT_NOTECARD: - idx = NOTECARD_ICON_NAME; - break; - case LLAssetType::AT_ANIMATION: - idx = ANIMATION_ICON_NAME; - break; - case LLAssetType::AT_GESTURE: - idx = GESTURE_ICON_NAME; - break; - case LLAssetType::AT_LINK: - idx = LINKITEM_ICON_NAME; - break; - case LLAssetType::AT_LINK_FOLDER: - idx = LINKFOLDER_ICON_NAME; - break; - default: - break; + LLPointer new_item = new LLViewerInventoryItem(item); + new_item->setParent(new_parent_id); + new_item->updateParentOnServer(restamp); + model->updateItem(new_item); + model->notifyObservers(); } - - return ICON_NAME[idx]; } -LLUIImagePtr get_item_icon(LLAssetType::EType asset_type, - LLInventoryType::EType inventory_type, - U32 attachment_point, - BOOL item_is_multi) -{ - const std::string& icon_name = get_item_icon_name(asset_type, inventory_type, attachment_point, item_is_multi ); - return LLUI::getUIImage(icon_name); -} BOOL get_is_item_worn(const LLUUID& id) { @@ -582,26 +457,3 @@ BOOL get_is_item_worn(const LLUUID& id) } return FALSE; } - - -void change_item_parent(LLInventoryModel* model, - LLViewerInventoryItem* item, - const LLUUID& new_parent_id, - BOOL restamp) -{ - if (item->getParentUUID() != new_parent_id) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - LLPointer new_item = new LLViewerInventoryItem(item); - new_item->setParent(new_parent_id); - new_item->updateParentOnServer(restamp); - model->updateItem(new_item); - model->notifyObservers(); - } -} -- cgit v1.3 From dbaaebfe5b2847a258019cd82a2abe044e3d3140 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Wed, 12 May 2010 13:46:21 -0400 Subject: EXT-7319 : FIXED : INFRASTRUCTURE : LLInventoryBridge cleanup Const correctness, logic cleanup, took out subclasses redefining same variable as base class (!!!). --- indra/newview/llinventorybridge.cpp | 45 ++++++---------- indra/newview/llinventorybridge.h | 99 +++++++++++++--------------------- indra/newview/llinventoryfunctions.cpp | 21 +++----- indra/newview/llinventoryfunctions.h | 2 +- 4 files changed, 62 insertions(+), 105 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 64379f4ce7..973257b19c 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -997,46 +997,36 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) } } -bool LLInvFVBridge::isInOutfitsSidePanel() const +BOOL LLInvFVBridge::isInOutfitsSidePanel() const { LLInventoryPanel *my_panel = dynamic_cast(mInventoryPanel.get()); LLPanelOutfitsInventory *outfit_panel = dynamic_cast(LLSideTray::getInstance()->getPanel("panel_outfits_inventory")); if (!outfit_panel) - return false; + return FALSE; return outfit_panel->isTabPanel(my_panel); } -bool LLInvFVBridge::canShare() +BOOL LLInvFVBridge::canShare() const { const LLInventoryModel* model = getInventoryModel(); - if(!model) - { - return false; - } + if (!model) return FALSE; - LLViewerInventoryItem *item = model->getItem(mUUID); + const LLViewerInventoryItem *item = model->getItem(mUUID); if (item) { - bool allowed = false; - allowed = LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item); - if (allowed && - !item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) - { - allowed = false; - } - if (allowed && - !item->getPermissions().allowCopyBy(gAgent.getID())) - { - allowed = false; - } - return allowed; + if (!LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) + return FALSE; + if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) + return FALSE; + if (!item->getPermissions().allowCopyBy(gAgent.getID())) + return FALSE; + return TRUE; } - LLViewerInventoryCategory* cat = model->getCategory(mUUID); - // All categories can be given. - return cat != NULL; + const LLViewerInventoryCategory* cat = model->getCategory(mUUID); + return (cat != NULL); } // +=================================================+ @@ -4095,12 +4085,11 @@ LLObjectBridge::LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) : - LLItemBridge(inventory, root, uuid), - mInvType(type) + LLItemBridge(inventory, root, uuid) { mAttachPt = (flags & 0xff); // low bye of inventory flags - mIsMultiObject = ( flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS ) ? TRUE: FALSE; + mInvType = type; } LLUIImagePtr LLObjectBridge::getIcon() const @@ -4447,9 +4436,9 @@ LLWearableBridge::LLWearableBridge(LLInventoryPanel* inventory, LLWearableType::EType wearable_type) : LLItemBridge(inventory, root, uuid), mAssetType( asset_type ), - mInvType(inv_type), mWearableType(wearable_type) { + mInvType = inv_type; } // *NOTE: hack to get from avatar inventory to avatar diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 1c85dc8d7e..c5efefac7e 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -74,13 +74,19 @@ public: U32 flags = 0x00); virtual ~LLInvFVBridge() {} - virtual const LLUUID& getUUID() const { return mUUID; } + BOOL isInOutfitsSidePanel() const; // allow context menus to be customized for side panel + BOOL canShare() const; + //-------------------------------------------------------------------- + // LLInvFVBridge functionality + //-------------------------------------------------------------------- + virtual const LLUUID& getUUID() const { return mUUID; } + virtual void clearDisplayName() {} virtual void restoreItem() {} virtual void restoreToWorld() {} //-------------------------------------------------------------------- - // LLFolderViewEventListener functions + // Inherited LLFolderViewEventListener functions //-------------------------------------------------------------------- virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; @@ -99,7 +105,6 @@ public: virtual BOOL isItemMovable() const; virtual BOOL isItemInTrash() const; virtual BOOL isLink() const; - //virtual BOOL removeItem() = 0; virtual void removeBatch(LLDynamicArray& batch); virtual void move(LLFolderViewEventListener* new_parent_bridge) {} @@ -119,15 +124,6 @@ public: void* cargo_data) { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return mInvType; } - //-------------------------------------------------------------------- - // LLInvFVBridge functionality - //-------------------------------------------------------------------- - virtual void clearDisplayName() {} - - bool isInOutfitsSidePanel() const; // allow context menus to be customized for side panel - - bool canShare(); - //-------------------------------------------------------------------- // Convenience functions for adding various common menu options. //-------------------------------------------------------------------- @@ -195,7 +191,6 @@ public: LLInvFVBridge(inventory, root, uuid) {} virtual void performAction(LLInventoryModel* model, std::string action); - virtual void selectItem(); virtual void restoreItem(); virtual void restoreToWorld(); @@ -214,20 +209,17 @@ public: virtual BOOL hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - // Override for LLInvFVBridge - virtual void clearDisplayName() { mDisplayName.clear(); } + /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } LLViewerInventoryItem* getItem() const; - bool isAddAction(std::string action) const; bool isRemoveAction(std::string action) const; - protected: + BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); virtual BOOL isItemPermissive() const; static void buildDisplayName(LLInventoryItem* item, std::string& name); - mutable std::string mDisplayName; - BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); + mutable std::string mDisplayName; }; class LLFolderBridge : public LLInvFVBridge @@ -240,10 +232,9 @@ public: mCallingCards(FALSE), mWearables(FALSE), mMenu(NULL) {} - BOOL dragItemIntoFolder(LLInventoryItem* inv_item, - BOOL drop); - BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, - BOOL drop); + BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop); + BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop); + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void closeItem(); @@ -311,10 +302,15 @@ protected: menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items + + //-------------------------------------------------------------------- + // Messy hacks for handling folder options + //-------------------------------------------------------------------- public: static LLFolderBridge* sSelf; static void staticFolderOptionsMenu(); void folderOptionsMenu(); + private: BOOL mCallingCards; BOOL mWearables; @@ -330,15 +326,15 @@ public: LLFolderView* root, const LLUUID& uuid, LLInventoryType::EType type) : - LLItemBridge(inventory, root, uuid), - mInvType(type) {} + LLItemBridge(inventory, root, uuid) + { + mInvType = type; + } virtual LLUIImagePtr getIcon() const; virtual void openItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLInventoryModel* model, std::string action); - bool canSaveTexture(void); - LLInventoryType::EType mInvType; }; class LLSoundBridge : public LLItemBridge @@ -447,8 +443,7 @@ public: virtual BOOL renameItem(const std::string& new_name); LLInventoryObject* getObject() const; protected: - static LLUUID sContextMenuItemID; // Only valid while the context menu is open. - LLInventoryType::EType mInvType; + static LLUUID sContextMenuItemID; // Only valid while the context menu is open. U32 mAttachPt; BOOL mIsMultiObject; }; @@ -499,7 +494,6 @@ public: void removeFromAvatar(); protected: LLAssetType::EType mAssetType; - LLInventoryType::EType mInvType; LLWearableType::EType mWearableType; }; @@ -563,47 +557,33 @@ protected: LLInventoryModel* mModel; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Recent Inventory Panel related classes +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -/************************************************************************/ -/* Recent Inventory Panel related classes */ -/************************************************************************/ -class LLRecentInventoryBridgeBuilder; -/** - * Overridden version of the Inventory-Folder-View-Bridge for Folders - */ +// Overridden version of the Inventory-Folder-View-Bridge for Folders class LLRecentItemsFolderBridge : public LLFolderBridge { - friend class LLRecentInventoryBridgeBuilder; - public: - /** - * Creates context menu for Folders related to Recent Inventory Panel. - * - * It uses base logic and than removes from visible items "New..." menu items. - */ - /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); - -protected: + // Creates context menu for Folders related to Recent Inventory Panel. + // Uses base logic and than removes from visible items "New..." menu items. LLRecentItemsFolderBridge(LLInventoryType::EType type, - LLInventoryPanel* inventory, - LLFolderView* root, - const LLUUID& uuid) : + LLInventoryPanel* inventory, + LLFolderView* root, + const LLUUID& uuid) : LLFolderBridge(inventory, root, uuid) { mInvType = type; } + /*virtual*/ void buildContextMenu(LLMenuGL& menu, U32 flags); }; -/** - * Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel - */ +// Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel class LLRecentInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder { - /** - * Overrides FolderBridge for Recent Inventory Panel. - * - * It use base functionality for bridges other than FolderBridge. - */ +public: + // Overrides FolderBridge for Recent Inventory Panel. + // It use base functionality for bridges other than FolderBridge. virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, @@ -611,11 +591,8 @@ class LLRecentInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; - }; - - void wear_inventory_item_on_avatar(LLInventoryItem* item); void rez_attachment(LLViewerInventoryItem* item, diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 35ad8b64da..f1520d34df 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -92,39 +92,30 @@ LLUUID LLInventoryState::sWearNewClothingTransactionID; ///---------------------------------------------------------------------------- // static -bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(LLInventoryItem* item) +bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryItem* item) { if (!item) return false; - bool allowed = false; - switch(item->getType()) { case LLAssetType::AT_CALLINGCARD: - // not allowed + return false; break; - case LLAssetType::AT_OBJECT: if (isAgentAvatarValid() && !gAgentAvatarp->isWearingAttachment(item->getUUID())) - { - allowed = true; - } + return true; break; - case LLAssetType::AT_BODYPART: case LLAssetType::AT_CLOTHING: if(!gAgentWearables.isWearingItem(item->getUUID())) - { - allowed = true; - } + return true; break; default: - allowed = true; + return true; break; } - - return allowed; + return false; } bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 1e2b4ff09c..02a5fc9224 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -58,7 +58,7 @@ public: virtual ~LLInventoryCollectFunctor(){}; virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0; - static bool itemTransferCommonlyAllowed(LLInventoryItem* item); + static bool itemTransferCommonlyAllowed(const LLInventoryItem* item); }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.3 From ee53b612fb44b158c10fd949f223f80905a37ce4 Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Wed, 12 May 2010 16:46:28 -0400 Subject: EXT-7324 : INFRASTRUCTURE: Inventorymodel code cleanup Superficial header file cleanup Lots of moving functions around and recategorizing in LLInventoryModel.h Removed some obsolete functions, changed scoping of others Moved some static functions from LLInventoryModel into llinventoryfunctions.h --- indra/newview/llinventoryfunctions.cpp | 125 ++++--- indra/newview/llinventoryfunctions.h | 30 +- indra/newview/llinventorymodel.cpp | 334 ++---------------- indra/newview/llinventorymodel.h | 620 ++++++++++++++++----------------- indra/newview/llvoavatar.h | 19 +- indra/newview/llvoavatarself.h | 5 +- 6 files changed, 444 insertions(+), 689 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index f1520d34df..2b4d9fb25c 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -87,6 +87,79 @@ BOOL LLInventoryState::sWearNewClothing = FALSE; LLUUID LLInventoryState::sWearNewClothingTransactionID; +// Generates a string containing the path to the item specified by +// item_id. +void append_path(const LLUUID& id, std::string& path) +{ + std::string temp; + const LLInventoryObject* obj = gInventory.getObject(id); + LLUUID parent_id; + if(obj) parent_id = obj->getParentUUID(); + std::string forward_slash("/"); + while(obj) + { + obj = gInventory.getCategory(parent_id); + if(obj) + { + temp.assign(forward_slash + obj->getName() + temp); + parent_id = obj->getParentUUID(); + } + } + path.append(temp); +} + +void change_item_parent(LLInventoryModel* model, + LLViewerInventoryItem* item, + const LLUUID& new_parent_id, + BOOL restamp) +{ + if (item->getParentUUID() != new_parent_id) + { + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + LLPointer new_item = new LLViewerInventoryItem(item); + new_item->setParent(new_parent_id); + new_item->updateParentOnServer(restamp); + model->updateItem(new_item); + model->notifyObservers(); + } +} + + +BOOL get_is_item_worn(const LLUUID& id) +{ + const LLViewerInventoryItem* item = gInventory.getItem(id); + if (!item) + return FALSE; + + switch(item->getType()) + { + case LLAssetType::AT_OBJECT: + { + if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) + return TRUE; + break; + } + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + if(gAgentWearables.isWearingItem(item->getLinkedUUID())) + return TRUE; + break; + case LLAssetType::AT_GESTURE: + if (LLGestureMgr::instance().isGestureActive(item->getLinkedUUID())) + return TRUE; + break; + default: + break; + } + return FALSE; +} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -396,55 +469,3 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } } - -void change_item_parent(LLInventoryModel* model, - LLViewerInventoryItem* item, - const LLUUID& new_parent_id, - BOOL restamp) -{ - if (item->getParentUUID() != new_parent_id) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - LLPointer new_item = new LLViewerInventoryItem(item); - new_item->setParent(new_parent_id); - new_item->updateParentOnServer(restamp); - model->updateItem(new_item); - model->notifyObservers(); - } -} - - -BOOL get_is_item_worn(const LLUUID& id) -{ - const LLViewerInventoryItem* item = gInventory.getItem(id); - if (!item) - return FALSE; - - switch(item->getType()) - { - case LLAssetType::AT_OBJECT: - { - if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) - return TRUE; - break; - } - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - if(gAgentWearables.isWearingItem(item->getLinkedUUID())) - return TRUE; - break; - case LLAssetType::AT_GESTURE: - if (LLGestureMgr::instance().isGestureActive(item->getLinkedUUID())) - return TRUE; - break; - default: - break; - } - return FALSE; -} diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 02a5fc9224..79b9b4a9cc 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -38,6 +38,27 @@ #include "llfolderview.h" #include "llfolderviewitem.h" +/******************************************************************************** + ** ** + ** MISCELLANEOUS GLOBAL FUNCTIONS + **/ + +// Is this item or its baseitem is worn, attached, etc... +BOOL get_is_item_worn(const LLUUID& id); + + +void change_item_parent(LLInventoryModel* model, + LLViewerInventoryItem* item, + const LLUUID& new_parent_id, + BOOL restamp); + +// Generates a string containing the path to the item specified by item_id. +void append_path(const LLUUID& id, std::string& path); + +/** Miscellaneous global functions + ** ** + *******************************************************************************/ + /******************************************************************************** ** ** ** INVENTORY COLLECTOR FUNCTIONS @@ -297,15 +318,6 @@ public: virtual void doItem(LLFolderViewItem* item); }; -// Is this item or its baseitem is worn, attached, etc... -BOOL get_is_item_worn(const LLUUID& id); - - -void change_item_parent(LLInventoryModel* model, - LLViewerInventoryItem* item, - const LLUUID& new_parent_id, - BOOL restamp); - #endif // LL_LLINVENTORYFUNCTIONS_H diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index e63da1ebb9..a527694d25 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -319,30 +319,18 @@ void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id) // specifies 'type' as what it defaults to containing. The category is // not necessarily only for that type. *NOTE: This will create a new // inventory category on the fly if one does not exist. -const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType t, bool create_folder, bool find_in_library) -{ - const LLUUID &rv = findCatUUID(t, find_in_library); - if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library)) - { - const LLUUID &root_id = gInventory.getRootFolderID(); - if(root_id.notNull()) - { - return createNewCategory(root_id, t, LLStringUtil::null); - } - } - return rv; -} - -// Internal method which looks for a category with the specified -// preferred type. Returns LLUUID::null if not found. -const LLUUID &LLInventoryModel::findCatUUID(LLFolderType::EType preferred_type, bool find_in_library) const +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, + bool create_folder, + bool find_in_library) { + LLUUID rv = LLUUID::null; + const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID(); if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) { - return root_id; + rv = root_id; } - if(root_id.notNull()) + else if (root_id.notNull()) { cat_array_t* cats = NULL; cats = get_ptr_in_map(mParentChildCategoryTree, root_id); @@ -353,12 +341,21 @@ const LLUUID &LLInventoryModel::findCatUUID(LLFolderType::EType preferred_type, { if(cats->get(i)->getPreferredType() == preferred_type) { - return cats->get(i)->getUUID(); + rv = cats->get(i)->getUUID(); + break; } } } } - return LLUUID::null; + + if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library)) + { + if(root_id.notNull()) + { + return createNewCategory(root_id, preferred_type, LLStringUtil::null); + } + } + return rv; } // Convenience function to create a new category. You could call @@ -583,27 +580,6 @@ LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID return items; } -// Generates a string containing the path to the item specified by -// item_id. -void LLInventoryModel::appendPath(const LLUUID& id, std::string& path) const -{ - std::string temp; - const LLInventoryObject* obj = getObject(id); - LLUUID parent_id; - if(obj) parent_id = obj->getParentUUID(); - std::string forward_slash("/"); - while(obj) - { - obj = getCategory(parent_id); - if(obj) - { - temp.assign(forward_slash + obj->getName() + temp); - parent_id = obj->getParentUUID(); - } - } - path.append(temp); -} - bool LLInventoryModel::isInventoryUsable() const { bool result = false; @@ -1024,98 +1000,6 @@ void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) } } -void LLInventoryModel::deleteFromServer(LLDynamicArray& category_ids, - LLDynamicArray& item_ids) -{ - // Store off tre UUIDS of parents which are being deleted (thus no - // need to increment) and the parents which are being modified. We - // have to increment the version of the parent with each message - // sent upstream since the dataserver will increment each unique - // parent per update message. - std::set ignore_parents; - update_map_t inc_parents; - - S32 i; - S32 count = category_ids.count(); - BOOL start_new_message = TRUE; - LLMessageSystem* msg = gMessageSystem; - LLPointer cat; - for(i = 0; i < count; i++) - { - if(start_new_message) - { - start_new_message = FALSE; - msg->newMessageFast(_PREHASH_RemoveInventoryObjects); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - LLUUID cat_id = category_ids.get(i); - - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, cat_id); - cat = getCategory(cat_id); - ignore_parents.insert(cat_id); - addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, cat_id); - if(cat.notNull() && (ignore_parents.find(cat->getParentUUID())==ignore_parents.end())) - { - --inc_parents[cat->getParentUUID()]; - } - if(msg->isSendFullFast(_PREHASH_FolderData)) - { - start_new_message = TRUE; - msg->nextBlockFast(_PREHASH_ItemData); - msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null); - gAgent.sendReliableMessage(); - accountForUpdate(inc_parents); - inc_parents.clear(); - } - } - - count = item_ids.count(); - std::set::iterator not_ignored = ignore_parents.end(); - LLPointer item; - if((0 == count) && (!start_new_message)) - { - msg->nextBlockFast(_PREHASH_ItemData); - msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null); - } - for(i = 0; i < count; i++) - { - if(start_new_message) - { - start_new_message = FALSE; - msg->newMessageFast(_PREHASH_RemoveInventoryObjects); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, LLUUID::null); - } - LLUUID item_id = item_ids.get(i); - msg->nextBlockFast(_PREHASH_ItemData); - msg->addUUIDFast(_PREHASH_ItemID, item_id); - item = getItem(item_id); - addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, item_id); - if(item.notNull() && (ignore_parents.find(item->getParentUUID()) == not_ignored)) - { - --inc_parents[item->getParentUUID()]; - } - if(msg->isSendFullFast(_PREHASH_ItemData)) - { - start_new_message = TRUE; - gAgent.sendReliableMessage(); - accountForUpdate(inc_parents); - inc_parents.clear(); - } - } - if(!start_new_message) - { - gAgent.sendReliableMessage(); - accountForUpdate(inc_parents); - } -} - // Add/remove an observer. If the observer is destroyed, be sure to // remove it. void LLInventoryModel::addObserver(LLInventoryObserver* observer) @@ -1206,29 +1090,7 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) } } -// This method to prepares a set of mock inventory which provides -// minimal functionality before the actual arrival of inventory. -/* -void LLInventoryModel::mock(const LLUUID& root_id) -{ - llinfos << "LLInventoryModel::mock() " << root_id << llendl; - if(root_id.isNull()) - { - llwarns << "Not a valid root id" << llendl; - return; - } - LLPointer cat = new LLViewerInventoryCategory( - root_id, - LLUUID::null, - LLAssetType::AT_CATEGORY, - LLFolderType::lookupNewCategoryName(LLFolderType::FT_ROOT_INVENTORY), - gAgent.getID()); - addCategory(cat); - gInventory.buildParentChildMap(); -} -*/ - -//If we get back a normal response, handle it here +// If we get back a normal response, handle it here void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) { start_new_inventory_observer(); @@ -1301,7 +1163,7 @@ void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::str gInventory.notifyObservers("fetchinventory"); } -bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) +bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const { if(folder_id.isNull()) { @@ -1836,88 +1698,6 @@ bool LLInventoryModel::loadSkeleton( return rv; } -bool LLInventoryModel::loadMeat(const LLSD& options, const LLUUID& owner_id) -{ - llinfos << "importing inventory for " << owner_id << llendl; - bool rv = true; - for(LLSD::array_const_iterator it = options.beginArray(), - end = options.endArray(); it != end; ++it) - { - LLSD name = (*it)["name"]; - LLSD item_id = (*it)["item_id"]; - LLSD parent_id = (*it)["parent_id"]; - LLSD asset_type = (*it)["type"]; - LLSD data_id = (*it)["data_id"]; - if(name.isDefined() - && item_id.isDefined() - && parent_id.isDefined() - && asset_type.isDefined() - && data_id.isDefined()) - { - LLPointer item = new LLViewerInventoryItem; - item->rename(name.asString()); - item->setUUID(item_id.asUUID()); - item->setParent(parent_id.asUUID()); - LLAssetType::EType type = (LLAssetType::EType)asset_type.asInteger(); - item->setType(type); - - LLSD llsd_inv_type = (*it)["inv_type"]; - if(llsd_inv_type.isDefined()) - { - LLInventoryType::EType inv_type = (LLInventoryType::EType)llsd_inv_type.asInteger(); - item->setInventoryType(inv_type); - } - - if(LLAssetType::AT_CALLINGCARD == type) - { - LLPermissions perm; - perm.init(data_id.asUUID(), owner_id, LLUUID::null, LLUUID::null); - item->setPermissions(perm); - } - else - { - LLPermissions default_perm; - default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null); - LLSD llsd_perm_mask = (*it)["perm_mask"]; - if(llsd_perm_mask.isDefined()) - { - PermissionMask perm_mask = llsd_perm_mask.asInteger(); - default_perm.initMasks( - perm_mask, perm_mask, perm_mask, perm_mask, perm_mask); - } - else - { - default_perm.initMasks( - PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); - } - item->setPermissions(default_perm); - item->setAssetUUID(data_id.asUUID()); - } - - LLSD flags = (*it)["flags"]; - if(flags.isDefined()) - { - // Not sure how well LLSD.asInteger() maps to - // unsigned long - using strtoul() - item->setFlags(strtoul(flags.asString().c_str(), NULL, 0)); - } - - LLSD time = (*it)["time"]; - if(time.isDefined()) - { - item->setCreationDate(time.asInteger()); - } - addItem(item); - } - else - { - llwarns << "Unable to import near " << name.asString() << llendl; - rv = false; - } - } - return rv; -} - // This is a brute force method to rebuild the entire parent-child // relations. The overall operation has O(NlogN) performance, which // should be sufficient for our needs. @@ -2185,73 +1965,6 @@ bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const return (mID > rhs.mID); } -// Given the current state of the inventory items, figure out the -// clone information. *FIX: This is sub-optimal, since we can insert -// this information snurgically, but this makes sure the implementation -// works before we worry about optimization. -//void LLInventoryModel::recalculateCloneInformation() -//{ -// //dumpInventory(); -// -// // This implements a 'multi-map' like structure to keep track of -// // how many clones we find. -// typedef LLDynamicArray viewer_item_array_t; -// typedef std::map clone_map_t; -// clone_map_t clone_map; -// LLUUIDAndName id_and_name; -// viewer_item_array_t* clones = NULL; -// LLViewerInventoryItem* item = NULL; -// for(item = (LLViewerInventoryItem*)mItemMap.getFirstData(); -// item != NULL; -// item = (LLViewerInventoryItem*)mItemMap.getNextData()) -// { -// if(item->getType() == LLAssetType::AT_CALLINGCARD) -// { -// // if it's a calling card, we key off of the creator id, not -// // the asset id. -// id_and_name.mID = item->getCreatorUUID(); -// } -// else -// { -// // if it's not a calling card, we key clones from the -// // asset id. -// id_and_name.mID = item->getAssetUUID(); -// } -// if(id_and_name.mID == LLUUID::null) -// { -// continue; -// } -// id_and_name.mName = item->getName(); -// if(clone_map.checkData(id_and_name)) -// { -// clones = clone_map.getData(id_and_name); -// } -// else -// { -// clones = new viewer_item_array_t; -// clone_map.addData(id_and_name, clones); -// } -// clones->put(item); -// } -// -// S32 count = 0; -// for(clones = clone_map.getFirstData(); -// clones != NULL; -// clones = clone_map.getNextData()) -// { -// count = clones->count(); -// for(S32 i = 0; i < count; i++) -// { -// item = clones->get(i); -// item->setCloneCount(count - 1); -// //clones[i] = NULL; -// } -// delete clones; -// } -// clone_map.removeAllData(); -// //dumpInventory(); -//} - // static bool LLInventoryModel::loadFromFile(const std::string& filename, LLInventoryModel::cat_array_t& categories, @@ -3097,6 +2810,10 @@ static LLInventoryModel::item_array_t::iterator find_item_iter_by_uuid(LLInvento } // static +// * @param[in, out] items - vector with items to be updated. It should be sorted in a right way +// * before calling this method. +// * @param src_item_id - LLUUID of inventory item to be moved in new position +// * @param dest_item_id - LLUUID of inventory item before which source item should be placed. void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id) { LLInventoryModel::item_array_t::iterator it_src = find_item_iter_by_uuid(items, src_item_id); @@ -3112,6 +2829,7 @@ void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, c items.insert(it_dest, src_item); } +//* @param[in] items vector of items in order to be saved. void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) { int sortField = 0; @@ -3155,6 +2873,8 @@ static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& i std::sort(items.begin(), items.end(), sort_functor); } +// * @param source_item_id - LLUUID of the source item to be moved into new position +// * @param target_item_id - LLUUID of the target item before which source item should be placed. void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) { LLInventoryModel::cat_array_t cats; diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index b7c1b57397..5f5d4d6118 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -58,17 +58,15 @@ class LLMessageSystem; class LLInventoryCollectFunctor; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryModel +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// LLInventoryModel // -// This class represents a collection of inventory, and provides -// efficient ways to access that information. This class could in -// theory be used for any place where you need inventory, though it -// optimizes for time efficiency - not space efficiency, probably -// making it inappropriate for use on tasks. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - +// Represents a collection of inventory, and provides efficient ways to access +// that information. +// NOTE: This class could in theory be used for any place where you need +// inventory, though it optimizes for time efficiency - not space efficiency, +// probably making it inappropriate for use on tasks. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLInventoryModel { public: @@ -84,79 +82,129 @@ public: typedef LLDynamicArray > cat_array_t; typedef LLDynamicArray > item_array_t; typedef std::set changed_items_t; - - // construction & destruction - LLInventoryModel(); - ~LLInventoryModel(); - - void cleanupInventory(); - + class fetchInventoryResponder : public LLHTTPClient::Responder { public: fetchInventoryResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; void result(const LLSD& content); - void error(U32 status, const std::string& reason); - - public: - typedef std::vector folder_ref_t; protected: LLSD mRequestSD; }; - // - // Accessors - // +/******************************************************************************** + ** ** + ** INITIALIZATION/SETUP + **/ - // Check if one object has a parent chain up to the category specified by UUID. - BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id) const; + //-------------------------------------------------------------------- + // Constructors / Destructors + //-------------------------------------------------------------------- +public: + LLInventoryModel(); + ~LLInventoryModel(); + void cleanupInventory(); +protected: + void empty(); // empty the entire contents - // Get whatever special folder this object is a child of, if any. - const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; + //-------------------------------------------------------------------- + // Initialization + //-------------------------------------------------------------------- +public: + // The inventory model usage is sensitive to the initial construction of the model + bool isInventoryUsable() const; +private: + bool mIsAgentInvUsable; // used to handle an invalid inventory state - // Get the object by id. Returns NULL if not found. - // * WARNING: use the pointer returned for read operations - do - // not modify the object values in place or you will break stuff. - LLInventoryObject* getObject(const LLUUID& id) const; + //-------------------------------------------------------------------- + // Root Folders + //-------------------------------------------------------------------- +public: + // The following are set during login with data from the server + void setRootFolderID(const LLUUID& id); + void setLibraryOwnerID(const LLUUID& id); + void setLibraryRootFolderID(const LLUUID& id); - // Get the item by id. Returns NULL if not found. - // * WARNING: use the pointer for read operations - use the - // updateItem() method to actually modify values. - LLViewerInventoryItem* getItem(const LLUUID& id) const; + const LLUUID &getRootFolderID() const; + const LLUUID &getLibraryOwnerID() const; + const LLUUID &getLibraryRootFolderID() const; +private: + LLUUID mRootFolderID; + LLUUID mLibraryRootFolderID; + LLUUID mLibraryOwnerID; + + //-------------------------------------------------------------------- + // Structure + //-------------------------------------------------------------------- +public: + // Methods to load up inventory skeleton & meat. These are used + // during authentication. Returns true if everything parsed. + bool loadSkeleton(const LLSD& options, const LLUUID& owner_id); + void buildParentChildMap(); // brute force method to rebuild the entire parent-child relations + // Call on logout to save a terse representation. + void cache(const LLUUID& parent_folder_id, const LLUUID& agent_id); +private: + // Information for tracking the actual inventory. We index this + // information in a lot of different ways so we can access + // the inventory using several different identifiers. + // mInventory member data is the 'master' list of inventory, and + // mCategoryMap and mItemMap store uuid->object mappings. + typedef std::map > cat_map_t; + typedef std::map > item_map_t; + cat_map_t mCategoryMap; + item_map_t mItemMap; + // This last set of indices is used to map parents to children. + typedef std::map parent_cat_map_t; + typedef std::map parent_item_map_t; + parent_cat_map_t mParentChildCategoryTree; + parent_item_map_t mParentChildItemTree; - // Get the category by id. Returns NULL if not found. - // * WARNING: use the pointer for read operations - use the - // updateCategory() method to actually modify values. - LLViewerInventoryCategory* getCategory(const LLUUID& id) const; + //-------------------------------------------------------------------- + // Login + //-------------------------------------------------------------------- +public: + static BOOL getIsFirstTimeInViewer2(); +private: + static BOOL sFirstTimeInViewer2; + const static S32 sCurrentInvCacheVersion; // expected inventory cache version - // Return the number of items or categories - S32 getItemCount() const; - S32 getCategoryCount() const; +/** Initialization/Setup + ** ** + *******************************************************************************/ + +/******************************************************************************** + ** ** + ** ACCESSORS + **/ + + //-------------------------------------------------------------------- + // Descendents + //-------------------------------------------------------------------- +public: + // Make sure we have the descendents in the structure. Returns true + // if a fetch was performed. + bool fetchDescendentsOf(const LLUUID& folder_id) const; // Return the direct descendents of the id provided.Set passed // in values to NULL if the call fails. - // *WARNING: The array provided points straight into the guts of - // this object, and should only be used for read operations, since - // modifications may invalidate the internal state of the - // inventory. + // NOTE: The array provided points straight into the guts of + // this object, and should only be used for read operations, since + // modifications may invalidate the internal state of the inventory. void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t*& categories, item_array_t*& items) const; - // SJB: Added version to lock the arrays to catch potential logic bugs - void lockDirectDescendentArrays(const LLUUID& cat_id, - cat_array_t*& categories, - item_array_t*& items); - void unlockDirectDescendentArrays(const LLUUID& cat_id); - // Starting with the object specified, add its descendents to the // array provided, but do not add the inventory object specified - // by id. There is no guaranteed order. Neither array will be - // erased before adding objects to it. Do not store a copy of the - // pointers collected - use them, and collect them again later if - // you need to reference the same objects. - enum { EXCLUDE_TRASH = FALSE, INCLUDE_TRASH = TRUE }; + // by id. There is no guaranteed order. + // NOTE: Neither array will be erased before adding objects to it. + // Do not store a copy of the pointers collected - use them, and + // collect them again later if you need to reference the same objects. + enum { + EXCLUDE_TRASH = FALSE, + INCLUDE_TRASH = TRUE + }; void collectDescendents(const LLUUID& id, cat_array_t& categories, item_array_t& items, @@ -172,156 +220,162 @@ public: // Assumes item_id is itself not a linked item. item_array_t collectLinkedItems(const LLUUID& item_id, const LLUUID& start_folder_id = LLUUID::null); + - // Get the inventoryID that this item points to, else just return item_id + // Check if one object has a parent chain up to the category specified by UUID. + BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id) const; + + //-------------------------------------------------------------------- + // Find + //-------------------------------------------------------------------- +public: + // Returns the uuid of the category that specifies 'type' as what it + // defaults to containing. The category is not necessarily only for that type. + // NOTE: If create_folder is true, this will create a new inventory category + // on the fly if one does not exist. *NOTE: if find_in_library is true it + // will search in the user's library folder instead of "My Inventory" + const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type, + bool create_folder = true, + bool find_in_library = false); + + // Get whatever special folder this object is a child of, if any. + const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; + + // Get the object by id. Returns NULL if not found. + // NOTE: Use the pointer returned for read operations - do + // not modify the object values in place or you will break stuff. + LLInventoryObject* getObject(const LLUUID& id) const; + + // Get the item by id. Returns NULL if not found. + // NOTE: Use the pointer for read operations - use the + // updateItem() method to actually modify values. + LLViewerInventoryItem* getItem(const LLUUID& id) const; + + // Get the category by id. Returns NULL if not found. + // NOTE: Use the pointer for read operations - use the + // updateCategory() method to actually modify values. + LLViewerInventoryCategory* getCategory(const LLUUID& id) const; + + // Get the inventoryID that this item points to, else just return item_id. const LLUUID& getLinkedItemID(const LLUUID& object_id) const; +private: + mutable LLPointer mLastItem; // cache recent lookups - // The inventory model usage is sensitive to the initial construction of the - // model. - bool isInventoryUsable() const; + //-------------------------------------------------------------------- + // Count + //-------------------------------------------------------------------- +public: + // Return the number of items or categories + S32 getItemCount() const; + S32 getCategoryCount() const; + +/** Accessors + ** ** + *******************************************************************************/ - // - // Mutators - // +/******************************************************************************** + ** ** + ** MUTATORS + **/ - // Calling this method with an inventory item will either change - // an existing item with a matching item_id, or will add the item +public: + // Change an existing item with a matching item_id or add the item // to the current inventory. Returns the change mask generated by // the update. No notification will be sent to observers. This // method will only generate network traffic if the item had to be // reparented. - // *NOTE: In usage, you will want to perform cache accounting - // operations in LLInventoryModel::accountForUpdate() or - // LLViewerInventoryItem::updateServer() before calling this - // method. + // NOTE: In usage, you will want to perform cache accounting + // operations in LLInventoryModel::accountForUpdate() or + // LLViewerInventoryItem::updateServer() before calling this method. U32 updateItem(const LLViewerInventoryItem* item); - // Calling this method with an inventory category will either - // change an existing item with the matching id, or it will add + // Change an existing item with the matching id or add // the category. No notifcation will be sent to observers. This // method will only generate network traffic if the item had to be // reparented. - // *NOTE: In usage, you will want to perform cache accounting - // operations in LLInventoryModel::accountForUpdate() or - // LLViewerInventoryCategory::updateServer() before calling this - // method. + // NOTE: In usage, you will want to perform cache accounting + // operations in accountForUpdate() or LLViewerInventoryCategory:: + // updateServer() before calling this method. void updateCategory(const LLViewerInventoryCategory* cat); - // This method will move the specified object id to the specified - // category, update the internal structures. No cache accounting, + // Move the specified object id to the specified category and + // update the internal structures. No cache accounting, // observer notification, or server update is performed. void moveObject(const LLUUID& object_id, const LLUUID& cat_id); - // delete a particular inventory object by ID. This will purge one - // object from the internal data structures maintaining a + //-------------------------------------------------------------------- + // Delete + //-------------------------------------------------------------------- +public: + // Delete a particular inventory object by ID. Will purge one + // object from the internal data structures, maintaining a // consistent internal state. No cache accounting, observer - // notification, or server update is performed. Purges linked items. + // notification, or server update is performed. void deleteObject(const LLUUID& id); + void removeItem(const LLUUID& item_id); - // delete a particular inventory object by ID, and delete it from - // the server. Also updates linked items. + // Delete a particular inventory object by ID, and delete it from + // the server. Also updates linked items. void purgeObject(const LLUUID& id); - void updateLinkedObjectsFromPurge(const LLUUID& baseobj_id); - // This is a method which collects the descendants of the id + // Collects and purges the descendants of the id // provided. If the category is not found, no action is // taken. This method goes through the long winded process of // removing server representation of folders and items while doing // cache accounting in a fairly efficient manner. This method does // not notify observers (though maybe it should...) void purgeDescendentsOf(const LLUUID& id); +protected: + void updateLinkedObjectsFromPurge(const LLUUID& baseobj_id); + + //-------------------------------------------------------------------- + // Reorder + //-------------------------------------------------------------------- +public: + // Changes items order by insertion of the item identified by src_item_id + // before the item identified by dest_item_id. Both items must exist in items array. + // Sorting is stored after method is finished. Only src_item_id is moved before dest_item_id. + static void updateItemsOrder(LLInventoryModel::item_array_t& items, + const LLUUID& src_item_id, + const LLUUID& dest_item_id); + + // Saves current order of the passed items using inventory item sort field. + // Resets 'items' sort fields and saves them on server. + // Is used to save order for Favorites folder. + void saveItemsOrder(const LLInventoryModel::item_array_t& items); - // This method optimally removes the referenced categories and - // items from the current agent's inventory in the database. It - // performs all of the during deletion. The local representation - // is not removed. - void deleteFromServer(LLDynamicArray& category_ids, - LLDynamicArray& item_ids); - - // Add/remove an observer. If the observer is destroyed, be sure - // to remove it. - void addObserver(LLInventoryObserver* observer); - void removeObserver(LLInventoryObserver* observer); - BOOL containsObserver(LLInventoryObserver* observer) const; - - // - // Misc Methods - // - - // findCategoryUUIDForType() returns the uuid of the category that - // specifies 'type' as what it defaults to containing. The - // category is not necessarily only for that type. *NOTE: If create_folder is true, this - // will create a new inventory category on the fly if one does not exist. *NOTE: if find_in_library is - // true it will search in the user's library folder instead of "My Inventory" - // SDK: Added flag to specify whether the folder should be created if not found. This fixes the horrible - // multiple trash can bug. - const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder = true, bool find_in_library = false); - - // This gets called by the idle loop. It only updates if new - // state is detected. Call notifyObservers() manually to update - // regardless of whether state change has been indicated. - void idleNotifyObservers(); - - // Call this method to explicitly update everyone on a new state. - // The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] - void notifyObservers(const std::string service_name=""); - - // This allows outsiders to tell the inventory if something has - // been changed 'under the hood', but outside the control of the - // inventory. For example, if we grant someone modify permissions, - // then that changes the data structures for LLAvatarTracker, but - // potentially affects inventory observers. This API makes sure - // that the next notify will include that notification. - void addChangedMask(U32 mask, const LLUUID& referent); - - const changed_items_t& getChangedIDs() const { return mChangedItemIDs; } - - // This method to prepares a set of mock inventory which provides - // minimal functionality before the actual arrival of inventory. - //void mock(const LLUUID& root_id); - - // Make sure we have the descendents in the structure. Returns true - // if a fetch was performed. - bool fetchDescendentsOf(const LLUUID& folder_id); - - // call this method to request the inventory. - //void requestFromServer(const LLUUID& agent_id); - - // call this method on logout to save a terse representation - void cache(const LLUUID& parent_folder_id, const LLUUID& agent_id); - - // Generates a string containing the path to the item specified by - // item_id. - void appendPath(const LLUUID& id, std::string& path) const; - - // message handling functionality - static void registerCallbacks(LLMessageSystem* msg); + // Rearranges Landmarks inside Favorites folder. + // Moves source landmark before target one. + void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); - // Convenience function to create a new category. You could call - // updateCatgory() with a newly generated UUID category, but this - // version will take care of details like what the name should be - // based on preferred type. Returns the UUID of the new - // category. If you want to use the default name based on type, - // pass in a NULL to the 'name parameter. + //-------------------------------------------------------------------- + // Creation + //-------------------------------------------------------------------- +public: + // Returns the UUID of the new category. If you want to use the default + // name based on type, pass in a NULL to the 'name' parameter. LLUUID createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& name); +protected: + // Internal methods that add inventory and make sure that all of + // the internal data structures are consistent. These methods + // should be passed pointers of newly created objects, and the + // instance will take over the memory management from there. + void addCategory(LLViewerInventoryCategory* category); + void addItem(LLViewerInventoryItem* item); + +/** Mutators + ** ** + *******************************************************************************/ - // methods to load up inventory skeleton & meat. These are used - // during authentication. return true if everything parsed. - bool loadSkeleton(const LLSD& options, const LLUUID& owner_id); - bool loadMeat(const LLSD& options, const LLUUID& owner_id); - - // This is a brute force method to rebuild the entire parent-child - // relations. - void buildParentChildMap(); - - // - // Category accounting. - // +/******************************************************************************** + ** ** + ** CATEGORY ACCOUNTING + **/ - // This structure represents the number of items added or removed - // from a category. +public: + // Represents the number of items added or removed from a category. struct LLCategoryUpdate { LLCategoryUpdate() : mDescendentDelta(0) {} @@ -333,8 +387,7 @@ public: }; typedef std::vector update_list_t; - // This structure eixts to make it easier to account for deltas in - // a map. + // This exists to make it easier to account for deltas in a map. struct LLInitializedS32 { LLInitializedS32() : mValue(0) {} @@ -345,102 +398,89 @@ public: }; typedef std::map update_map_t; - // Call these methods when there are category updates, but call - // them *before* the actual update so the method can do descendent - // accounting correctly. + // Call when there are category updates. Call them *before* the + // actual update so the method can do descendent accounting correctly. void accountForUpdate(const LLCategoryUpdate& update) const; void accountForUpdate(const update_list_t& updates); void accountForUpdate(const update_map_t& updates); - // Return child status of category children. yes/no/maybe + // Return (yes/no/maybe) child status of category children. EHasChildren categoryHasChildren(const LLUUID& cat_id) const; - // returns true iff category version is known and theoretical + // Returns true iff category version is known and theoretical // descendents == actual descendents. bool isCategoryComplete(const LLUUID& cat_id) const; - // callbacks - // Trigger a notification and empty the folder type (FT_TRASH or FT_LOST_AND_FOUND) if confirmed - void emptyFolderType(const std::string notification, LLFolderType::EType folder_type); - bool callbackEmptyFolderType(const LLSD& notification, const LLSD& response, LLFolderType::EType preferred_type); - - // Utility Functions - void removeItem(const LLUUID& item_id); - - // Data about the agent's root folder and root library folder - // are stored here, rather than in LLAgent where it used to be, because - // gInventory is a singleton and represents the agent's inventory. - // The "library" is actually the inventory of a special agent, - // usually Alexandria Linden. - const LLUUID &getRootFolderID() const; - const LLUUID &getLibraryOwnerID() const; - const LLUUID &getLibraryRootFolderID() const; - - // These are set during login with data from the server - void setRootFolderID(const LLUUID& id); - void setLibraryOwnerID(const LLUUID& id); - void setLibraryRootFolderID(const LLUUID& id); +/** Category Accounting + ** ** + *******************************************************************************/ +/******************************************************************************** + ** ** + ** NOTIFICATIONS + **/ - /** - * Changes items order by insertion of the item identified by src_item_id - * BEFORE the item identified by dest_item_id. Both items must exist in items array. - * - * Sorting is stored after method is finished. Only src_item_id is moved before dest_item_id. - * - * @param[in, out] items - vector with items to be updated. It should be sorted in a right way - * before calling this method. - * @param src_item_id - LLUUID of inventory item to be moved in new position - * @param dest_item_id - LLUUID of inventory item before which source item should be placed. - */ - static void updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id); - - /** - * Saves current order of the passed items using inventory item sort field. - * - * It reset items' sort fields and saves them on server. - * Is used to save order for Favorites folder. - * - * @param[in] items vector of items in order to be saved. - */ - void saveItemsOrder(const LLInventoryModel::item_array_t& items); +public: + // Called by the idle loop. Only updates if new state is detected. Call + // notifyObservers() manually to update regardless of whether state change + // has been indicated. + void idleNotifyObservers(); - /** - * Rearranges Landmarks inside Favorites folder. - * Moves source landmark before target one. - * - * @param source_item_id - LLUUID of the source item to be moved into new position - * @param target_item_id - LLUUID of the target item before which source item should be placed. - */ - void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); + // Call to explicitly update everyone on a new state. The optional argument + // 'service_name' is used by Agent Inventory Service [DEV-20328] + void notifyObservers(const std::string service_name=""); + // Allows outsiders to tell the inventory if something has + // been changed 'under the hood', but outside the control of the + // inventory. The next notify will include that notification. + void addChangedMask(U32 mask, const LLUUID& referent); + const changed_items_t& getChangedIDs() const { return mChangedItemIDs; } protected: + // Updates all linked items pointing to this id. + void addChangedMaskForLinks(const LLUUID& object_id, U32 mask); +private: + // Flag set when notifyObservers is being called, to look for bugs + // where it's called recursively. + BOOL mIsNotifyObservers; + // Variables used to track what has changed since the last notify. + U32 mModifyMask; + changed_items_t mChangedItemIDs; + + //-------------------------------------------------------------------- + // Observers + //-------------------------------------------------------------------- +public: + // If the observer is destroyed, be sure to remove it. + void addObserver(LLInventoryObserver* observer); + void removeObserver(LLInventoryObserver* observer); + BOOL containsObserver(LLInventoryObserver* observer) const; +private: + typedef std::set observer_list_t; + observer_list_t mObservers; + +/** Notifications + ** ** + *******************************************************************************/ - // Internal methods which add inventory and make sure that all of - // the internal data structures are consistent. These methods - // should be passed pointers of newly created objects, and the - // instance will take over the memory management from there. - void addCategory(LLViewerInventoryCategory* category); - void addItem(LLViewerInventoryItem* item); - - // ! DEPRECRATE ! Remove this and add it into findCategoryUUIDForType, - // since that's the only function that uses this. It's too confusing - // having both methods. - // - // Internal method which looks for a category with the specified - // preferred type. Returns LLUUID::null if not found - const LLUUID &findCatUUID(LLFolderType::EType preferred_type, bool find_in_library = false) const; - // Empty the entire contents - void empty(); +/******************************************************************************** + ** ** + ** MISCELLANEOUS + **/ - // Given the current state of the inventory items, figure out the - // clone information. *FIX: This is sub-optimal, since we can - // insert this information snurgically, but this makes sure the - // implementation works before we worry about optimization. - //void recalculateCloneInformation(); + //-------------------------------------------------------------------- + // Callbacks + //-------------------------------------------------------------------- +public: + // Trigger a notification and empty the folder type (FT_TRASH or FT_LOST_AND_FOUND) if confirmed + void emptyFolderType(const std::string notification, LLFolderType::EType folder_type); + bool callbackEmptyFolderType(const LLSD& notification, const LLSD& response, LLFolderType::EType preferred_type); + static void registerCallbacks(LLMessageSystem* msg); - // file import/export. + //-------------------------------------------------------------------- + // File I/O + //-------------------------------------------------------------------- +protected: static bool loadFromFile(const std::string& filename, cat_array_t& categories, item_array_t& items, @@ -449,86 +489,46 @@ protected: const cat_array_t& categories, const item_array_t& items); - // message handling functionality - //static void processUseCachedInventory(LLMessageSystem* msg, void**); + //-------------------------------------------------------------------- + // Message handling functionality + //-------------------------------------------------------------------- +public: static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**); static void processRemoveInventoryItem(LLMessageSystem* msg, void**); static void processUpdateInventoryFolder(LLMessageSystem* msg, void**); static void processRemoveInventoryFolder(LLMessageSystem* msg, void**); - //static void processExchangeCallingcard(LLMessageSystem* msg, void**); - //static void processAddCallingcard(LLMessageSystem* msg, void**); - //static void processDeclineCallingcard(LLMessageSystem* msg, void**); static void processSaveAssetIntoInventory(LLMessageSystem* msg, void**); static void processBulkUpdateInventory(LLMessageSystem* msg, void**); static void processInventoryDescendents(LLMessageSystem* msg, void**); static void processMoveInventoryItem(LLMessageSystem* msg, void**); static void processFetchInventoryReply(LLMessageSystem* msg, void**); - +protected: bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting); - // Updates all linked items pointing to this id. - void addChangedMaskForLinks(const LLUUID& object_id, U32 mask); - + //-------------------------------------------------------------------- + // Locks + //-------------------------------------------------------------------- +public: + void lockDirectDescendentArrays(const LLUUID& cat_id, + cat_array_t*& categories, + item_array_t*& items); + void unlockDirectDescendentArrays(const LLUUID& cat_id); protected: cat_array_t* getUnlockedCatArray(const LLUUID& id); item_array_t* getUnlockedItemArray(const LLUUID& id); - private: - // Variables used to track what has changed since the last notify. - U32 mModifyMask; - changed_items_t mChangedItemIDs; - std::map mCategoryLock; std::map mItemLock; - // cache recent lookups - mutable LLPointer mLastItem; - - // This last set of indices is used to map parents to children. - typedef std::map parent_cat_map_t; - typedef std::map parent_item_map_t; - parent_cat_map_t mParentChildCategoryTree; - parent_item_map_t mParentChildItemTree; - - typedef std::set observer_list_t; - observer_list_t mObservers; - - // Agent inventory folder information. - LLUUID mRootFolderID; - LLUUID mLibraryRootFolderID; - LLUUID mLibraryOwnerID; - - // Expected inventory cache version - const static S32 sCurrentInvCacheVersion; - - // This flag is used to handle an invalid inventory state. - bool mIsAgentInvUsable; - -private: - // Information for tracking the actual inventory. We index this - // information in a lot of different ways so we can access - // the inventory using several different identifiers. - // mInventory member data is the 'master' list of inventory, and - // mCategoryMap and mItemMap store uuid->object mappings. - typedef std::map > cat_map_t; - typedef std::map > item_map_t; - //inv_map_t mInventory; - cat_map_t mCategoryMap; - item_map_t mItemMap; - - // Flag set when notifyObservers is being called, to look for bugs - // where it's called recursively. - BOOL mIsNotifyObservers; + //-------------------------------------------------------------------- + // Debugging + //-------------------------------------------------------------------- public: - // *NOTE: DEBUG functionality void dumpInventory() const; - //////////////////////////////////////////////////////////////////////////////// - // Login status -public: - static BOOL getIsFirstTimeInViewer2(); -private: - static BOOL sFirstTimeInViewer2; +/** Miscellaneous + ** ** + *******************************************************************************/ }; // a special inventory model for the agent diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 9533d820eb..24bd2739f7 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -71,9 +71,10 @@ class LLTexGlobalColor; class LLVOAvatarBoneInfo; class LLVOAvatarSkeletonInfo; -//------------------------------------------------------------------------ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LLVOAvatar -//------------------------------------------------------------------------ +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLVOAvatar : public LLViewerObject, public LLCharacter @@ -138,13 +139,13 @@ public: virtual void updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax); virtual void getSpatialExtents(LLVector3& newMin, LLVector3& newMax); virtual BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, - S32 face = -1, // which face to check, -1 = ALL_SIDES - BOOL pick_transparent = FALSE, - S32* face_hit = NULL, // which face was hit - LLVector3* intersection = NULL, // return the intersection point - LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point - LLVector3* normal = NULL, // return the surface normal at the intersection point - LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point + S32 face = -1, // which face to check, -1 = ALL_SIDES + BOOL pick_transparent = FALSE, + S32* face_hit = NULL, // which face was hit + LLVector3* intersection = NULL, // return the intersection point + LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point + LLVector3* normal = NULL, // return the surface normal at the intersection point + LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point //-------------------------------------------------------------------- // LLCharacter interface and related diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index e5949ae941..460291a929 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -39,9 +39,10 @@ struct LocalTextureData; -//------------------------------------------------------------------------ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LLVOAvatarSelf -//------------------------------------------------------------------------ +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class LLVOAvatarSelf : public LLVOAvatar { -- cgit v1.3 From cb0589715265dc1568626fe238aac7417b44ef89 Mon Sep 17 00:00:00 2001 From: Vadim Savchuk Date: Tue, 25 May 2010 14:17:11 +0300 Subject: EXT-6726 WIP Added handlers for most of Appearance SP context/gear menus. Reviewed by Mike Antipov and Nyx at https://codereview.productengine.com/secondlife/r/428/ --HG-- branch : product-engine --- indra/llui/llview.cpp | 2 - indra/newview/llagentwearables.cpp | 62 +++++++++-- indra/newview/llagentwearables.h | 14 ++- indra/newview/llappearancemgr.cpp | 88 +++++++++++----- indra/newview/llappearancemgr.h | 6 +- indra/newview/llcofwearables.cpp | 69 ++++++++++++ indra/newview/llinventorybridge.cpp | 15 +-- indra/newview/llinventoryfunctions.cpp | 29 ++++++ indra/newview/llinventoryfunctions.h | 14 +++ indra/newview/llinventorymodel.cpp | 5 + indra/newview/llinventorymodel.h | 1 + indra/newview/lloutfitslist.cpp | 15 +++ indra/newview/llpanelobjectinventory.cpp | 6 +- indra/newview/llwearableitemslist.cpp | 116 ++++++++++++++++++++- indra/newview/llwearableitemslist.h | 9 ++ .../skins/default/xui/en/panel_outfit_edit.xml | 1 + 16 files changed, 397 insertions(+), 55 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index c091686ffb..bd56da9121 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1319,8 +1319,6 @@ void LLView::drawChildren() if (viewp->getVisible() && viewp->getRect().isValid()) { - // check for bad data - llassert_always(viewp->getVisible() == TRUE); // Only draw views that are within the root view localRectToScreen(viewp->getRect(),&screenRect); if ( rootRect.overlaps(screenRect) && LLUI::sDirtyRect.overlaps(screenRect)) diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 6b5e43973e..68c4fa1ea0 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -49,6 +49,7 @@ #include "llmd5.h" #include "llnotificationsutil.h" #include "llpaneloutfitsinventory.h" +#include "llsidepanelappearance.h" #include "llsidetray.h" #include "lltexlayer.h" #include "llviewerregion.h" @@ -539,9 +540,15 @@ void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string& BOOL LLAgentWearables::isWearableModifiable(LLWearableType::EType type, U32 index) const { LLUUID item_id = getWearableItemID(type, index); - if (!item_id.isNull()) + return item_id.notNull() ? isWearableModifiable(item_id) : FALSE; +} + +BOOL LLAgentWearables::isWearableModifiable(const LLUUID& item_id) const +{ + const LLUUID& linked_id = gInventory.getLinkedItemID(item_id); + if (linked_id.notNull()) { - LLInventoryItem* item = gInventory.getItem(item_id); + LLInventoryItem* item = gInventory.getItem(linked_id); if (item && item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID())) { @@ -595,12 +602,13 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::ETyp const LLWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const { + const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); for (S32 i=0; i < LLWearableType::WT_COUNT; i++) { for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++) { const LLWearable * curr_wearable = getWearable((LLWearableType::EType)i, j); - if (curr_wearable && (curr_wearable->getItemID() == item_id)) + if (curr_wearable && (curr_wearable->getItemID() == base_item_id)) { return curr_wearable; } @@ -812,6 +820,16 @@ LLWearable* LLAgentWearables::getTopWearable(const LLWearableType::EType type) return getWearable(type, count-1); } +LLWearable* LLAgentWearables::getBottomWearable(const LLWearableType::EType type) +{ + if (getWearableCount(type) == 0) + { + return NULL; + } + + return getWearable(type, 0); +} + U32 LLAgentWearables::getWearableCount(const LLWearableType::EType type) const { wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type); @@ -860,12 +878,7 @@ const LLUUID LLAgentWearables::getWearableAssetID(LLWearableType::EType type, U3 BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const { - const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - if (getWearableFromItemID(base_item_id) != NULL) - { - return TRUE; - } - return FALSE; + return getWearableFromItemID(item_id) != NULL; } // MULTI-WEARABLE: DEPRECATED (see backwards compatibility) @@ -1862,6 +1875,20 @@ void LLAgentWearables::checkWearablesLoaded() const #endif } +// Returns false if the given wearable is already topmost/bottommost +// (depending on closer_to_body parameter). +bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_body) +{ + const LLWearable* wearable = getWearableFromItemID(item_id); + if (!wearable) return false; + + LLWearableType::EType wtype = wearable->getType(); + const LLWearable* marginal_wearable = closer_to_body ? getBottomWearable(wtype) : getTopWearable(wtype); + if (!marginal_wearable) return false; + + return wearable != marginal_wearable; +} + BOOL LLAgentWearables::areWearablesLoaded() const { checkWearablesLoaded(); @@ -1932,6 +1959,23 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos return false; } +// static +void LLAgentWearables::editWearable(const LLUUID& item_id) +{ + LLViewerInventoryItem* item; + LLWearable* wearable; + + if ((item = gInventory.getLinkedItem(item_id)) && + (wearable = gAgentWearables.getWearableFromAssetID(item->getAssetUUID())) && + gAgentWearables.isWearableModifiable(item->getUUID()) && + item->isFinished()) + { + LLPanel* panel = LLSideTray::getInstance()->showPanel("panel_outfit_edit", LLSD()); + // copied from LLPanelOutfitEdit::onEditWearableClicked() + LLSidepanelAppearance::editWearable(wearable, panel->getParent()); + } +} + void LLAgentWearables::updateServer() { sendAgentWearablesUpdate(); diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 734bd9fd47..1f19d1045b 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -71,17 +71,18 @@ protected: public: BOOL isWearingItem(const LLUUID& item_id) const; BOOL isWearableModifiable(LLWearableType::EType type, U32 index /*= 0*/) const; + BOOL isWearableModifiable(const LLUUID& item_id) const; + BOOL isWearableCopyable(LLWearableType::EType type, U32 index /*= 0*/) const; BOOL areWearablesLoaded() const; void updateWearablesLoaded(); void checkWearablesLoaded() const; + bool canMoveWearable(const LLUUID& item_id, bool closer_to_body); // Note: False for shape, skin, eyes, and hair, unless you have MORE than 1. bool canWearableBeRemoved(const LLWearable* wearable) const; void animateAllWearableParams(F32 delta, BOOL upload_bake); - - bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); //-------------------------------------------------------------------- // Accessors @@ -96,6 +97,7 @@ public: LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/); const LLWearable* getWearable(const LLWearableType::EType type, U32 index /*= 0*/) const; LLWearable* getTopWearable(const LLWearableType::EType type); + LLWearable* getBottomWearable(const LLWearableType::EType type); U32 getWearableCount(const LLWearableType::EType type) const; U32 getWearableCount(const U32 tex_index) const; @@ -133,6 +135,14 @@ protected: void recoverMissingWearable(const LLWearableType::EType type, U32 index /*= 0*/); void recoverMissingWearableDone(); + //-------------------------------------------------------------------- + // Editing/moving wearables + //-------------------------------------------------------------------- + +public: + static void editWearable(const LLUUID& item_id); + bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); + //-------------------------------------------------------------------- // Removing wearables //-------------------------------------------------------------------- diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index dcef86a5fc..2eb7cfd34a 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -152,27 +152,6 @@ public: }; -//Inventory collect functor collecting wearables of a specific wearable type -class LLFindClothesOfType : public LLInventoryCollectFunctor -{ -public: - LLFindClothesOfType(LLWearableType::EType type) : mWearableType(type) {} - virtual ~LLFindClothesOfType() {} - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) - { - if (!item) return false; - if (item->getType() != LLAssetType::AT_CLOTHING) return false; - - LLViewerInventoryItem *vitem = dynamic_cast(item); - if (!vitem || vitem->getWearableType() != mWearableType) return false; - - return true; - } - - const LLWearableType::EType mWearableType; -}; - - LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(): mFireCount(0) { @@ -671,7 +650,7 @@ const LLUUID LLAppearanceMgr::getBaseOutfitUUID() return outfit_cat->getUUID(); } -bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update) +bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace) { if (item_id_to_wear.isNull()) return false; @@ -692,6 +671,14 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up LLNotificationsUtil::add("CanNotChangeAppearanceUntilLoaded"); return false; } + + // Remove the existing wearables of the same type. + // Remove existing body parts anyway because we must not be able to wear e.g. two skins. + if (replace || item_to_wear->getType() == LLAssetType::AT_BODYPART) + { + removeCOFLinksOfType(item_to_wear->getWearableType(), false); + } + addCOFItemLink(item_to_wear, do_update); break; case LLAssetType::AT_OBJECT: @@ -711,6 +698,35 @@ void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool ap LLAppearanceMgr::instance().updateCOF(category,append); } +void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit); + wearInventoryCategory(cat, false, false); +} + +void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id) +{ + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + wearInventoryCategory(cat, false, true); +} + +void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearables collector; + + gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); + + LLInventoryModel::item_array_t::const_iterator it = items.begin(); + const LLInventoryModel::item_array_t::const_iterator it_end = items.end(); + for( ; it_end != it; ++it) + { + LLViewerInventoryItem* item = *it; + removeItemFromAvatar(item->getUUID()); + } +} + // Create a copy of src_id + contents as a subfolder of dst_id. void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, LLPointer cb) @@ -1563,6 +1579,29 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update) } } +void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update) +{ + LLFindWearablesOfType filter_wearables_of_type(type); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLInventoryModel::item_array_t::const_iterator it; + + gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type); + for (it = items.begin(); it != items.end(); ++it) + { + const LLViewerInventoryItem* item = *it; + if (item->getIsLinkType()) // we must operate on links only + { + gInventory.purgeObject(item->getUUID()); + } + } + + if (do_update) + { + updateAppearanceFromCOF(); + } +} + bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2) { if (!item1 || !item2) @@ -1893,7 +1932,6 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove) } } - bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body) { if (!item || !item->isWearableType()) return false; @@ -1902,11 +1940,11 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLFindClothesOfType filter_wearables_of_type(item->getWearableType()); + LLFindWearablesOfType filter_wearables_of_type(item->getWearableType()); gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type); if (items.empty()) return false; - //*TODO all items are not guarantied to have valid descriptions (check?) + // We assume that the items have valid descriptions. std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType())); if (closer_to_body && items.front() == item) return false; diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 516dada39d..96541beb7d 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -58,6 +58,9 @@ public: void wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append); void wearOutfitByName(const std::string& name); void changeOutfit(bool proceed, const LLUUID& category, bool append); + void replaceCurrentOutfit(const LLUUID& new_outfit); + void takeOffOutfit(const LLUUID& cat_id); + void addCategoryToCurrentOutfit(const LLUUID& cat_id); // Copy all items and the src category itself. void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, @@ -81,7 +84,7 @@ public: const LLUUID getBaseOutfitUUID(); // Wear/attach an item (from a user's inventory) on the agent - bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true); + bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true, bool replace = false); // Update the displayed outfit name in UI. void updatePanelOutfitName(const std::string& name); @@ -111,6 +114,7 @@ public: // Remove COF entries void removeCOFItemLinks(const LLUUID& item_id, bool do_update = true); + void removeCOFLinksOfType(LLWearableType::EType type, bool do_update = true); // Add COF link to ensemble folder. void addEnsembleLink(LLInventoryCategory* item, bool do_update = true); diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index 47862ad921..dfc203111a 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -61,6 +61,11 @@ protected: /*virtual*/ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + + functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); + registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, mUUIDs)); + return createFromFile("menu_cof_attachment.xml"); } }; @@ -73,8 +78,49 @@ protected: /*virtual*/ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.back(); + functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); + + registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, mUUIDs)); + registrar.add("Clothing.MoveUp", boost::bind(moveWearable, selected_id, false)); + registrar.add("Clothing.MoveDown", boost::bind(moveWearable, selected_id, true)); + registrar.add("Clothing.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); + + enable_registrar.add("Clothing.OnEnable", boost::bind(&CofClothingContextMenu::onEnable, this, _2)); + return createFromFile("menu_cof_clothing.xml"); } + + bool onEnable(const LLSD& data) + { + std::string param = data.asString(); + LLUUID selected_id = mUUIDs.back(); + + if ("move_up" == param) + { + return gAgentWearables.canMoveWearable(selected_id, false); + } + else if ("move_down" == param) + { + return gAgentWearables.canMoveWearable(selected_id, true); + } + else if ("edit" == param) + { + return gAgentWearables.isWearableModifiable(selected_id); + } + return true; + } + + // We don't use LLAppearanceMgr::moveWearable() directly because + // the item may be invalidated between setting the callback and calling it. + static bool moveWearable(const LLUUID& item_id, bool closer_to_body) + { + LLViewerInventoryItem* item = gInventory.getItem(item_id); + return LLAppearanceMgr::instance().moveWearable(item, closer_to_body); + } + }; ////////////////////////////////////////////////////////////////////////// @@ -85,8 +131,31 @@ protected: /*virtual*/ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.back(); + + registrar.add("BodyPart.Replace", boost::bind(&LLAppearanceMgr::wearItemOnAvatar, + LLAppearanceMgr::getInstance(), selected_id, true, true)); + registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); + + enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2)); + return createFromFile("menu_cof_body_part.xml"); } + + bool onEnable(const LLSD& data) + { + std::string param = data.asString(); + LLUUID selected_id = mUUIDs.back(); + + if ("edit" == param) + { + return gAgentWearables.isWearableModifiable(selected_id); + } + + return true; + } }; ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 2d27c89074..35f7cbcd01 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -222,9 +222,7 @@ void LLInvFVBridge::cutToClipboard() // *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { - LLSD key; - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); + show_item_profile(mUUID); // Disable old properties floater; this is replaced by the sidepanel. /* @@ -4150,9 +4148,7 @@ void LLObjectBridge::openItem() LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); } - LLSD key; - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); + show_item_profile(mUUID); // Disable old properties floater; this is replaced by the sidepanel. /* @@ -4434,7 +4430,7 @@ void wear_inventory_item_on_avatar( LLInventoryItem* item ) lldebugs << "wear_inventory_item_on_avatar( " << item->getName() << " )" << llendl; - LLAppearanceMgr::instance().addCOFItemLink(item); + LLAppearanceMgr::getInstance()->wearItemOnAvatar(item->getUUID(), true, false); } } @@ -4892,8 +4888,7 @@ void LLWearableBridge::onEditOnAvatar(void* user_data) void LLWearableBridge::editOnAvatar() { - LLUUID linked_id = gInventory.getLinkedItemID(mUUID); - const LLWearable* wearable = gAgentWearables.getWearableFromItemID(linked_id); + const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID); if( wearable ) { // Set the tab to the right wearable. @@ -4983,7 +4978,7 @@ void LLWearableBridge::removeAllClothesFromAvatar() gAgentWearables.getWearableInventoryItem((LLWearableType::EType)itype, index)); if (!item) continue; - const LLUUID &item_id = gInventory.getLinkedItemID(item->getUUID()); + const LLUUID &item_id = item->getUUID(); const LLWearable *wearable = gAgentWearables.getWearableFromItemID(item_id); if (!wearable) continue; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 2b4d9fb25c..c38d45f0f5 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -74,6 +74,7 @@ #include "llscrollbar.h" #include "llscrollcontainer.h" #include "llselectmgr.h" +#include "llsidetray.h" #include "lltabcontainer.h" #include "lltooldraganddrop.h" #include "lluictrlfactory.h" @@ -160,6 +161,19 @@ BOOL get_is_item_worn(const LLUUID& id) return FALSE; } +void show_item_profile(const LLUUID& item_uuid) +{ + LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); + LLSideTray::getInstance()->showPanel("sidepanel_inventory", LLSD().with("id", linked_uuid)); +} + +void show_item_original(const LLUUID& item_uuid) +{ + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (!active_panel) return; + active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO); +} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -343,6 +357,21 @@ bool LLFindWearables::operator()(LLInventoryCategory* cat, return FALSE; } +bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (!item) return false; + if (item->getType() != LLAssetType::AT_CLOTHING && + item->getType() != LLAssetType::AT_BODYPART) + { + return false; + } + + LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem || vitem->getWearableType() != mWearableType) return false; + + return true; +} + ///---------------------------------------------------------------------------- /// LLAssetIDMatches ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 79b9b4a9cc..8b96ba29d9 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -46,6 +46,9 @@ // Is this item or its baseitem is worn, attached, etc... BOOL get_is_item_worn(const LLUUID& id); +void show_item_profile(const LLUUID& item_uuid); + +void show_item_original(const LLUUID& item_uuid); void change_item_parent(LLInventoryModel* model, LLViewerInventoryItem* item, @@ -262,6 +265,17 @@ public: LLInventoryItem* item); }; +//Inventory collect functor collecting wearables of a specific wearable type +class LLFindWearablesOfType : public LLInventoryCollectFunctor +{ +public: + LLFindWearablesOfType(LLWearableType::EType type) : mWearableType(type) {} + virtual ~LLFindWearablesOfType() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); + + const LLWearableType::EType mWearableType; +}; + /** Inventory Collector Functions ** ** *******************************************************************************/ diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index c373512ace..23df6b6cc5 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -566,6 +566,11 @@ const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const return item->getLinkedUUID(); } +LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const +{ + return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL; +} + LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id, const LLUUID& start_folder_id) { diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 1f7bd50328..7b56d0bdd1 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -258,6 +258,7 @@ public: // Get the inventoryID or item that this item points to, else just return object_id const LLUUID& getLinkedItemID(const LLUUID& object_id) const; + LLViewerInventoryItem* getLinkedItem(const LLUUID& object_id) const; private: mutable LLPointer mLastItem; // cache recent lookups diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 36832c9d16..17a2db7a43 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -38,6 +38,7 @@ #include "llaccordionctrl.h" #include "llaccordionctrltab.h" +#include "llagentwearables.h" #include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" @@ -55,6 +56,20 @@ class OutfitContextMenu : public LLListContextMenu protected: /* virtual */ LLContextMenu* createMenu() { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLUUID selected_id = mUUIDs.front(); + + registrar.add("Outfit.WearReplace", + boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.WearAdd", + boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); + registrar.add("Outfit.TakeOff", + boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); + // *TODO: implement this + // registrar.add("Outfit.Rename", boost::bind()); + // registrar.add("Outfit.Delete", boost::bind()); + return createFromFile("menu_outfit_tab.xml"); } }; diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 39ade40967..0d3beaa9a5 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -174,11 +174,7 @@ LLInventoryItem* LLTaskInvFVBridge::findItem() const void LLTaskInvFVBridge::showProperties() { - LLSD key; - key["object"] = mPanel->getTaskUUID(); - key["id"] = mUUID; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); - + show_item_profile(mUUID); // Disable old properties floater; this is replaced by the sidepanel. /* diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index 5836252eac..fb7577c008 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -36,6 +36,7 @@ #include "lliconctrl.h" #include "llagentwearables.h" +#include "llappearancemgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llmenugl.h" // for LLContextMenu @@ -433,7 +434,120 @@ void LLWearableItemsList::onRightClick(S32 x, S32 y) // virtual LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() { - return createFromFile("menu_wearable_list_item.xml"); + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + const uuid_vec_t& ids = mUUIDs; // selected items IDs + LLUUID selected_id = ids.front(); // ID of the first selected item + + functor_t wear = boost::bind(&LLAppearanceMgr::wearItemOnAvatar, LLAppearanceMgr::getInstance(), _1, true, false); + functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1); + + // Register handlers common for all wearable types. + registrar.add("Wearable.Wear", boost::bind(handleMultiple, wear, ids)); + registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids)); + registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id)); + + // Register handlers for clothing. + registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, ids)); + + // Register handlers for body parts. + + // Register handlers for attachments. + registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, ids)); + registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id)); + + // Create the menu. + LLContextMenu* menu = createFromFile("menu_wearable_list_item.xml"); + + // Determine which items should be visible/enabled. + updateItemsVisibility(menu); + return menu; +} + +void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu) +{ + if (!menu) + { + llwarns << "Invalid menu" << llendl; + return; + } + + const uuid_vec_t& ids = mUUIDs; // selected items IDs + U32 mask = 0; // mask of selected items' types + U32 nitems = ids.size(); // number of selected items + U32 nworn = 0; // number of worn items among the selected ones + U32 nwornlinks = 0; // number of worn links among the selected items + U32 neditable = 0; // number of editable items among the selected ones + + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + LLUUID id = *it; + LLViewerInventoryItem* item = gInventory.getItem(id); + + if (!item) + { + llwarns << "Invalid item" << llendl; + // *NOTE: the logic below may not work in this case + continue; + } + + updateMask(mask, item->getType()); + + bool is_link = item->getIsLinkType(); + bool is_worn = get_is_item_worn(id); + bool is_editable = gAgentWearables.isWearableModifiable(id); + + if (is_worn) + { + ++nworn; + + if (is_link) + { + ++nwornlinks; + } + } + if (is_editable) + { + ++neditable; + } + } // for + + // *TODO: eliminate multiple traversals over the menu items + // *TODO: try disabling items rather than hiding them + // *FIX: we may hide *all* items and thus get an ugly empty menu + setMenuItemVisible(menu, "wear", nworn == 0); + setMenuItemVisible(menu, "edit", mask & (MASK_CLOTHING|MASK_BODYPART) && nitems == 1 && neditable == 1); + setMenuItemVisible(menu, "show_original", nitems == 1 && nwornlinks == nitems); + setMenuItemVisible(menu, "take_off", mask == MASK_CLOTHING && nworn == nitems); // selected only worn clothes + setMenuItemVisible(menu, "detach", mask == MASK_ATTACHMENT && nworn == nitems); + setMenuItemVisible(menu, "object_profile", mask == MASK_ATTACHMENT && nitems == 1); +} + +// We need this method to convert non-zero BOOL values to exactly 1 (TRUE). +// Otherwise code relying on a BOOL value being TRUE may fail +// (I experienced a weird assert in LLView::drawChildren() because of that. +void LLWearableItemsList::ContextMenu::setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val) +{ + menu->setItemVisible(name, val); +} + +void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType at) +{ + if (at == LLAssetType::AT_CLOTHING) + { + mask |= MASK_CLOTHING; + } + else if (at == LLAssetType::AT_BODYPART) + { + mask |= MASK_BODYPART; + } + else if (at == LLAssetType::AT_OBJECT) + { + mask |= MASK_ATTACHMENT; + } + else + { + llwarns << "Unsupported asset type: " << at << llendl; + } } // EOF diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index d7b09ca934..7ad1b5a3ad 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -287,7 +287,16 @@ public: class ContextMenu : public LLListContextMenu, public LLSingleton { protected: + enum { + MASK_CLOTHING = 0x01, + MASK_BODYPART = 0x02, + MASK_ATTACHMENT = 0x04, + }; + /* virtual */ LLContextMenu* createMenu(); + void updateItemsVisibility(LLContextMenu* menu); + void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val); + void updateMask(U32& mask, LLAssetType::EType at); }; struct Params : public LLInitParam::Block diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index b473a7a282..895cc4e3cc 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -336,6 +336,7 @@ allow_select="true" layout="topleft" follows="all" + multi_select="true" width="310" height="140" left="0" -- cgit v1.3 From cd46893057b9ec7d309059eff3f503cfaf46de56 Mon Sep 17 00:00:00 2001 From: "Nyx (Neal Orman)" Date: Thu, 27 May 2010 14:40:49 -0400 Subject: EXT-7209 EXT-7366 EXT-7213 FIX EXT-7392 WIP Final fixes for: EXT-7209 camera to enter appearance edit mode EXT-7366 change camera to face front of avatar in previews EXT-7213 kill old appearance editor Partial fix for: EXT-7392 alpha mask checkboxes are not hooked up All changes code reviewed by vir. Following commits (plus this one) reviewed with this checkin: 425d4e960450 15d04b6464a7 e92ae606de12 adc94512ea9b 24577dbbf7a4 6f28b241eae2 (sorry dessie! :) ) --- indra/newview/CMakeLists.txt | 2 -- indra/newview/llagent.cpp | 2 +- indra/newview/llagentcamera.cpp | 2 +- indra/newview/llagentwearables.cpp | 1 - indra/newview/llappearancemgr.cpp | 1 - indra/newview/llinventorybridge.cpp | 2 +- indra/newview/llinventoryfunctions.cpp | 1 - indra/newview/llmorphview.cpp | 1 - indra/newview/llpaneleditwearable.cpp | 1 + indra/newview/llpaneleditwearable.h | 4 ++-- indra/newview/lltoolmorph.cpp | 1 - indra/newview/llviewerinventory.cpp | 4 ++-- indra/newview/llviewermenu.cpp | 3 +-- indra/newview/llviewerwindow.cpp | 1 - indra/newview/llvoavatar.cpp | 2 +- indra/newview/llwearable.cpp | 1 - indra/newview/macview_Prefix.h | 1 - 17 files changed, 10 insertions(+), 20 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ddd5d47e78..ea91ee9276 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -164,7 +164,6 @@ set(viewer_SOURCE_FILES llfloaterbuyland.cpp llfloatercamera.cpp llfloatercolorpicker.cpp - llfloatercustomize.cpp llfloaterdaycycle.cpp llfloaterenvsettings.cpp llfloaterevent.cpp @@ -683,7 +682,6 @@ set(viewer_HEADER_FILES llfloaterbuyland.h llfloatercamera.h llfloatercolorpicker.h - llfloatercustomize.h llfloaterdaycycle.h llfloaterenvsettings.h llfloaterevent.h diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 7d84f8d071..0fa77ff7c2 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -45,7 +45,6 @@ #include "llchannelmanager.h" #include "llconsole.h" #include "llfloatercamera.h" -#include "llfloatercustomize.h" #include "llfloaterreg.h" #include "llfloatertools.h" #include "llgroupactions.h" @@ -73,6 +72,7 @@ #include "llviewerdisplay.h" #include "llviewerjoystick.h" #include "llviewermediafocus.h" +#include "llviewermenu.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerstats.h" diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index d9eceec30d..47f290ad3b 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -38,7 +38,6 @@ #include "llagent.h" #include "llanimationstates.h" #include "llfloatercamera.h" -#include "llfloatercustomize.h" #include "llfloaterreg.h" #include "llhudmanager.h" #include "lljoystickbutton.h" @@ -48,6 +47,7 @@ #include "llviewercamera.h" #include "llviewercontrol.h" #include "llviewerjoystick.h" +#include "llviewermenu.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerwindow.h" diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 1deba2e2e6..cc9e68d593 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -39,7 +39,6 @@ #include "llagentwearablesfetch.h" #include "llappearancemgr.h" #include "llcallbacklist.h" -#include "llfloatercustomize.h" #include "llfolderview.h" #include "llgesturemgr.h" #include "llinventorybridge.h" diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 5a4c30a307..8cc4436188 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -38,7 +38,6 @@ #include "llagentwearables.h" #include "llappearancemgr.h" #include "llcommandhandler.h" -#include "llfloatercustomize.h" #include "llgesturemgr.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index f3dfde03c3..577652fc79 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -41,7 +41,6 @@ #include "llagentwearables.h" #include "llappearancemgr.h" #include "llavataractions.h" -#include "llfloatercustomize.h" #include "llfloateropenobject.h" #include "llfloaterreg.h" #include "llfloaterworldmap.h" @@ -66,6 +65,7 @@ #include "llsidetray.h" #include "lltrans.h" #include "llviewerassettype.h" +#include "llviewermenu.h" #include "llviewermessage.h" #include "llviewerobjectlist.h" #include "llviewerwindow.h" diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index c38d45f0f5..f67d91cfa5 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -52,7 +52,6 @@ #include "llappearancemgr.h" #include "llappviewer.h" //#include "llfirstuse.h" -#include "llfloatercustomize.h" #include "llfocusmgr.h" #include "llfolderview.h" #include "llgesturemgr.h" diff --git a/indra/newview/llmorphview.cpp b/indra/newview/llmorphview.cpp index 27a27fb65a..4c28e98e62 100644 --- a/indra/newview/llmorphview.cpp +++ b/indra/newview/llmorphview.cpp @@ -42,7 +42,6 @@ #include "lldrawpoolavatar.h" #include "llface.h" //#include "llfirstuse.h" -#include "llfloatercustomize.h" #include "llfloatertools.h" #include "llresmgr.h" #include "lltoolmgr.h" diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 2ba39fca9c..ecc3512abd 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -217,6 +217,7 @@ LLEditWearableDictionary::Wearables::Wearables() { // note the subpart that is listed first is treated as "default", regardless of what order is in enum. // Please match the order presented in XUI. -Nyx + // this will affect what camera angle is shown when first editing a wearable addEntry(LLWearableType::WT_SHAPE, new WearableEntry(LLWearableType::WT_SHAPE,"edit_shape_title","shape_desc_text",0,0,9, SUBPART_SHAPE_WHOLE, SUBPART_SHAPE_HEAD, SUBPART_SHAPE_EYES, SUBPART_SHAPE_EARS, SUBPART_SHAPE_NOSE, SUBPART_SHAPE_MOUTH, SUBPART_SHAPE_CHIN, SUBPART_SHAPE_TORSO, SUBPART_SHAPE_LEGS )); addEntry(LLWearableType::WT_SKIN, new WearableEntry(LLWearableType::WT_SKIN,"edit_skin_title","skin_desc_text",0,3,4, TEX_HEAD_BODYPAINT, TEX_UPPER_BODYPAINT, TEX_LOWER_BODYPAINT, SUBPART_SKIN_COLOR, SUBPART_SKIN_FACEDETAIL, SUBPART_SKIN_MAKEUP, SUBPART_SKIN_BODYDETAIL)); addEntry(LLWearableType::WT_HAIR, new WearableEntry(LLWearableType::WT_HAIR,"edit_hair_title","hair_desc_text",0,1,4, TEX_HAIR, SUBPART_HAIR_COLOR, SUBPART_HAIR_STYLE, SUBPART_HAIR_EYEBROWS, SUBPART_HAIR_FACIAL)); diff --git a/indra/newview/llpaneleditwearable.h b/indra/newview/llpaneleditwearable.h index 0af3758a4e..0e2aebb619 100644 --- a/indra/newview/llpaneleditwearable.h +++ b/indra/newview/llpaneleditwearable.h @@ -1,6 +1,6 @@ /** - * @file llfloatercustomize.h - * @brief The customize avatar floater, triggered by "Appearance..." + * @file llpaneleditwearable.h + * @brief A LLPanel dedicated to the editing of wearables. * * $LicenseInfo:firstyear=2009&license=viewergpl$ * diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp index 22e15dcff2..c1dc1de5e5 100644 --- a/indra/newview/lltoolmorph.cpp +++ b/indra/newview/lltoolmorph.cpp @@ -49,7 +49,6 @@ #include "lldrawable.h" #include "lldrawpoolavatar.h" #include "llface.h" -#include "llfloatercustomize.h" #include "llmorphview.h" #include "llresmgr.h" #include "llselectmgr.h" diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 4dbede79da..f8b6435614 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -61,7 +61,6 @@ #include "llviewerwindow.h" #include "lltrans.h" #include "llappearancemgr.h" -#include "llfloatercustomize.h" #include "llcommandhandler.h" #include "llviewermessage.h" #include "llsidepanelappearance.h" @@ -881,7 +880,8 @@ void WearOnAvatarCallback::fire(const LLUUID& inv_item) void ModifiedCOFCallback::fire(const LLUUID& inv_item) { LLAppearanceMgr::instance().updateAppearanceFromCOF(); - if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode() ) + // TODO: camera mode may not be changed if a debug setting is tweaked + if( gAgentCamera.cameraCustomizeAvatar() ) { // If we're in appearance editing mode, the current tab may need to be refreshed LLSidepanelAppearance *panel = dynamic_cast(LLSideTray::getInstance()->getPanel("sidepanel_appearance")); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index b1b6db3305..69352e6685 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -54,7 +54,6 @@ #include "llfloaterbuy.h" #include "llfloaterbuycontents.h" #include "llbuycurrencyhtml.h" -#include "llfloatercustomize.h" #include "llfloatergodtools.h" #include "llfloaterinventory.h" #include "llfloaterland.h" @@ -3738,7 +3737,7 @@ void handle_reset_view() { if (gAgentCamera.cameraCustomizeAvatar()) { - // switching to outfit editor should automagically save any currently edited wearable + // switching to outfit selector should automagically save any currently edited wearable LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "my_outfits")); } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index f72f122f8a..8da49b2127 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -106,7 +106,6 @@ #include "llfloaterbuildoptions.h" #include "llfloaterbuyland.h" #include "llfloatercamera.h" -#include "llfloatercustomize.h" #include "llfloaterland.h" #include "llfloaterinspect.h" #include "llfloatermap.h" diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index a1637c4724..c68609d5e9 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -7899,7 +7899,7 @@ BOOL LLVOAvatar::isTextureVisible(LLVOAvatarDefines::ETextureIndex type, U32 ind // baked textures can use TE images directly return ((isTextureDefined(type) || isSelf()) && (getTEImage(type)->getID() != IMG_INVISIBLE - || LLDrawPoolAlpha::sShowDebugAlpha)); + || LLDrawPoolAlpha::sShowDebugAlpha)); } } diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp index 0fcb257e74..9e9b46473e 100644 --- a/indra/newview/llwearable.cpp +++ b/indra/newview/llwearable.cpp @@ -36,7 +36,6 @@ #include "llagentcamera.h" #include "llagentwearables.h" #include "lldictionary.h" -#include "llfloatercustomize.h" #include "lllocaltextureobject.h" #include "llnotificationsutil.h" #include "llviewertexturelist.h" diff --git a/indra/newview/macview_Prefix.h b/indra/newview/macview_Prefix.h index a71362a139..07f8093426 100644 --- a/indra/newview/macview_Prefix.h +++ b/indra/newview/macview_Prefix.h @@ -65,7 +65,6 @@ #include "llfloater.h" #include "llfloaterbuildoptions.h" #include "llfloaterchat.h" -#include "llfloatercustomize.h" #include "llfloatergroups.h" #include "llfloaterworldmap.h" #include "llfloatermute.h" -- cgit v1.3 From d42fd6cf7fa3ac4d7e2ef35cef50eb7740b4ec6d Mon Sep 17 00:00:00 2001 From: Paul Guslisty Date: Fri, 28 May 2010 20:10:10 +0300 Subject: EXT-7472 FIXED Open add to outfit panel by (+) button click on unwearable items and by selecting 'Replace' menu item click body part context menu Main changes: - Added callback for a '(+) button' to the LLCOFCallbacks and bind it with LLPanelOutfitEdit::onAddWearableClicked - Created the callback(LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked) for 'Replace' menu item of context menu Related changes: - Changed LLFilteredWearableListManager so that it can use different functors (subclasses of LLInventoryCollectFunctor) as a criterion for LLInventoryItemsList filtering. Before it used only LLFindNonLinksByMask filter. Moved LLFindNonLinksByMask from to the llfilteredwearablelist.cpp to the llinventoryfunctions.h - Created getter 'LLPanelDummyClothingListItem::getWearableType()' for LLPanelDummyClothingListItem - Made 'add wearables panel' a member of LLPanelOutfitEdit so that not to use findChild each time panel is needed Reviewed by Igor Borovkov at http://jira.secondlife.com/browse/EXT-7472 --HG-- branch : product-engine --- indra/newview/llcofwearables.cpp | 16 +++++++++-- indra/newview/llcofwearables.h | 3 ++ indra/newview/llfilteredwearablelist.cpp | 46 +++++++++--------------------- indra/newview/llfilteredwearablelist.h | 9 +++--- indra/newview/llinventoryfunctions.cpp | 5 ++++ indra/newview/llinventoryfunctions.h | 35 ++++++++++++++++++++++- indra/newview/llpaneloutfitedit.cpp | 49 +++++++++++++++++++++++++++++--- indra/newview/llpaneloutfitedit.h | 9 ++++++ indra/newview/llwearableitemslist.cpp | 5 ++++ indra/newview/llwearableitemslist.h | 1 + 10 files changed, 134 insertions(+), 44 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp index dfc203111a..6e7a7fe937 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -43,6 +43,8 @@ #include "llmenugl.h" #include "llviewermenu.h" #include "llwearableitemslist.h" +#include "llpaneloutfitedit.h" +#include "llsidetray.h" static LLRegisterPanelClassWrapper t_cof_accodion_list_adaptor("accordion_list_adaptor"); @@ -135,8 +137,10 @@ protected: LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; LLUUID selected_id = mUUIDs.back(); - registrar.add("BodyPart.Replace", boost::bind(&LLAppearanceMgr::wearItemOnAvatar, - LLAppearanceMgr::getInstance(), selected_id, true, true)); + // *HACK* need to pass pointer to LLPanelOutfitEdit instead of LLSideTray::getInstance()->getPanel(). + // LLSideTray::getInstance()->getPanel() is rather slow variant + LLPanelOutfitEdit* panel_oe = dynamic_cast(LLSideTray::getInstance()->getPanel("panel_outfit_edit")); + registrar.add("BodyPart.Replace", boost::bind(&LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked, panel_oe, selected_id)); registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id)); enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2)); @@ -416,6 +420,7 @@ void LLCOFWearables::addClothingTypesDummies(const LLAppearanceMgr::wearables_by LLWearableType::EType w_type = static_cast(type); LLPanelInventoryListItemBase* item_panel = LLPanelDummyClothingListItem::create(w_type); if(!item_panel) continue; + item_panel->childSetAction("btn_add", mCOFCallbacks.mAddWearable); mClothing->addItem(item_panel, LLUUID::null, ADD_BOTTOM, false); } } @@ -435,6 +440,13 @@ bool LLCOFWearables::getSelectedUUIDs(uuid_vec_t& selected_ids) return selected_ids.size() != 0; } +LLPanel* LLCOFWearables::getSelectedItem() +{ + if (!mLastSelectedList) return NULL; + + return mLastSelectedList->getSelectedItem(); +} + void LLCOFWearables::clear() { mAttachments->clear(); diff --git a/indra/newview/llcofwearables.h b/indra/newview/llcofwearables.h index 590aa709dd..8f8bda2be8 100644 --- a/indra/newview/llcofwearables.h +++ b/indra/newview/llcofwearables.h @@ -107,6 +107,7 @@ public: typedef boost::function cof_callback_t; + cof_callback_t mAddWearable; cof_callback_t mMoveWearableCloser; cof_callback_t mMoveWearableFurther; cof_callback_t mEditWearable; @@ -123,6 +124,8 @@ public: LLUUID getSelectedUUID(); bool getSelectedUUIDs(uuid_vec_t& selected_ids); + LLPanel* getSelectedItem(); + void refresh(); void clear(); diff --git a/indra/newview/llfilteredwearablelist.cpp b/indra/newview/llfilteredwearablelist.cpp index fd99f673e0..28e159421c 100644 --- a/indra/newview/llfilteredwearablelist.cpp +++ b/indra/newview/llfilteredwearablelist.cpp @@ -37,32 +37,10 @@ #include "llinventoryitemslist.h" #include "llinventorymodel.h" -class LLFindNonLinksByMask : public LLInventoryCollectFunctor -{ -public: - LLFindNonLinksByMask(U64 mask) - : mFilterMask(mask) - {} - - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) - { - if(item && !item->getIsLinkType() && (mFilterMask & (1LL << item->getInventoryType())) ) - { - return true; - } - - return false; - } -private: - U64 mFilterMask; -}; - -////////////////////////////////////////////////////////////////////////// - -LLFilteredWearableListManager::LLFilteredWearableListManager(LLInventoryItemsList* list, U64 filter_mask) +LLFilteredWearableListManager::LLFilteredWearableListManager(LLInventoryItemsList* list, LLInventoryCollectFunctor* collector) : mWearableList(list) -, mFilterMask(filter_mask) +, mCollector(collector) { llassert(mWearableList); gInventory.addObserver(this); @@ -84,9 +62,9 @@ void LLFilteredWearableListManager::changed(U32 mask) populateList(); } -void LLFilteredWearableListManager::setFilterMask(U64 mask) +void LLFilteredWearableListManager::setFilterCollector(LLInventoryCollectFunctor* collector) { - mFilterMask = mask; + mCollector = collector; populateList(); } @@ -94,14 +72,16 @@ void LLFilteredWearableListManager::populateList() { LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; - LLFindNonLinksByMask collector(mFilterMask); - gInventory.collectDescendentsIf( - gInventory.getRootFolderID(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - collector); + if(mCollector) + { + gInventory.collectDescendentsIf( + gInventory.getRootFolderID(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + *mCollector); + } // Probably will also need to get items from Library (waiting for reply in EXT-6724). diff --git a/indra/newview/llfilteredwearablelist.h b/indra/newview/llfilteredwearablelist.h index 0780c02442..b7825c07af 100644 --- a/indra/newview/llfilteredwearablelist.h +++ b/indra/newview/llfilteredwearablelist.h @@ -32,6 +32,7 @@ #ifndef LL_LLFILTEREDWEARABLELIST_H #define LL_LLFILTEREDWEARABLELIST_H +#include "llinventoryfunctions.h" #include "llinventoryobserver.h" class LLInventoryItemsList; @@ -42,7 +43,7 @@ class LLFilteredWearableListManager : public LLInventoryObserver LOG_CLASS(LLFilteredWearableListManager); public: - LLFilteredWearableListManager(LLInventoryItemsList* list, U64 filter_mask); + LLFilteredWearableListManager(LLInventoryItemsList* list, LLInventoryCollectFunctor* collector); ~LLFilteredWearableListManager(); /** LLInventoryObserver implementation @@ -51,9 +52,9 @@ public: /*virtual*/ void changed(U32 mask); /** - * Sets new filter and applies it immediately + * Sets new collector and applies it immediately */ - void setFilterMask(U64 mask); + void setFilterCollector(LLInventoryCollectFunctor* collector); /** * Populates wearable list with filtered data. @@ -62,7 +63,7 @@ public: private: LLInventoryItemsList* mWearableList; - U64 mFilterMask; + LLInventoryCollectFunctor* mCollector; }; #endif //LL_LLFILTEREDWEARABLELIST_H diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index c38d45f0f5..6301ff0d6d 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -372,6 +372,11 @@ bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem return true; } +void LLFindWearablesOfType::setType(LLWearableType::EType type) +{ + mWearableType = type; +} + ///---------------------------------------------------------------------------- /// LLAssetIDMatches ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 8b96ba29d9..bb365573d7 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -251,6 +251,37 @@ public: }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindNonLinksByMask +// +// +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindNonLinksByMask : public LLInventoryCollectFunctor +{ +public: + LLFindNonLinksByMask(U64 mask) + : mFilterMask(mask) + {} + + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if(item && !item->getIsLinkType() && (mFilterMask & (1LL << item->getInventoryType())) ) + { + return true; + } + + return false; + } + + void setFilterMask(U64 mask) + { + mFilterMask = mask; + } + +private: + U64 mFilterMask; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFindWearables // @@ -272,8 +303,10 @@ public: LLFindWearablesOfType(LLWearableType::EType type) : mWearableType(type) {} virtual ~LLFindWearablesOfType() {} virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); + void setType(LLWearableType::EType type); - const LLWearableType::EType mWearableType; +private: + LLWearableType::EType mWearableType; }; /** Inventory Collector Functions diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index ae4b288588..8d7a2b33be 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -67,6 +67,7 @@ #include "llsidepanelappearance.h" #include "lltoggleablemenu.h" #include "llwearablelist.h" +#include "llwearableitemslist.h" static LLRegisterPanelClassWrapper t_outfit_edit("panel_outfit_edit"); @@ -214,7 +215,10 @@ LLPanelOutfitEdit::LLPanelOutfitEdit() mCOFObserver(NULL), mGearMenu(NULL), mCOFDragAndDropObserver(NULL), - mInitialized(false) + mInitialized(false), + mAddWearablesPanel(NULL), + mWearableListMaskCollector(NULL), + mWearableListTypeCollector(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); @@ -236,6 +240,9 @@ LLPanelOutfitEdit::~LLPanelOutfitEdit() delete mCOFObserver; delete mCOFDragAndDropObserver; + + delete mWearableListMaskCollector; + delete mWearableListTypeCollector; } BOOL LLPanelOutfitEdit::postBuild() @@ -261,6 +268,7 @@ BOOL LLPanelOutfitEdit::postBuild() mCOFWearables = getChild("cof_wearables_list"); mCOFWearables->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onOutfitItemSelectionChange, this)); + mCOFWearables->getCOFCallbacks().mAddWearable = boost::bind(&LLPanelOutfitEdit::onAddWearableClicked, this); mCOFWearables->getCOFCallbacks().mEditWearable = boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this); mCOFWearables->getCOFCallbacks().mDeleteWearable = boost::bind(&LLPanelOutfitEdit::onRemoveFromOutfitClicked, this); mCOFWearables->getCOFCallbacks().mMoveWearableCloser = boost::bind(&LLPanelOutfitEdit::moveWearable, this, true); @@ -268,6 +276,7 @@ BOOL LLPanelOutfitEdit::postBuild() mCOFWearables->childSetAction("add_btn", boost::bind(&LLPanelOutfitEdit::toggleAddWearablesPanel, this)); + mAddWearablesPanel = getChild("add_wearables_panel"); mInventoryItemsPanel = getChild("inventory_items"); mInventoryItemsPanel->setFilterTypes(ALL_ITEMS_MASK); @@ -306,9 +315,12 @@ BOOL LLPanelOutfitEdit::postBuild() save_registar.add("Outfit.SaveAsNew.Action", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); mSaveMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_save_outfit.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mWearableListMaskCollector = new LLFindNonLinksByMask(ALL_ITEMS_MASK); + mWearableListTypeCollector = new LLFindWearablesOfType(LLWearableType::WT_NONE); + mWearableItemsPanel = getChild("filtered_wearables_panel"); mWearableItemsList = getChild("filtered_wearables_list"); - mWearableListManager = new LLFilteredWearableListManager(mWearableItemsList, ALL_ITEMS_MASK); + mWearableListManager = new LLFilteredWearableListManager(mWearableItemsList, mWearableListMaskCollector); return TRUE; } @@ -334,7 +346,8 @@ void LLPanelOutfitEdit::moveWearable(bool closer_to_body) void LLPanelOutfitEdit::toggleAddWearablesPanel() { - childSetVisible("add_wearables_panel", !childIsVisible("add_wearables_panel")); + BOOL current_visibility = mAddWearablesPanel->getVisible(); + mAddWearablesPanel->setVisible(!current_visibility); } void LLPanelOutfitEdit::showWearablesFilter() @@ -407,7 +420,9 @@ void LLPanelOutfitEdit::onTypeFilterChanged(LLUICtrl* ctrl) { U32 curr_filter_type = type_filter->getCurrentIndex(); mInventoryItemsPanel->setFilterTypes(mLookItemTypes[curr_filter_type].inventoryMask); - mWearableListManager->setFilterMask(mLookItemTypes[curr_filter_type].inventoryMask); + + mWearableListMaskCollector->setFilterMask(mLookItemTypes[curr_filter_type].inventoryMask); + mWearableListManager->setFilterCollector(mWearableListMaskCollector); } mSavedFolderState->setApply(TRUE); @@ -487,6 +502,25 @@ void LLPanelOutfitEdit::onAddToOutfitClicked(void) LLAppearanceMgr::getInstance()->wearItemOnAvatar(selected_id); } +void LLPanelOutfitEdit::onAddWearableClicked(void) +{ + LLPanelDummyClothingListItem* item = dynamic_cast(mCOFWearables->getSelectedItem()); + + if(item) + { + showFilteredWearableItemsList(item->getWearableType()); + } +} + +void LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked(LLUUID selected_item_id) +{ + LLViewerInventoryItem* item = gInventory.getLinkedItem(selected_item_id); + + if (item && item->getType() == LLAssetType::AT_BODYPART) + { + showFilteredWearableItemsList(item->getWearableType()); + } +} void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void) { @@ -722,4 +756,11 @@ void LLPanelOutfitEdit::onGearMenuItemClick(const LLSD& data) } } +void LLPanelOutfitEdit::showFilteredWearableItemsList(LLWearableType::EType type) +{ + mWearableListTypeCollector->setType(type); + mWearableListManager->setFilterCollector(mWearableListTypeCollector); + mAddWearablesPanel->setVisible(TRUE); +} + // EOF diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 1bf69c5606..dbfeb2c375 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -59,6 +59,8 @@ class LLToggleableMenu; class LLFilterEditor; class LLFilteredWearableListManager; class LLMenuGL; +class LLFindNonLinksByMask; +class LLFindWearablesOfType; class LLPanelOutfitEdit : public LLPanel { @@ -103,6 +105,8 @@ public: void onOutfitItemSelectionChange(void); void onRemoveFromOutfitClicked(void); void onEditWearableClicked(void); + void onAddWearableClicked(void); + void onReplaceBodyPartMenuItemClicked(LLUUID selected_item_id); void displayCurrentOutfit(); void updateCurrentOutfitName(); @@ -129,6 +133,7 @@ private: void onGearButtonClick(LLUICtrl* clicked_button); void onGearMenuItemClick(const LLSD& data); + void showFilteredWearableItemsList(LLWearableType::EType type); LLTextBox* mCurrentOutfitName; @@ -141,6 +146,10 @@ private: LLButton* mFolderViewBtn; LLButton* mListViewBtn; LLToggleableMenu* mSaveMenu; + LLPanel* mAddWearablesPanel; + + LLFindNonLinksByMask* mWearableListMaskCollector; + LLFindWearablesOfType* mWearableListTypeCollector; LLFilteredWearableListManager* mWearableListManager; LLInventoryItemsList* mWearableItemsList; diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index fb7577c008..161cd40cfc 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -251,6 +251,11 @@ BOOL LLPanelDummyClothingListItem::postBuild() return TRUE; } +LLWearableType::EType LLPanelDummyClothingListItem::getWearableType() const +{ + return mWearableType; +} + LLPanelDummyClothingListItem::LLPanelDummyClothingListItem(LLWearableType::EType w_type) : LLPanelWearableListItem(NULL) , mWearableType(w_type) diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index 7ad1b5a3ad..de024ed220 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -162,6 +162,7 @@ public: /*virtual*/ void updateItem(); /*virtual*/ BOOL postBuild(); + LLWearableType::EType getWearableType() const; protected: LLPanelDummyClothingListItem(LLWearableType::EType w_type); -- cgit v1.3 From 671625695fc44adc430a7ddf3be158ce26a61cc6 Mon Sep 17 00:00:00 2001 From: Vadim Savchuk Date: Wed, 2 Jun 2010 20:46:16 +0300 Subject: EXT-6726 WIP Added more menus to the Appearance SP. Done: - Implemented creating new wearables via My Outfits gear menu. - Implemented renaming/removing/editing an outfit via My Outfits context menu. - Implemented "Attach to..." / "Attach to HUD..." context submenus. - Now disabling (instead of hiding) irrelevant wearable context menu items. - Added "Take Off / Detach" context menu item that's shown for clothes and attachments. Useful if you selected a bunch of items and want to take them all off. - Fixed taking off an outfit (not all wearables were taken off because of a wrong inventory collector). - Fixed crash when editing a skirt (reference to a missing string). - In LLWearableItemsList::ContextMenu::updateItemsVisibility renamed variables and introduced MASK_UNKNOWN per Nyx's request. Known issues: - "Attach to..." context menus may be displayed partially off-screen (there is the same bug in the inventory panel). - The way we invoke wearable editing panel after the wearable gets created is currently a hack. TODO: - Wear / take off / rename / delete an outfit via My Outfits gear menu (currently not implemented because of missing selection support in My Outfits). - Add "Create new..." to body part / clothing context menu in Edit Outfit. - Add "Create new..." submenus to the Edit Outfit gear menu. Reviewed by Nyx at https://codereview.productengine.com/secondlife/r/466/ --HG-- branch : product-engine --- indra/newview/CMakeLists.txt | 2 + indra/newview/llagentwearables.cpp | 26 ++++ indra/newview/llagentwearables.h | 1 + indra/newview/llappearancemgr.cpp | 2 +- indra/newview/llinventorybridge.cpp | 161 +------------------- indra/newview/llinventorybridge.h | 1 - indra/newview/llinventoryfunctions.cpp | 166 +++++++++++++++++++++ indra/newview/llinventoryfunctions.h | 25 ++++ indra/newview/llinventorypanel.cpp | 44 +----- indra/newview/lloutfitslist.cpp | 142 +++++++++++++++++- indra/newview/llpaneloutfitsinventory.cpp | 89 ++++++++++- indra/newview/llpaneloutfitsinventory.h | 2 +- indra/newview/llviewerattachmenu.cpp | 139 +++++++++++++++++ indra/newview/llviewerattachmenu.h | 43 ++++++ indra/newview/llviewerinventory.cpp | 15 +- indra/newview/llwearableitemslist.cpp | 101 ++++++++++--- indra/newview/llwearableitemslist.h | 8 +- .../skins/default/xui/en/menu_cof_attachment.xml | 8 - .../skins/default/xui/en/menu_outfit_gear.xml | 160 ++++++++++++++++++-- .../skins/default/xui/en/menu_outfit_tab.xml | 27 +++- .../default/xui/en/menu_wearable_list_item.xml | 34 +++-- .../newview/skins/default/xui/en/notifications.xml | 22 +++ indra/newview/skins/default/xui/en/strings.xml | 1 + 23 files changed, 965 insertions(+), 254 deletions(-) create mode 100644 indra/newview/llviewerattachmenu.cpp create mode 100644 indra/newview/llviewerattachmenu.h (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9ba44e787b..b00104c427 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -466,6 +466,7 @@ set(viewer_SOURCE_FILES llviewchildren.cpp llviewerassetstorage.cpp llviewerassettype.cpp + llviewerattachmenu.cpp llvieweraudio.cpp llviewercamera.cpp llviewerchat.cpp @@ -984,6 +985,7 @@ set(viewer_HEADER_FILES llviewchildren.h llviewerassetstorage.h llviewerassettype.h + llviewerattachmenu.h llvieweraudio.h llviewercamera.h llviewerchat.h diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index e5796f8e63..d2a01aba16 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -1973,6 +1973,32 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos return false; } +// static +void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id) +{ + LLWearable* wearable = LLWearableList::instance().createNewWearable(type); + LLAssetType::EType asset_type = wearable->getAssetType(); + LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; + LLPointer cb = wear ? new WearOnAvatarCallback : NULL; + LLUUID folder_id; + + if (parent_id.notNull()) + { + folder_id = parent_id; + } + else + { + LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(asset_type); + folder_id = gInventory.findCategoryUUIDForType(folder_type); + } + + create_inventory_item(gAgent.getID(), gAgent.getSessionID(), + folder_id, wearable->getTransactionID(), wearable->getName(), + wearable->getDescription(), asset_type, inv_type, wearable->getType(), + wearable->getPermissions().getMaskNextOwner(), + cb); +} + // static void LLAgentWearables::editWearable(const LLUUID& item_id) { diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 5d5c5ae371..6d379746ba 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -144,6 +144,7 @@ protected: //-------------------------------------------------------------------- public: + static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null); static void editWearable(const LLUUID& item_id); bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index f27e632180..cf45743a48 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -714,7 +714,7 @@ void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) { LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLFindWearables collector; + LLFindWorn collector; gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 1f87b14ddd..b7495f7dbe 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -164,38 +164,7 @@ time_t LLInvFVBridge::getCreationDate() const // Can be destroyed (or moved to trash) BOOL LLInvFVBridge::isItemRemovable() const { - const LLInventoryModel* model = getInventoryModel(); - if(!model) - { - return FALSE; - } - - // Can't delete an item that's in the library. - if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID())) - { - return FALSE; - } - - // Disable delete from COF folder; have users explicitly choose "detach/take off", - // unless the item is not worn but in the COF (i.e. is bugged). - if (LLAppearanceMgr::instance().getIsProtectedCOFItem(mUUID)) - { - if (get_is_item_worn(mUUID)) - { - return FALSE; - } - } - - const LLInventoryObject *obj = model->getItem(mUUID); - if (obj && obj->getIsLinkType()) - { - return TRUE; - } - if (get_is_item_worn(mUUID)) - { - return FALSE; - } - return TRUE; + return get_is_item_removable(getInventoryModel(), mUUID); } // Can be moved to another folder @@ -833,24 +802,7 @@ void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model, const LLUUID& new_parent_id, BOOL restamp) { - // Can't move a folder into a child of itself. - if (model->isObjectDescendentOf(new_parent_id, cat->getUUID())) - { - return; - } - - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); - update.push_back(new_folder); - model->accountForUpdate(update); - - LLPointer new_cat = new LLViewerInventoryCategory(cat); - new_cat->setParent(new_parent_id); - new_cat->updateParentOnServer(restamp); - model->updateCategory(new_cat); - model->notifyObservers(); + change_category_parent(model, cat, new_parent_id, restamp); } LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, @@ -1538,26 +1490,7 @@ public: // Can be destroyed (or moved to trash) BOOL LLFolderBridge::isItemRemovable() const { - LLInventoryModel* model = getInventoryModel(); - if(!model) - { - return FALSE; - } - - if(!model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID())) - { - return FALSE; - } - - if (!isAgentAvatarValid()) return FALSE; - - LLInventoryCategory* category = model->getCategory(mUUID); - if(!category) - { - return FALSE; - } - - if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) + if (!get_is_category_removable(getInventoryModel(), mUUID)) { return FALSE; } @@ -1573,6 +1506,7 @@ BOOL LLFolderBridge::isItemRemovable() const return FALSE; } } + return TRUE; } @@ -2379,21 +2313,8 @@ LLUIImagePtr LLFolderBridge::getOpenIcon() const BOOL LLFolderBridge::renameItem(const std::string& new_name) { - if(!isItemRenameable()) - return FALSE; - LLInventoryModel* model = getInventoryModel(); - if(!model) - return FALSE; - LLViewerInventoryCategory* cat = getCategory(); - if(cat && (cat->getName() != new_name)) - { - LLPointer new_cat = new LLViewerInventoryCategory(cat); - new_cat->rename(new_name); - new_cat->updateServer(FALSE); - model->updateCategory(new_cat); + rename_category(getInventoryModel(), mUUID, new_name); - model->notifyObservers(); - } // return FALSE because we either notified observers (& therefore // rebuilt) or we didn't update. return FALSE; @@ -2447,36 +2368,7 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re { // move it to the trash LLPreview::hide(mUUID); - LLInventoryModel* model = getInventoryModel(); - if(!model) return FALSE; - - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - - // Look for any gestures and deactivate them - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE ); - - for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin(); - iter != descendent_items.end(); - ++iter) - { - const LLViewerInventoryItem* item = (*iter); - const LLUUID& item_id = item->getUUID(); - if (item->getType() == LLAssetType::AT_GESTURE - && LLGestureMgr::instance().isGestureActive(item_id)) - { - LLGestureMgr::instance().deactivateGesture(item_id); - } - } - - // go ahead and do the normal remove if no 'last calling - // cards' are being removed. - LLViewerInventoryCategory* cat = getCategory(); - if(cat) - { - LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE); - } + remove_category(getInventoryModel(), mUUID); return TRUE; } return FALSE; @@ -2672,22 +2564,6 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv return ((item_array.count() > 0) ? TRUE : FALSE ); } -class LLFindWorn : public LLInventoryCollectFunctor -{ -public: - LLFindWorn() {} - virtual ~LLFindWorn() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item) - { - if (item && get_is_item_worn(item->getUUID())) - { - return TRUE; - } - return FALSE; - } -}; - BOOL LLFolderBridge::areAnyContentsWorn(LLInventoryModel* model) const { LLInventoryModel::cat_array_t cat_array; @@ -3006,22 +2882,7 @@ void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::ETyp { if(!bridge) return; LLUUID parent_id = bridge->getUUID(); - createWearable(parent_id, type); -} - -// Separate function so can be called by global menu as well as right-click -// menu. -// static -void LLFolderBridge::createWearable(const LLUUID &parent_id, LLWearableType::EType type) -{ - LLWearable* wearable = LLWearableList::instance().createNewWearable(type); - LLAssetType::EType asset_type = wearable->getAssetType(); - LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE; - create_inventory_item(gAgent.getID(), gAgent.getSessionID(), - parent_id, wearable->getTransactionID(), wearable->getName(), - wearable->getDescription(), asset_type, inv_type, wearable->getType(), - wearable->getPermissions().getMaskNextOwner(), - LLPointer(NULL)); + LLAgentWearables::createWearable(type, false, parent_id); } void LLFolderBridge::modifyOutfit(BOOL append) @@ -4898,13 +4759,7 @@ void LLWearableBridge::onEditOnAvatar(void* user_data) void LLWearableBridge::editOnAvatar() { - LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID); - if( wearable ) - { - LLPanel * panel = LLSideTray::getInstance()->getPanel("sidepanel_appearance"); - - LLSidepanelAppearance::editWearable(wearable, panel); - } + LLAgentWearables::editWearable(mUUID); } // static diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index c5efefac7e..59c1f3d6fb 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -270,7 +270,6 @@ public: virtual BOOL copyToClipboard() const; static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type); - static void createWearable(const LLUUID &parent_folder_id, LLWearableType::EType type); LLViewerInventoryCategory* getCategory() const; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 0cc4b0e389..9fe9d2de8e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -130,6 +130,90 @@ void change_item_parent(LLInventoryModel* model, } } +void change_category_parent(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& new_parent_id, + BOOL restamp) +{ + if (!model || !cat) + { + return; + } + + // Can't move a folder into a child of itself. + if (model->isObjectDescendentOf(new_parent_id, cat->getUUID())) + { + return; + } + + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); + update.push_back(new_folder); + model->accountForUpdate(update); + + LLPointer new_cat = new LLViewerInventoryCategory(cat); + new_cat->setParent(new_parent_id); + new_cat->updateParentOnServer(restamp); + model->updateCategory(new_cat); + model->notifyObservers(); +} + +void remove_category(LLInventoryModel* model, const LLUUID& cat_id) +{ + if (!model || !get_is_category_removable(model, cat_id)) + { + return; + } + + // Look for any gestures and deactivate them + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + gInventory.collectDescendents(cat_id, descendent_categories, descendent_items, FALSE); + + for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin(); + iter != descendent_items.end(); + ++iter) + { + const LLViewerInventoryItem* item = (*iter); + const LLUUID& item_id = item->getUUID(); + if (item->getType() == LLAssetType::AT_GESTURE + && LLGestureMgr::instance().isGestureActive(item_id)) + { + LLGestureMgr::instance().deactivateGesture(item_id); + } + } + + // go ahead and do the normal remove if no 'last calling + // cards' are being removed. + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (cat) + { + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + change_category_parent(model, cat, trash_id, TRUE); + } +} + +void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) +{ + LLViewerInventoryCategory* cat; + + if (!model || + !get_is_category_renameable(model, cat_id) || + (cat = model->getCategory(cat_id)) == NULL || + cat->getName() == new_name) + { + return; + } + + LLPointer new_cat = new LLViewerInventoryCategory(cat); + new_cat->rename(new_name); + new_cat->updateServer(FALSE); + model->updateCategory(new_cat); + + model->notifyObservers(); +} BOOL get_is_item_worn(const LLUUID& id) { @@ -160,6 +244,83 @@ BOOL get_is_item_worn(const LLUUID& id) return FALSE; } +BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) +{ + if (!model) + { + return FALSE; + } + + // Can't delete an item that's in the library. + if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) + { + return FALSE; + } + + // Disable delete from COF folder; have users explicitly choose "detach/take off", + // unless the item is not worn but in the COF (i.e. is bugged). + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(id)) + { + if (get_is_item_worn(id)) + { + return FALSE; + } + } + + const LLInventoryObject *obj = model->getItem(id); + if (obj && obj->getIsLinkType()) + { + return TRUE; + } + if (get_is_item_worn(id)) + { + return FALSE; + } + return TRUE; +} + +BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) +{ + // This function doesn't check the folder's children. + + if (!model) + { + return FALSE; + } + + if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) + { + return FALSE; + } + + if (!isAgentAvatarValid()) return FALSE; + + LLInventoryCategory* category = model->getCategory(id); + if (!category) + { + return FALSE; + } + + if (LLFolderType::lookupIsProtectedType(category->getPreferredType())) + { + return FALSE; + } + + return TRUE; +} + +BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) +{ + LLViewerInventoryCategory* cat = model->getCategory(id); + + if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) && + cat->getOwnerID() == gAgent.getID()) + { + return TRUE; + } + return FALSE; +} + void show_item_profile(const LLUUID& item_uuid) { LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); @@ -376,6 +537,11 @@ void LLFindWearablesOfType::setType(LLWearableType::EType type) mWearableType = type; } +bool LLFindWorn::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return item && get_is_item_worn(item->getUUID()); +} + ///---------------------------------------------------------------------------- /// LLAssetIDMatches ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index bb365573d7..5c07a3190f 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -46,6 +46,12 @@ // Is this item or its baseitem is worn, attached, etc... BOOL get_is_item_worn(const LLUUID& id); +BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id); + +BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id); + +BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id); + void show_item_profile(const LLUUID& item_uuid); void show_item_original(const LLUUID& item_uuid); @@ -55,6 +61,15 @@ void change_item_parent(LLInventoryModel* model, const LLUUID& new_parent_id, BOOL restamp); +void change_category_parent(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& new_parent_id, + BOOL restamp); + +void remove_category(LLInventoryModel* model, const LLUUID& cat_id); + +void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); + // Generates a string containing the path to the item specified by item_id. void append_path(const LLUUID& id, std::string& path); @@ -309,6 +324,16 @@ private: LLWearableType::EType mWearableType; }; +// Find worn items. +class LLFindWorn : public LLInventoryCollectFunctor +{ +public: + LLFindWorn() {} + virtual ~LLFindWorn() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); +}; + + /** Inventory Collector Functions ** ** *******************************************************************************/ diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 4766c1c227..bb3f34dde2 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -49,6 +49,7 @@ #include "llsidepanelinventory.h" #include "llsidetray.h" #include "llscrollcontainer.h" +#include "llviewerattachmenu.h" #include "llviewerfoldertype.h" #include "llvoavatarself.h" @@ -877,48 +878,19 @@ bool LLInventoryPanel::beginIMSession() bool LLInventoryPanel::attachObject(const LLSD& userdata) { + // Copy selected item UUIDs to a vector. std::set selected_items = mFolderRoot->getSelectionList(); - - std::string joint_name = userdata.asString(); - LLViewerJointAttachment* attachmentp = NULL; - for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); - iter != gAgentAvatarp->mAttachmentPoints.end(); ) - { - LLVOAvatar::attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getName() == joint_name) - { - attachmentp = attachment; - break; - } - } - if (attachmentp == NULL) - { - return true; - } - + uuid_vec_t items; for (std::set::const_iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - const LLUUID &id = *set_iter; - LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(id); - if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())) - { - rez_attachment(item, attachmentp); - } - else if(item && item->isFinished()) - { - // must be in library. copy it to our inventory and put it on. - LLPointer cb = new RezAttachmentCallback(attachmentp); - copy_inventory_item(gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - LLUUID::null, - std::string(), - cb); - } + items.push_back(*set_iter); } + + // Attach selected items. + LLViewerAttachMenu::attachObjects(items, userdata.asString()); + gFocusMgr.setKeyboardFocus(NULL); return true; diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 98ec272e96..5c8b3d1894 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -43,6 +43,8 @@ #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "lllistcontextmenu.h" +#include "llnotificationsutil.h" +#include "llsidetray.h" #include "lltransutil.h" #include "llviewermenu.h" #include "llvoavatar.h" @@ -53,6 +55,28 @@ static bool is_tab_header_clicked(LLAccordionCtrlTab* tab, S32 y); ////////////////////////////////////////////////////////////////////////// +// Collect non-removable folders and items. +class LLFindNonRemovableObjects : public LLInventoryCollectFunctor +{ +public: + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if (item) + { + return !get_is_item_removable(&gInventory, item->getUUID()); + } + if (cat) + { + return !get_is_category_removable(&gInventory, cat->getUUID()); + } + + llwarns << "Not a category and not an item?" << llendl; + return false; + } +}; + +////////////////////////////////////////////////////////////////////////// + class OutfitContextMenu : public LLListContextMenu { protected: @@ -68,12 +92,124 @@ protected: boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id)); registrar.add("Outfit.TakeOff", boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); - // *TODO: implement this - // registrar.add("Outfit.Rename", boost::bind()); - // registrar.add("Outfit.Delete", boost::bind()); + registrar.add("Outfit.Edit", boost::bind(editOutfit)); + registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); + registrar.add("Outfit.Delete", boost::bind(deleteOutfit, selected_id)); + + enable_registrar.add("Outfit.OnEnable", boost::bind(&OutfitContextMenu::onEnable, this, _2)); return createFromFile("menu_outfit_tab.xml"); } + + bool onEnable(const LLSD& data) + { + std::string param = data.asString(); + LLUUID outfit_cat_id = mUUIDs.back(); + bool is_worn = LLAppearanceMgr::instance().getBaseOutfitUUID() == outfit_cat_id; + + if ("wear_replace" == param) + { + return !is_worn; + } + else if ("wear_add" == param) + { + return !is_worn; + } + else if ("take_off" == param) + { + return is_worn; + } + else if ("edit" == param) + { + return is_worn; + } + else if ("rename" == param) + { + return get_is_category_renameable(&gInventory, outfit_cat_id); + } + else if ("delete" == param) + { + return canDeleteOutfit(outfit_cat_id); + } + + return true; + } + + static void editOutfit() + { + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_outfit")); + } + + static void renameOutfit(const LLUUID& outfit_cat_id) + { + LLViewerInventoryCategory* outfit_cat = gInventory.getCategory(outfit_cat_id); + llassert(outfit_cat); + if (!outfit_cat) return; + + LLSD args; + args["NAME"] = outfit_cat->getName(); + + LLSD payload; + payload["cat_id"] = outfit_cat_id; + + LLNotificationsUtil::add("RenameOutfit", args, payload, boost::bind(onRename, _1, _2)); + } + + // User typed new outfit name. + static void onRename(const LLSD& notification, const LLSD& response) + { + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + std::string outfit_name = response["new_name"].asString(); + LLStringUtil::trim(outfit_name); + if (!outfit_name.empty()) + { + LLUUID cat_id = notification["payload"]["cat_id"].asUUID(); + rename_category(&gInventory, cat_id, outfit_name); + } + } + + static bool canDeleteOutfit(const LLUUID& outfit_cat_id) + { + // Disallow removing the base outfit. + if (outfit_cat_id == LLAppearanceMgr::instance().getBaseOutfitUUID()) + { + return false; + } + + // Check if the outfit folder itself is removable. + if (!get_is_category_removable(&gInventory, outfit_cat_id)) + { + return false; + } + + // Check if the folder contains worn items. + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWorn filter_worn; + gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_worn); + if (!items.empty()) + { + return false; + } + + // Check for the folder's non-removable descendants. + LLFindNonRemovableObjects filter_non_removable; + LLInventoryModel::item_array_t::const_iterator it; + gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_non_removable); + if (!cats.empty() || !items.empty()) + { + return false; + } + + return true; + } + + static void deleteOutfit(const LLUUID& outfit_cat_id) + { + remove_category(&gInventory, outfit_cat_id); + } }; ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 21f69d3470..6fb016cdfd 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -42,6 +42,7 @@ #include "llfloaterworldmap.h" #include "llfloaterinventory.h" #include "llfoldervieweventlistener.h" +#include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" #include "llinventorypanel.h" @@ -70,6 +71,89 @@ static const std::string COF_TAB_NAME = "cof_tab"; static LLRegisterPanelClassWrapper t_inventory("panel_outfits_inventory"); +// Context-dependent menu actions are not implemented +// because accordions don't properly support selection yet. +class LLOutfitListGearMenu +{ +public: + static LLMenuGL* createMenu() + { + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Gear.Wear", boost::bind(onWear)); + registrar.add("Gear.TakeOff", boost::bind(onTakeOff)); + registrar.add("Gear.Rename", boost::bind(onRename)); + registrar.add("Gear.Delete", boost::bind(onDelete)); + registrar.add("Gear.Create", boost::bind(onCreate, _2)); + + enable_registrar.add("Gear.OnEnable", boost::bind(onEnable, _2)); + + return LLUICtrlFactory::getInstance()->createFromFile( + "menu_outfit_gear.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + } + +private: + static void onWear() + { + // *TODO: not implemented + } + + static void onTakeOff() + { + // *TODO: not implemented + } + + static void onRename() + { + // *TODO: not implemented + } + + static void onDelete() + { + // *TODO: not implemented + } + + static void onCreate(const LLSD& data) + { + LLWearableType::EType type = LLWearableType::typeNameToType(data.asString()); + if (type == LLWearableType::WT_NONE) + { + llwarns << "Invalid wearable type" << llendl; + return; + } + + LLAgentWearables::createWearable(type, true); + } + + static bool onEnable(const LLSD& data) + { + std::string param = data.asString(); + + if ("wear" == param) + { + // *TODO: not implemented + return false; + } + else if ("take_off" == param) + { + // *TODO: not implemented + return false; + } + else if ("rename" == param) + { + // *TODO: not implemented + return false; + } + else if ("delete" == param) + { + // *TODO: not implemented + return false; + } + + return true; + } +}; LLPanelOutfitsInventory::LLPanelOutfitsInventory() : mMyOutfitsPanel(NULL), @@ -385,8 +469,7 @@ void LLPanelOutfitsInventory::initListCommandsHandlers() , _7 // EAcceptance* accept )); - mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile("menu_outfit_gear.xml", - gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mGearMenu = LLOutfitListGearMenu::createMenu(); } void LLPanelOutfitsInventory::updateListCommands() @@ -403,7 +486,7 @@ void LLPanelOutfitsInventory::updateListCommands() void LLPanelOutfitsInventory::onGearButtonClick() { - showActionMenu(mMenuGearDefault,"options_gear_btn"); + showActionMenu(mGearMenu, "options_gear_btn"); } void LLPanelOutfitsInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name) diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index 7bdd37c16c..8e76688de5 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -129,7 +129,7 @@ protected: void onWearablesLoaded(); private: LLPanel* mListCommands; - LLMenuGL* mMenuGearDefault; + LLMenuGL* mGearMenu; LLMenuGL* mMenuAdd; // List Commands // //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llviewerattachmenu.cpp b/indra/newview/llviewerattachmenu.cpp new file mode 100644 index 0000000000..f7f5ec72fd --- /dev/null +++ b/indra/newview/llviewerattachmenu.cpp @@ -0,0 +1,139 @@ +/** + * @file llviewerattachmenu.cpp + * @brief "Attach to" / "Attach to HUD" submenus. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerattachmenu.h" + +// project includes +#include "llagent.h" +#include "llinventorybridge.h" // for rez_attachment() +#include "llinventorymodel.h" +#include "llviewerinventory.h" +#include "llviewermenu.h" // for gMenuHolder +#include "llvoavatarself.h" + +// linden libraries +#include "llmenugl.h" +#include "lltrans.h" + +// static +void LLViewerAttachMenu::populateMenus(const std::string& attach_to_menu_name, const std::string& attach_to_hud_menu_name) +{ + // *TODO: share this code with other similar menus + // (inventory panel context menu, in-world object menu). + + if (attach_to_menu_name.empty() || attach_to_hud_menu_name.empty() || !isAgentAvatarValid()) return; + + LLContextMenu* attach_menu = gMenuHolder->getChild(attach_to_menu_name); + LLContextMenu* attach_hud_menu = gMenuHolder->getChild(attach_to_hud_menu_name); + + if (!attach_menu || attach_menu->getChildCount() != 0 || + !attach_hud_menu || attach_hud_menu->getChildCount() != 0) + { + return; + } + + // Populate "Attach to..." / "Attach to HUD..." submenus. + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ) + { + LLVOAvatar::attachment_map_t::iterator curiter = iter++; + LLViewerJointAttachment* attachment = curiter->second; + LLMenuItemCallGL::Params p; + std::string submenu_name = attachment->getName(); + std::string translated_submenu_name; + + if (LLTrans::findString(translated_submenu_name, submenu_name)) + { + p.name = (" ") + translated_submenu_name + " "; + } + else + { + p.name = submenu_name; + } + + LLSD cbparams; + cbparams["index"] = curiter->first; + cbparams["label"] = attachment->getName(); + p.on_click.function_name = "Object.Attach"; + p.on_click.parameter = LLSD(attachment->getName()); + p.on_enable.function_name = "Attachment.Label"; + p.on_enable.parameter = cbparams; + + LLMenuItemCallGL* item = LLUICtrlFactory::create(p); + LLView* parent_menu = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu; + parent_menu->addChild(item); + } +} + +// static +void LLViewerAttachMenu::attachObjects(const uuid_vec_t& items, const std::string& joint_name) +{ + LLViewerJointAttachment* attachmentp = NULL; + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ) + { + LLVOAvatar::attachment_map_t::iterator curiter = iter++; + LLViewerJointAttachment* attachment = curiter->second; + if (attachment->getName() == joint_name) + { + attachmentp = attachment; + break; + } + } + if (attachmentp == NULL) + { + return; + } + + for (uuid_vec_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + const LLUUID &id = *it; + LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getLinkedItem(id); + if(item && gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())) + { + rez_attachment(item, attachmentp); + } + else if(item && item->isFinished()) + { + // must be in library. copy it to our inventory and put it on. + LLPointer cb = new RezAttachmentCallback(attachmentp); + copy_inventory_item(gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + LLUUID::null, + std::string(), + cb); + } + } +} diff --git a/indra/newview/llviewerattachmenu.h b/indra/newview/llviewerattachmenu.h new file mode 100644 index 0000000000..d1db9914f3 --- /dev/null +++ b/indra/newview/llviewerattachmenu.h @@ -0,0 +1,43 @@ +/** + * @file llviewerattachmenu.h + * @brief "Attach to" / "Attach to HUD" submenus. + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLVIEWERATTACHMENU_H +#define LL_LLVIEWERATTACHMENU_H + +class LLViewerAttachMenu +{ +public: + static void populateMenus(const std::string& attach_to_menu_name, const std::string& attach_to_hud_menu_name); + static void attachObjects(const uuid_vec_t& items, const std::string& joint_name); +}; + +#endif // LL_LLVIEWERATTACHMENU_H diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 77b0d9f8d3..a788d3c457 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -39,6 +39,7 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llagentwearables.h" #include "llviewerfoldertype.h" #include "llfolderview.h" #include "llviewercontrol.h" @@ -880,6 +881,14 @@ void WearOnAvatarCallback::fire(const LLUUID& inv_item) void ModifiedCOFCallback::fire(const LLUUID& inv_item) { LLAppearanceMgr::instance().updateAppearanceFromCOF(); + + if (LLSideTray::getInstance()->isPanelActive("sidepanel_appearance")) + { + // *HACK: Edit the wearable that has just been worn + // only if the Appearance SP is currently opened. + LLAgentWearables::editWearable(inv_item); + } + // TODO: camera mode may not be changed if a debug setting is tweaked if( gAgentCamera.cameraCustomizeAvatar() ) { @@ -1240,10 +1249,8 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons LLWearableType::EType wearable_type = LLWearableType::typeNameToType(type_name); if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT) { - LLAssetType::EType asset_type = LLWearableType::getAssetType(wearable_type); - LLFolderType::EType folder_type = LLFolderType::assetTypeToFolderType(asset_type); - const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(folder_type); - LLFolderBridge::createWearable(parent_id, wearable_type); + const LLUUID parent_id = bridge ? bridge->getUUID() : LLUUID::null; + LLAgentWearables::createWearable(wearable_type, false, parent_id); } else { diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index cfb48a22bb..78c784e4df 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -41,6 +41,7 @@ #include "llinventorymodel.h" #include "llmenugl.h" // for LLContextMenu #include "lltransutil.h" +#include "llviewerattachmenu.h" class LLFindOutfitItems : public LLInventoryCollectFunctor { @@ -511,7 +512,9 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() // Register handlers common for all wearable types. registrar.add("Wearable.Wear", boost::bind(handleMultiple, wear, ids)); registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids)); + registrar.add("Wearable.CreateNew", boost::bind(createNewWearable, selected_id)); registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id)); + registrar.add("Wearable.TakeOffDetach", boost::bind(handleMultiple, take_off, ids)); // Register handlers for clothing. registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, ids)); @@ -521,12 +524,16 @@ LLContextMenu* LLWearableItemsList::ContextMenu::createMenu() // Register handlers for attachments. registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, ids)); registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id)); + registrar.add("Object.Attach", boost::bind(LLViewerAttachMenu::attachObjects, ids, _2)); // Create the menu. LLContextMenu* menu = createFromFile("menu_wearable_list_item.xml"); // Determine which items should be visible/enabled. updateItemsVisibility(menu); + + // Update labels for the items requiring that. + updateItemsLabels(menu); return menu; } @@ -540,10 +547,10 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu const uuid_vec_t& ids = mUUIDs; // selected items IDs U32 mask = 0; // mask of selected items' types - U32 nitems = ids.size(); // number of selected items - U32 nworn = 0; // number of worn items among the selected ones - U32 nwornlinks = 0; // number of worn links among the selected items - U32 neditable = 0; // number of editable items among the selected ones + U32 n_items = ids.size(); // number of selected items + U32 n_worn = 0; // number of worn items among the selected ones + U32 n_links = 0; // number of links among the selected items + U32 n_editable = 0; // number of editable items among the selected ones for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) { @@ -565,38 +572,82 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu if (is_worn) { - ++nworn; - - if (is_link) - { - ++nwornlinks; - } + ++n_worn; } if (is_editable) { - ++neditable; + ++n_editable; + } + if (is_link) + { + ++n_links; } } // for // *TODO: eliminate multiple traversals over the menu items - // *TODO: try disabling items rather than hiding them - // *FIX: we may hide *all* items and thus get an ugly empty menu - setMenuItemVisible(menu, "wear", nworn == 0); - setMenuItemVisible(menu, "edit", mask & (MASK_CLOTHING|MASK_BODYPART) && nitems == 1 && neditable == 1); - setMenuItemVisible(menu, "show_original", nitems == 1 && nwornlinks == nitems); - setMenuItemVisible(menu, "take_off", mask == MASK_CLOTHING && nworn == nitems); // selected only worn clothes - setMenuItemVisible(menu, "detach", mask == MASK_ATTACHMENT && nworn == nitems); - setMenuItemVisible(menu, "object_profile", mask == MASK_ATTACHMENT && nitems == 1); + setMenuItemVisible(menu, "wear", n_worn == 0); + setMenuItemVisible(menu, "edit", mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1); + setMenuItemEnabled(menu, "edit", n_editable == 1 && n_worn == 1); + setMenuItemVisible(menu, "create_new", mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1); + setMenuItemEnabled(menu, "show_original", n_items == 1 && n_links == n_items); + setMenuItemVisible(menu, "take_off", mask == MASK_CLOTHING && n_worn == n_items); + setMenuItemVisible(menu, "detach", mask == MASK_ATTACHMENT && n_worn == n_items); + setMenuItemVisible(menu, "take_off_or_detach", mask == (MASK_ATTACHMENT|MASK_CLOTHING)); + setMenuItemEnabled(menu, "take_off_or_detach", n_worn == n_items); + setMenuItemVisible(menu, "object_profile", mask & (MASK_ATTACHMENT|MASK_CLOTHING)); + setMenuItemEnabled(menu, "object_profile", n_items == 1); + + // Populate or hide the "Attach to..." / "Attach to HUD..." submenus. + if (mask == MASK_ATTACHMENT && n_worn == 0) + { + LLViewerAttachMenu::populateMenus("wearable_attach_to", "wearable_attach_to_hud"); + } + else + { + setMenuItemVisible(menu, "wearable_attach_to", false); + setMenuItemVisible(menu, "wearable_attach_to_hud", false); + } + + if (mask & MASK_UNKNOWN) + { + llwarns << "Non-wearable items passed." << llendl; + } +} + +void LLWearableItemsList::ContextMenu::updateItemsLabels(LLContextMenu* menu) +{ + llassert(menu); + if (!menu) return; + + // Set proper label for the "Create new " menu item. + LLViewerInventoryItem* item = gInventory.getLinkedItem(mUUIDs.back()); + if (!item || !item->isWearableType()) return; + + LLStringUtil::format_map_t args; + LLWearableType::EType w_type = item->getWearableType(); + args["[WEARABLE_TYPE]"] = LLWearableType::getTypeDefaultNewName(w_type); + std::string new_label = LLTrans::getString("CreateNewWearable", args); + + LLMenuItemGL* menu_item = menu->getChild("create_new"); + menu_item->setLabel(new_label); } // We need this method to convert non-zero BOOL values to exactly 1 (TRUE). // Otherwise code relying on a BOOL value being TRUE may fail // (I experienced a weird assert in LLView::drawChildren() because of that. +// static void LLWearableItemsList::ContextMenu::setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val) { menu->setItemVisible(name, val); } +// static +void LLWearableItemsList::ContextMenu::setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val) +{ + menu->setItemEnabled(name, val); +} + +// static void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType at) { if (at == LLAssetType::AT_CLOTHING) @@ -613,8 +664,18 @@ void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType } else { - llwarns << "Unsupported asset type: " << at << llendl; + mask |= MASK_UNKNOWN; } } +// static +void LLWearableItemsList::ContextMenu::createNewWearable(const LLUUID& item_id) +{ + // *TODO: proper implementation of creating new wearables. + LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id); + if (!item || !item->isWearableType()) return; + + LLAgentWearables::createWearable(item->getWearableType(), true); +} + // EOF diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index 995a8976f3..0ed480a92a 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -315,12 +315,16 @@ public: MASK_CLOTHING = 0x01, MASK_BODYPART = 0x02, MASK_ATTACHMENT = 0x04, + MASK_UNKNOWN = 0x08, }; /* virtual */ LLContextMenu* createMenu(); void updateItemsVisibility(LLContextMenu* menu); - void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val); - void updateMask(U32& mask, LLAssetType::EType at); + void updateItemsLabels(LLContextMenu* menu); + static void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val); + static void setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val); + static void updateMask(U32& mask, LLAssetType::EType at); + static void createNewWearable(const LLUUID& item_id); }; struct Params : public LLInitParam::Block diff --git a/indra/newview/skins/default/xui/en/menu_cof_attachment.xml b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml index b422d87938..c402100fb1 100644 --- a/indra/newview/skins/default/xui/en/menu_cof_attachment.xml +++ b/indra/newview/skins/default/xui/en/menu_cof_attachment.xml @@ -10,12 +10,4 @@ function="Attachment.Detach" parameter="detach"/> - - diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml index dfc72b557c..3cae12e30c 100644 --- a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml @@ -7,8 +7,7 @@ layout="topleft" name="wear"> + function="Gear.Wear" /> @@ -18,20 +17,164 @@ layout="topleft" name="take_off"> + function="Gear.TakeOff" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + function="Gear.Rename" /> @@ -41,10 +184,9 @@ layout="topleft" name="delete_outfit"> + function="Gear.Delete" /> + parameter="delete" /> diff --git a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml index 8f3e62157a..67559638d9 100644 --- a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml +++ b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml @@ -8,6 +8,9 @@ name="wear_replace"> + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml index 7ea7eaade5..46aca54eee 100644 --- a/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml +++ b/indra/newview/skins/default/xui/en/menu_wearable_list_item.xml @@ -8,6 +8,13 @@ + + + - + name="wearable_attach_to_hud" /> + function="Clothing.TakeOff" /> + function="Wearable.Edit" /> + + + diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 2590924b58..5ead756d20 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2068,6 +2068,28 @@ Would you be my friend? + + New outfit name: +
+ + [NAME] + +