From a00af78b95a62c613f9a2ac4a28a0017086c5ffa Mon Sep 17 00:00:00 2001 From: Loren Shih Date: Wed, 24 Jun 2009 21:36:42 +0000 Subject: merge -r125267:125272 svn+ssh://svn.lindenlab.com/svn/linden/branches/avatar-pipeline/inventory-links__merge__viewer2.0.0-3-r125202 to svn+ssh://svn.lindenlab.com/svn/linden/branches/viewer/viewer-2.0.0-3 --- indra/newview/llagentwearables.cpp | 19 +- indra/newview/llagentwearables.h | 2 +- indra/newview/llfloaterproperties.cpp | 16 +- indra/newview/llfolderview.h | 1 + indra/newview/llinventorybridge.cpp | 679 ++++++++++++++------- indra/newview/llinventorybridge.h | 39 +- indra/newview/llinventorymodel.cpp | 60 +- indra/newview/llinventorymodel.h | 30 +- indra/newview/llviewerinventory.cpp | 135 +++- indra/newview/llviewerinventory.h | 22 +- indra/newview/llvoavatarself.cpp | 33 +- indra/newview/llvoavatarself.h | 2 +- indra/newview/llwearablelist.cpp | 17 +- .../skins/default/xui/en/menu_inventory.xml | 8 + 14 files changed, 806 insertions(+), 257 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 1c756b1441..1da54ad08c 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -626,9 +626,24 @@ const LLUUID& LLAgentWearables::getWearableItem(EWearableType type, U32 index) c } -BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const +// Warning: include_linked_items = TRUE makes this operation expensive. +BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id, BOOL include_linked_items) const { - return (getWearableFromWearableItem(item_id) != NULL); + if (getWearableFromWearableItem(item_id) != NULL) return TRUE; + if (include_linked_items) + { + LLInventoryModel::item_array_t item_array; + gInventory.collectLinkedItems(item_id, item_array); + for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + LLViewerInventoryItem *linked_item = (*iter); + const LLUUID &item_id = linked_item->getUUID(); + if (getWearableFromWearableItem(item_id) != NULL) return TRUE; + } + } + return FALSE; } // MULTI-WEARABLE: update for multiple diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 98f49203d3..31d6e30069 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -63,7 +63,7 @@ protected: // Queries //-------------------------------------------------------------------- public: - BOOL isWearingItem(const LLUUID& item_id) const; + BOOL isWearingItem(const LLUUID& item_id, const BOOL include_linked_items = FALSE) const; BOOL isWearableModifiable(EWearableType type, U32 index /*= 0*/) const; BOOL isWearableCopyable(EWearableType type, U32 index /*= 0*/) const; BOOL areWearablesLoaded() const { return mWearablesLoaded; } diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp index 5a8afc2277..dc72b66949 100644 --- a/indra/newview/llfloaterproperties.cpp +++ b/indra/newview/llfloaterproperties.cpp @@ -305,10 +305,11 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) BOOL is_complete = i->isComplete(); const LLPermissions& perm = item->getPermissions(); - BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm, - GP_OBJECT_MANIPULATE); - BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm, - GP_OBJECT_SET_SALE); + const BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm, + GP_OBJECT_MANIPULATE); + const BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm, + GP_OBJECT_SET_SALE); + const BOOL is_link = LLAssetType::lookupIsLinkType(i->getActualType()); // You need permission to modify the object to modify an inventory // item in it. @@ -491,7 +492,12 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) ///////////// // Check for ability to change values. - if (is_obj_modify && can_agent_manipulate) + if (is_link) + { + childSetEnabled("CheckShareWithGroup",FALSE); + childSetEnabled("CheckEveryoneCopy",FALSE); + } + else if (is_obj_modify && can_agent_manipulate) { childSetEnabled("CheckShareWithGroup",TRUE); childSetEnabled("CheckEveryoneCopy",(owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER)); diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 9d91f0d64e..3386a7fb0e 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -103,6 +103,7 @@ public: virtual void cutToClipboard() = 0; virtual BOOL isClipboardPasteable() const = 0; virtual void pasteFromClipboard() = 0; + virtual void pasteLinkFromClipboard() = 0; virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; virtual BOOL isUpToDate() const = 0; virtual BOOL hasChildren() const = 0; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 540cefbc46..af653238d3 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -202,6 +202,38 @@ BOOL LLInvFVBridge::isItemRemovable() return FALSE; } +// Sends an update to all link items that point to the base item. +void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& new_name) +{ + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + + if (LLAssetType::lookupIsLinkType(itemp->getActualType())) + { + return; + } + + LLInventoryModel::item_array_t item_array; + model->collectLinkedItems(item_id, item_array); + for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + LLViewerInventoryItem *linked_item = (*iter); + if (linked_item->getUUID() == item_id) continue; + + LLPointer new_item = new LLViewerInventoryItem(linked_item); + new_item->rename(new_name); + new_item->updateServer(FALSE); + model->updateItem(new_item); + // model->addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID()); + } + model->notifyObservers(); +} + // Can be moved to another folder BOOL LLInvFVBridge::isItemMovable() { @@ -370,15 +402,71 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArrayisObjectDescendentOf(mUUID, gAgent.getInventoryRootID()); + if (!model) + { + return FALSE; + } + + const LLUUID &agent_id = gAgent.getID(); - if(LLInventoryClipboard::instance().hasContents() && is_agent_inventory) + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + for(S32 i = 0; i < count; i++) { - return TRUE; + const LLUUID &item_id = objects.get(i); + + // Can't paste folders + const LLInventoryCategory *cat = model->getCategory(item_id); + if (cat) + { + return FALSE; + } + + const LLInventoryItem *item = model->getItem(item_id); + if (item) + { + if (!item->getPermissions().allowCopyBy(agent_id)) + { + return FALSE; + } + } } - return FALSE; + return TRUE; +} + +BOOL LLInvFVBridge::isClipboardPasteableAsLink() const +{ + if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) + { + return FALSE; + } + LLInventoryModel* model = getInventoryModel(); + if (!model) + { + return FALSE; + } + + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + for(S32 i = 0; i < count; i++) + { + LLInventoryItem *item = model->getItem(objects.get(i)); + if (item) + { + if (!LLAssetType::lookupCanLink(item->getActualType())) + { + return FALSE; + } + } + } + return TRUE; } void hideContextEntries(LLMenuGL& menu, @@ -461,6 +549,11 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Paste")); } + items.push_back(std::string("Paste As Link")); + if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Paste As Link")); + } items.push_back(std::string("Paste Separator")); items.push_back(std::string("Delete")); @@ -544,10 +637,25 @@ BOOL LLInvFVBridge::isInTrash() const { LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; - LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + const LLUUID& trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); return model->isObjectDescendentOf(mUUID, trash_id); } +BOOL LLInvFVBridge::isLinkedObjectInTrash() const +{ + if (isInTrash()) return TRUE; + + LLInventoryModel* model = getInventoryModel(); + if(!model) return FALSE; + LLInventoryObject *obj = model->getObject(mUUID); + if (obj && LLAssetType::lookupIsLinkType(obj->getActualType())) + { + const LLUUID& trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH); + return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id); + } + return FALSE; +} + BOOL LLInvFVBridge::isAgentInventory() const { LLInventoryModel* model = getInventoryModel(); @@ -627,108 +735,106 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, LLInvFVBridge* new_listener = NULL; switch(asset_type) { - case LLAssetType::AT_TEXTURE: - if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLTextureBridge(inventory, uuid, inv_type); - break; + case LLAssetType::AT_TEXTURE: + if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLTextureBridge(inventory, uuid, inv_type); + break; - case LLAssetType::AT_SOUND: - if(!(inv_type == LLInventoryType::IT_SOUND)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLSoundBridge(inventory, uuid); - break; + case LLAssetType::AT_SOUND: + if(!(inv_type == LLInventoryType::IT_SOUND)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLSoundBridge(inventory, uuid); + break; - case LLAssetType::AT_LANDMARK: - if(!(inv_type == LLInventoryType::IT_LANDMARK)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLLandmarkBridge(inventory, uuid, flags); - break; + case LLAssetType::AT_LANDMARK: + if(!(inv_type == LLInventoryType::IT_LANDMARK)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLLandmarkBridge(inventory, uuid, flags); + break; - case LLAssetType::AT_CALLINGCARD: - if(!(inv_type == LLInventoryType::IT_CALLINGCARD)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLCallingCardBridge(inventory, uuid); - break; - - case LLAssetType::AT_SCRIPT: - if(!(inv_type == LLInventoryType::IT_LSL)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLScriptBridge(inventory, uuid); - break; - - case LLAssetType::AT_OBJECT: - if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags); - break; + case LLAssetType::AT_CALLINGCARD: + if(!(inv_type == LLInventoryType::IT_CALLINGCARD)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLCallingCardBridge(inventory, uuid); + break; - case LLAssetType::AT_NOTECARD: - if(!(inv_type == LLInventoryType::IT_NOTECARD)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLNotecardBridge(inventory, uuid); - break; + case LLAssetType::AT_SCRIPT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLScriptBridge(inventory, uuid); + break; - case LLAssetType::AT_ANIMATION: - if(!(inv_type == LLInventoryType::IT_ANIMATION)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLAnimationBridge(inventory, uuid); - break; + case LLAssetType::AT_OBJECT: + if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags); + break; - case LLAssetType::AT_GESTURE: - if(!(inv_type == LLInventoryType::IT_GESTURE)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLGestureBridge(inventory, uuid); - break; + case LLAssetType::AT_NOTECARD: + if(!(inv_type == LLInventoryType::IT_NOTECARD)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLNotecardBridge(inventory, uuid); + break; - case LLAssetType::AT_LSL_TEXT: - if(!(inv_type == LLInventoryType::IT_LSL)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLLSLTextBridge(inventory, uuid); - break; + case LLAssetType::AT_ANIMATION: + if(!(inv_type == LLInventoryType::IT_ANIMATION)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLAnimationBridge(inventory, uuid); + break; - case LLAssetType::AT_CLOTHING: - case LLAssetType::AT_BODYPART: - if(!(inv_type == LLInventoryType::IT_WEARABLE)) - { - llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; - } - new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags); - break; + case LLAssetType::AT_GESTURE: + if(!(inv_type == LLInventoryType::IT_GESTURE)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLGestureBridge(inventory, uuid); + break; - case LLAssetType::AT_CATEGORY: - case LLAssetType::AT_ROOT_CATEGORY: - new_listener = new LLFolderBridge(inventory, uuid); - break; + case LLAssetType::AT_LSL_TEXT: + if(!(inv_type == LLInventoryType::IT_LSL)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLLSLTextBridge(inventory, uuid); + break; - case LLAssetType::AT_FAVORITE: - new_listener = new LLFolderBridge(inventory, uuid); - break; - - default: - llinfos << "Unhandled asset type (llassetstorage.h): " - << (S32)asset_type << llendl; - break; + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + if(!(inv_type == LLInventoryType::IT_WEARABLE)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags); + break; + case LLAssetType::AT_CATEGORY: + case LLAssetType::AT_ROOT_CATEGORY: + new_listener = new LLFolderBridge(inventory, uuid); + break; + case LLAssetType::AT_LINK: + // Only should happen for broken links. + new_listener = new LLLinkItemBridge(inventory, uuid); + break; + default: + llinfos << "Unhandled asset type (llassetstorage.h): " + << (S32)asset_type << llendl; + break; } if (new_listener) @@ -739,6 +845,22 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, return new_listener; } +void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) +{ + LLInventoryCategory* cat = model->getCategory(uuid); + if (cat) + { + model->purgeDescendentsOf(uuid); + model->notifyObservers(); + } + LLInventoryObject* obj = model->getObject(uuid); + if (obj) + { + model->purgeObject(uuid); + model->notifyObservers(); + } +} + // +=================================================+ // | LLItemBridge | // +=================================================+ @@ -748,32 +870,27 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, if ("open" == action) { openItem(); + return; } else if ("properties" == action) { showProperties(); + return; } else if ("purge" == action) { - LLInventoryCategory* cat = model->getCategory(mUUID); - if(cat) - { - model->purgeDescendentsOf(mUUID); - } - LLInventoryObject* obj = model->getObject(mUUID); - if(!obj) return; - obj->removeFromServer(); - LLPreview::hide(mUUID); - model->deleteObject(mUUID); - model->notifyObservers(); + purgeItem(model, mUUID); + return; } else if ("restoreToWorld" == action) { restoreToWorld(); + return; } else if ("restore" == action) { restoreItem(); + return; } else if ("copy_uuid" == action) { @@ -804,6 +921,18 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, folder_view_itemp->getListener()->pasteFromClipboard(); return; } + else if ("paste_link" == action) + { + // Single item only + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + + LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID()); + if (!folder_view_itemp) return; + + folder_view_itemp->getListener()->pasteLinkFromClipboard(); + return; + } } void LLItemBridge::selectItem() @@ -913,6 +1042,24 @@ void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name) } } +LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const +{ + U8 font = LLFontGL::NORMAL; + + if( gAgentWearables.isWearingItem( mUUID ) ) + { + // llinfos << "BOLD" << llendl; + font |= LLFontGL::BOLD; + } + + const LLViewerInventoryItem* item = getItem(); + if (LLAssetType::lookupIsLinkType(item->getActualType())) + { + font |= LLFontGL::ITALIC; + } + return (LLFontGL::StyleFlags)font; +} + std::string LLItemBridge::getLabelSuffix() const { // assume that this won't be called before string table is loaded @@ -932,8 +1079,12 @@ std::string LLItemBridge::getLabelSuffix() const BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + BOOL link = (item->getActualType() == LLAssetType::AT_LINK); const char* EMPTY = ""; + const char* LINK = " (link)"; // *TODO: Seraph translate + if (link) return LINK; + const char* scopy; if(copy) scopy = EMPTY; else scopy = NO_COPY; @@ -986,6 +1137,8 @@ BOOL LLItemBridge::renameItem(const std::string& new_name) buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); + renameLinkedItems(item->getUUID(),new_name); + model->notifyObservers(); } // return FALSE because we either notified observers (& therefore @@ -1034,13 +1187,19 @@ BOOL LLItemBridge::isItemCopyable() const return FALSE; } - if( avatarp->isWearingAttachment( mUUID ) ) + if( avatarp->isWearingAttachment( mUUID, TRUE ) ) { return FALSE; } - - - return (item->getPermissions().allowCopyBy(gAgent.getID())); + + // All items can be copied, not all can be pasted. + // The only time an item can't be copied is if it's a link + // return (item->getPermissions().allowCopyBy(gAgent.getID())); + if (item->getActualType() == LLAssetType::AT_LINK) + { + return FALSE; + } + return TRUE; } return FALSE; } @@ -1152,7 +1311,7 @@ BOOL LLFolderBridge::isItemRemovable() if( (item->getType() == LLAssetType::AT_CLOTHING) || (item->getType() == LLAssetType::AT_BODYPART) ) { - if( gAgentWearables.isWearingItem( item->getUUID() ) ) + if( gAgentWearables.isWearingItem( item->getUUID(), TRUE ) ) { return FALSE; } @@ -1160,7 +1319,7 @@ BOOL LLFolderBridge::isItemRemovable() else if( item->getType() == LLAssetType::AT_OBJECT ) { - if( avatar->isWearingAttachment( item->getUUID() ) ) + if( avatar->isWearingAttachment( item->getUUID(), TRUE ) ) { return FALSE; } @@ -1183,6 +1342,21 @@ BOOL LLFolderBridge::isUpToDate() const return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; } +BOOL LLFolderBridge::isItemCopyable() const +{ + return TRUE; +} + +BOOL LLFolderBridge::copyToClipboard() const +{ + if(isItemCopyable()) + { + LLInventoryClipboard::instance().add(mUUID); + return TRUE; + } + return FALSE; +} + BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, BOOL drop) { @@ -1620,22 +1794,37 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model if ("open" == action) { openItem(); + return; } else if ("paste" == action) { pasteFromClipboard(); + return; + } + else if ("paste_link" == action) + { + pasteLinkFromClipboard(); + return; } else if ("properties" == action) { showProperties(); + return; } else if ("replaceoutfit" == action) { modifyOutfit(FALSE); + return; } else if ("addtooutfit" == action) { modifyOutfit(TRUE); + return; + } + else if ("copy" == action) + { + copyToClipboard(); + return; } else if ("removefromoutfit" == action) { @@ -1645,25 +1834,17 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model if(!cat) return; remove_inventory_category_from_avatar ( cat ); + return; } else if ("purge" == action) { - LLViewerInventoryCategory* cat; - cat = (LLViewerInventoryCategory*)getCategory(); - - if(cat) - { - model->purgeDescendentsOf(mUUID); - } - LLInventoryObject* obj = model->getObject(mUUID); - if(!obj) return; - obj->removeFromServer(); - model->deleteObject(mUUID); - model->notifyObservers(); + purgeItem(model, mUUID); + return; } else if ("restore" == action) { restoreItem(); + return; } } @@ -1723,59 +1904,59 @@ LLUIImagePtr LLFolderBridge::getIcon() const } switch(preferred_type) { - case LLAssetType::AT_TEXTURE: - control = "inv_folder_texture.tga"; - break; - case LLAssetType::AT_SOUND: - control = "inv_folder_sound.tga"; - break; - case LLAssetType::AT_CALLINGCARD: - control = "inv_folder_callingcard.tga"; - break; - case LLAssetType::AT_LANDMARK: - control = "inv_folder_landmark.tga"; - break; - case LLAssetType::AT_SCRIPT: - case LLAssetType::AT_LSL_TEXT: - control = "inv_folder_script.tga"; - break; - case LLAssetType::AT_OBJECT: - control = "inv_folder_object.tga"; - break; - case LLAssetType::AT_NOTECARD: - control = "inv_folder_notecard.tga"; - break; - case LLAssetType::AT_CATEGORY: - control = "inv_folder_plain_closed.tga"; - break; - case LLAssetType::AT_CLOTHING: - control = "inv_folder_clothing.tga"; - break; - case LLAssetType::AT_BODYPART: - control = "inv_folder_bodypart.tga"; - break; - case LLAssetType::AT_TRASH: - control = "inv_folder_trash.tga"; - break; - case LLAssetType::AT_SNAPSHOT_CATEGORY: - control = "inv_folder_snapshot.tga"; - break; - case LLAssetType::AT_LOST_AND_FOUND: - control = "inv_folder_lostandfound.tga"; - break; - case LLAssetType::AT_ANIMATION: - control = "inv_folder_animation.tga"; - break; - case LLAssetType::AT_GESTURE: - control = "inv_folder_gesture.tga"; - break; - case LLAssetType::AT_FAVORITE: - //TODO - need icon - control = "inv_folder_plain_closed.tga"; - break; - default: - control = "inv_folder_plain_closed.tga"; - break; + case LLAssetType::AT_TEXTURE: + control = "inv_folder_texture.tga"; + break; + case LLAssetType::AT_SOUND: + control = "inv_folder_sound.tga"; + break; + case LLAssetType::AT_CALLINGCARD: + control = "inv_folder_callingcard.tga"; + break; + case LLAssetType::AT_LANDMARK: + control = "inv_folder_landmark.tga"; + break; + case LLAssetType::AT_SCRIPT: + case LLAssetType::AT_LSL_TEXT: + control = "inv_folder_script.tga"; + break; + case LLAssetType::AT_OBJECT: + control = "inv_folder_object.tga"; + break; + case LLAssetType::AT_NOTECARD: + control = "inv_folder_notecard.tga"; + break; + case LLAssetType::AT_CATEGORY: + control = "inv_folder_plain_closed.tga"; + break; + case LLAssetType::AT_CLOTHING: + control = "inv_folder_clothing.tga"; + break; + case LLAssetType::AT_BODYPART: + control = "inv_folder_bodypart.tga"; + break; + case LLAssetType::AT_TRASH: + control = "inv_folder_trash.tga"; + break; + case LLAssetType::AT_SNAPSHOT_CATEGORY: + control = "inv_folder_snapshot.tga"; + break; + case LLAssetType::AT_LOST_AND_FOUND: + control = "inv_folder_lostandfound.tga"; + break; + case LLAssetType::AT_ANIMATION: + control = "inv_folder_animation.tga"; + break; + case LLAssetType::AT_GESTURE: + control = "inv_folder_gesture.tga"; + break; + case LLAssetType::AT_FAVORITE: + //TODO - need icon + control = "inv_folder_plain_closed.tga"; + break; + default: + control = "inv_folder_plain_closed.tga"; + break; } return LLUI::getUIImage(control); } @@ -1794,6 +1975,8 @@ BOOL LLFolderBridge::renameItem(const std::string& new_name) new_cat->rename(new_name); new_cat->updateServer(FALSE); model->updateCategory(new_cat); + renameLinkedItems(cat->getUUID(),new_name); + model->notifyObservers(); } // return FALSE because we either notified observers (& therefore @@ -1842,15 +2025,6 @@ BOOL LLFolderBridge::removeItem() return TRUE; } -BOOL LLFolderBridge::isClipboardPasteable() const -{ - if(LLInventoryClipboard::instance().hasContents() && isAgentInventory()) - { - return TRUE; - } - return FALSE; -} - void LLFolderBridge::pasteFromClipboard() { LLInventoryModel* model = getInventoryModel(); @@ -1878,6 +2052,33 @@ void LLFolderBridge::pasteFromClipboard() } } +void LLFolderBridge::pasteLinkFromClipboard() +{ + LLInventoryModel* model = getInventoryModel(); + if(model) + { + LLInventoryItem* item = NULL; + LLDynamicArray objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + LLUUID parent_id(mUUID); + for(S32 i = 0; i < count; i++) + { + item = model->getItem(objects.get(i)); + if (item) + { + link_inventory_item( + gAgent.getID(), + item->getUUID(), + parent_id, + item->getName(), + LLAssetType::AT_LINK, + LLPointer(NULL)); + } + } + } +} + void LLFolderBridge::staticFolderOptionsMenu() { if (!sSelf) return; @@ -1948,6 +2149,9 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv // Flags unused void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { + mItems.clear(); + mDisabledItems.clear(); + lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; // std::vector disabled_items; LLInventoryModel* model = getInventoryModel(); @@ -3116,7 +3320,7 @@ BOOL LLObjectBridge::isItemRemovable() { LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); if(!avatar) return FALSE; - if(avatar->isWearingAttachment(mUUID)) return FALSE; + if(avatar->isWearingAttachment(mUUID, TRUE)) return FALSE; return LLInvFVBridge::isItemRemovable(); } @@ -3125,6 +3329,17 @@ LLUIImagePtr LLObjectBridge::getIcon() const return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject ); } +LLInventoryObject* LLObjectBridge::getObject() const +{ + LLInventoryObject* object = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + object = (LLInventoryObject*)model->getObject(mUUID); + } + return object; +} + // virtual void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action) { @@ -3194,15 +3409,21 @@ void LLObjectBridge::openItem() LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const { + U8 font = LLFontGL::NORMAL; + LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); if( avatar && avatar->isWearingAttachment( mUUID ) ) { - return LLFontGL::BOLD; + font |= LLFontGL::BOLD; } - else + + LLInventoryItem* item = getItem(); + if (item->getActualType() == LLAssetType::AT_LINK) { - return LLFontGL::NORMAL; + font |= LLFontGL::ITALIC; } + + return (LLFontGL::StyleFlags)font; } std::string LLObjectBridge::getLabelSuffix() const @@ -3223,7 +3444,7 @@ std::string LLObjectBridge::getLabelSuffix() const void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment) { LLSD payload; - payload["item_id"] = item->getUUID(); + payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link. S32 attach_pt = 0; if (gAgent.getAvatarObject() && attachment) @@ -3315,7 +3536,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Detach From Yourself")); } else - if( !isInTrash() ) + if( !isInTrash() && !isLinkedObjectInTrash() ) { items.push_back(std::string("Attach Separator")); items.push_back(std::string("Object Wear")); @@ -3381,6 +3602,8 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); + renameLinkedItems(item->getUUID(),new_name); + model->notifyObservers(); LLVOAvatarSelf* avatar = gAgent.getAvatarObject(); @@ -4141,23 +4364,10 @@ BOOL LLWearableBridge::renameItem(const std::string& new_name) BOOL LLWearableBridge::isItemRemovable() { - if(gAgentWearables.isWearingItem(mUUID)) return FALSE; + if (gAgentWearables.isWearingItem(mUUID, TRUE)) return FALSE; return LLInvFVBridge::isItemRemovable(); } -LLFontGL::StyleFlags LLWearableBridge::getLabelStyle() const -{ - if( gAgentWearables.isWearingItem( mUUID ) ) - { - // llinfos << "BOLD" << llendl; - return LLFontGL::BOLD; - } - else - { - return LLFontGL::NORMAL; - } -} - std::string LLWearableBridge::getLabelSuffix() const { if( gAgentWearables.isWearingItem( mUUID ) ) @@ -4300,6 +4510,13 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { disabled_items.push_back(std::string("Wearable Edit")); } + // Don't allow items to be worn if their baseobj is in the trash. + if (isLinkedObjectInTrash()) + { + disabled_items.push_back(std::string("Wearable Wear")); + disabled_items.push_back(std::string("Wearable Add")); + disabled_items.push_back(std::string("Wearable Edit")); + } if( item && (item->getType() == LLAssetType::AT_CLOTHING) ) { @@ -4534,9 +4751,8 @@ void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable, delete item_id; } - LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type, - const LLUUID& uuid,LLInventoryModel* model) + const LLUUID& uuid,LLInventoryModel* model) { LLInvFVBridgeAction* action = NULL; switch(asset_type) @@ -4819,3 +5035,44 @@ void LLWearableBridgeAction::doIt() LLInvFVBridgeAction::doIt(); } + +// +=================================================+ +// | LLLinkItemBridge | +// +=================================================+ +// For broken links + +std::string LLLinkItemBridge::sPrefix("Link: "); + + +LLUIImagePtr LLLinkItemBridge::getIcon() const +{ + return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE); +} + +void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + // *TODO: Translate + lldebugs << "LLLink::buildContextMenu()" << llendl; + std::vector items; + std::vector disabled_items; + + if(isInTrash()) + { + items.push_back(std::string("Purge Item")); + if (!isItemRemovable()) + { + disabled_items.push_back(std::string("Purge Item")); + } + + items.push_back(std::string("Restore Item")); + } + else + { + items.push_back(std::string("Delete")); + if (!isItemRemovable()) + { + disabled_items.push_back(std::string("Delete")); + } + } + hideContextEntries(menu, items, disabled_items); +} diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 3958f7e9c2..016eb701d6 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -167,7 +167,9 @@ public: virtual BOOL copyToClipboard() const { return FALSE; } virtual void cutToClipboard() {} virtual BOOL isClipboardPasteable() const; + virtual BOOL isClipboardPasteableAsLink() const; virtual void pasteFromClipboard() {} + virtual void pasteLinkFromClipboard() {} void getClipboardEntries(bool show_asset_id, std::vector &items, std::vector &disabled_items, U32 flags); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); @@ -191,6 +193,8 @@ protected: LLInventoryModel* getInventoryModel() const; BOOL isInTrash() const; + BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash? + // return true if the item is in agent inventory. if false, it // must be lost or in the inventory library. BOOL isAgentInventory() const; @@ -204,11 +208,13 @@ protected: const LLUUID& new_parent, BOOL restamp); void removeBatchNoCheck(LLDynamicArray& batch); - + void renameLinkedItems(const LLUUID &item_id, const std::string& new_name); + protected: LLHandle mInventoryPanel; - LLUUID mUUID; // item id + const LLUUID mUUID; // item id LLInventoryType::EType mInvType; + void purgeItem(LLInventoryModel *model, const LLUUID &uuid); }; @@ -227,6 +233,7 @@ public: virtual LLUIImagePtr getIcon() const; virtual const std::string& getDisplayName() const; virtual std::string getLabelSuffix() const; + virtual LLFontGL::StyleFlags getLabelStyle() const; virtual PermissionMask getPermissionMask() const; virtual time_t getCreationDate() const; virtual BOOL isItemRenameable() const; @@ -267,8 +274,8 @@ public: virtual LLUIImagePtr getIcon() const; virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); - virtual BOOL isClipboardPasteable() const; virtual void pasteFromClipboard(); + virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual BOOL hasChildren() const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, @@ -278,7 +285,9 @@ public: virtual BOOL isItemRemovable(); virtual BOOL isItemMovable(); virtual BOOL isUpToDate() const; - + virtual BOOL isItemCopyable() const; + virtual BOOL copyToClipboard() const; + static void createWearable(LLFolderBridge* bridge, EWearableType type); static void createWearable(LLUUID parent_folder_id, EWearableType type); @@ -489,6 +498,8 @@ public: virtual BOOL isItemRemovable(); virtual BOOL renameItem(const std::string& new_name); + LLInventoryObject* getObject() const; + protected: LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) : LLItemBridge(inventory, uuid), mInvType(type) @@ -527,7 +538,6 @@ public: virtual void performAction(LLFolderView* folder, LLInventoryModel* model, std::string action); virtual void openItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual LLFontGL::StyleFlags getLabelStyle() const; virtual std::string getLabelSuffix() const; virtual BOOL isItemRemovable(); virtual BOOL renameItem(const std::string& new_name); @@ -562,8 +572,25 @@ protected: EWearableType mWearableType; }; +class LLLinkItemBridge : public LLItemBridge +{ + friend class LLInvFVBridge; +public: + virtual const std::string& getPrefix() { return sPrefix; } + + virtual LLUIImagePtr getIcon() const; + virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + +protected: + LLLinkItemBridge(LLInventoryPanel* inventory, const LLUUID& uuid) : + LLItemBridge(inventory, uuid) {} + +protected: + static std::string sPrefix; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInvFVBridgeAction (& it's derived classes) +// Class LLInvFVBridgeAction (& its derived classes) // // This is an implementation class to be able to // perform action to view inventory items. diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 9177d51d5c..5a14bdd55e 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -464,6 +464,18 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, } } +void LLInventoryModel::collectLinkedItems(const LLUUID& id, + item_array_t& items) +{ + LLInventoryModel::cat_array_t cat_array; + LLLinkedItemIDMatches is_linked_item_match(id); + collectDescendentsIf(gAgent.getInventoryRootID(), + cat_array, + items, + LLInventoryModel::INCLUDE_TRASH, + is_linked_item_match); +} + // Generates a string containing the path to the item specified by // item_id. void LLInventoryModel::appendPath(const LLUUID& id, std::string& path) @@ -747,6 +759,7 @@ void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id) // Delete a particular inventory object by ID. void LLInventoryModel::deleteObject(const LLUUID& id) { + purgeLinkedObjects(id); lldebugs << "LLInventoryModel::deleteObject()" << llendl; LLPointer obj = getObject(id); if(obj) @@ -786,6 +799,42 @@ void LLInventoryModel::deleteObject(const LLUUID& id) } } +// Delete a particular inventory item by ID, and remove it from the server. +void LLInventoryModel::purgeObject(const LLUUID &id) +{ + lldebugs << "LLInventoryModel::purgeObject()" << llendl; + LLPointer obj = getObject(id); + if(obj) + { + obj->removeFromServer(); + LLPreview::hide(id); + deleteObject(id); + } +} + +void LLInventoryModel::purgeLinkedObjects(const LLUUID &id) +{ + LLInventoryItem* itemp = getItem(id); + if (!itemp) return; + + if (LLAssetType::lookupIsLinkType(itemp->getActualType())) + { + return; + } + + LLInventoryModel::item_array_t item_array; + collectLinkedItems(id, item_array); + + for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + LLViewerInventoryItem *linked_item = (*iter); + if (linked_item->getUUID() == id) continue; + purgeObject(linked_item->getUUID()); + } +} + // This is a method which collects the descendents of the id // provided. If the category is not found, no action is // taken. This method goes through the long winded process of @@ -3914,11 +3963,20 @@ void LLInventoryTransactionObserver::changed(U32 mask) ///---------------------------------------------------------------------------- /// LLAssetIDMatches ///---------------------------------------------------------------------------- -bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +bool LLAssetIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { return (item && item->getAssetUUID() == mAssetID); } +///---------------------------------------------------------------------------- +/// LLLinkedItemIDMatches +///---------------------------------------------------------------------------- +bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return (item && + (LLAssetType::lookupIsLinkType(item->getActualType())) && + (item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID. +} ///---------------------------------------------------------------------------- /// Local function definitions diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 77e604769e..46288700d2 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -169,7 +169,7 @@ public: item_array_t*& items); void unlockDirectDescendentArrays(const LLUUID& cat_id); - // Starting with the object specified, add it's descendents to the + // 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 @@ -187,6 +187,11 @@ public: BOOL include_trash, LLInventoryCollectFunctor& add); + // Collect all items in inventory that are linked to item_id. + // Assumes item_id is itself not a linked item. + void collectLinkedItems(const LLUUID& item_id, + item_array_t& items); + // This method will return false if this inventory model is in an usabel state. // The inventory model usage is sensitive to the initial construction of the // model. @@ -227,8 +232,13 @@ public: // delete a particular inventory object by ID. This will purge one // object from the internal data structures maintaining a // cosistent internal state. No cache accounting, observer - // notification, or server update is performed. + // notification, or server update is performed. Purges linked items. void deleteObject(const LLUUID& id); + + // delete a particular inventory object by ID, and delete it from + // the server. Also purges linked items via purgeLinkedObjects. + void purgeObject(const LLUUID& id); + void purgeLinkedObjects(const LLUUID& id); // This is a method which collects the descendents of the id // provided. If the category is not found, no action is @@ -530,6 +540,22 @@ protected: LLUUID mAssetID; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLLinkedItemIDMatches +// +// This functor finds inventory items linked to the specific inventory id. +// Assumes the inventory id is itself not a linked item. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLLinkedItemIDMatches : public LLInventoryCollectFunctor +{ +public: + LLLinkedItemIDMatches(const LLUUID& item_id) : mBaseItemID(item_id) {} + virtual ~LLLinkedItemIDMatches() {} + bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); + +protected: + LLUUID mBaseItemID; +}; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLIsType diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 45d24ee7e8..2c79e67ebc 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -244,8 +244,7 @@ BOOL LLViewerInventoryItem::unpackMessage(LLSD item) } // virtual -BOOL LLViewerInventoryItem::unpackMessage( - LLMessageSystem* msg, const char* block, S32 block_num) +BOOL LLViewerInventoryItem::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num) { BOOL rv = LLInventoryItem::unpackMessage(msg, block, block_num); mIsComplete = TRUE; @@ -742,6 +741,32 @@ void copy_inventory_item( gAgent.sendReliableMessage(); } +void link_inventory_item( + const LLUUID& agent_id, + const LLUUID& item_id, + const LLUUID& parent_id, + const std::string& new_name, + const LLAssetType::EType asset_type, + LLPointer cb) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_LinkInventoryItem); + msg->nextBlockFast(_PREHASH_AgentData); + { + msg->addUUIDFast(_PREHASH_AgentID, agent_id); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + msg->nextBlockFast(_PREHASH_InventoryData); + { + msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb)); + msg->addUUIDFast(_PREHASH_FolderID, parent_id); + msg->addUUIDFast(_PREHASH_OldItemID, item_id); + msg->addStringFast(_PREHASH_Name, new_name); + msg->addU8Fast(_PREHASH_AssetType, asset_type); + } + gAgent.sendReliableMessage(); +} + void move_inventory_item( const LLUUID& agent_id, const LLUUID& session_id, @@ -948,26 +973,19 @@ void menu_create_inventory_item(LLFolderView* folder, LLFolderBridge *bridge, co LLAssetType::EType LLViewerInventoryItem::getType() const { - if (mType == LLAssetType::AT_LINK) + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) { - LLInventoryItem *linked_item = gInventory.getItem(mAssetUUID); - if (linked_item) - { - return linked_item->getType(); - } + return linked_item->getType(); } + return LLInventoryItem::getType(); } const LLUUID& LLViewerInventoryItem::getAssetUUID() const { - if (mType == LLAssetType::AT_LINK) + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) { - LLInventoryItem *linked_item = gInventory.getItem(mAssetUUID); - if (linked_item) - { - return linked_item->getAssetUUID(); - } + return linked_item->getAssetUUID(); } return LLInventoryItem::getAssetUUID(); @@ -975,11 +993,96 @@ const LLUUID& LLViewerInventoryItem::getAssetUUID() const const std::string& LLViewerInventoryItem::getName() const { - if (mType == LLAssetType::AT_LINK) + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) { - return LLInventoryItem::getName(); //+" link"; + return linked_item->getName(); } return LLInventoryItem::getName(); } +const LLPermissions& LLViewerInventoryItem::getPermissions() const +{ + // Use the actual permissions of the symlink, not its parent. + return LLInventoryItem::getPermissions(); +} + +const LLUUID& LLViewerInventoryItem::getCreatorUUID() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getCreatorUUID(); + } + + return LLInventoryItem::getCreatorUUID(); +} + +const std::string& LLViewerInventoryItem::getDescription() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getDescription(); + } + + return LLInventoryItem::getDescription(); +} + +const LLSaleInfo& LLViewerInventoryItem::getSaleInfo() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getSaleInfo(); + } + + return LLInventoryItem::getSaleInfo(); +} + +LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getInventoryType(); + } + + return LLInventoryItem::getInventoryType(); +} + +U32 LLViewerInventoryItem::getFlags() const +{ + if (const LLViewerInventoryItem *linked_item = getLinkedItem()) + { + return linked_item->getFlags(); + } + + return LLInventoryItem::getFlags(); +} + +time_t LLViewerInventoryItem::getCreationDate() const +{ + return LLInventoryItem::getCreationDate(); +} + +U32 LLViewerInventoryItem::getCRC32() const +{ + return LLInventoryItem::getCRC32(); +} + +const LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const +{ + if (mType == LLAssetType::AT_LINK) + { + const LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID); + return linked_item; + } + return NULL; +} + +const LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const +{ + if (mType == LLAssetType::AT_LINK_FOLDER) + { + const LLViewerInventoryCategory *linked_category = gInventory.getCategory(mAssetUUID); + return linked_category; + } + return NULL; +} diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 8318931dde..7084c9f37a 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -39,6 +39,7 @@ class LLFolderView; class LLFolderBridge; +class LLViewerInventoryCategory; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLViewerInventoryItem @@ -59,7 +60,15 @@ public: virtual LLAssetType::EType getType() const; virtual const LLUUID& getAssetUUID() const; virtual const std::string& getName() const; - + virtual const LLPermissions& getPermissions() const; + virtual const LLUUID& getCreatorUUID() const; + virtual const std::string& getDescription() const; + virtual const LLSaleInfo& getSaleInfo() const; + virtual LLInventoryType::EType getInventoryType() const; + virtual U32 getFlags() const; + virtual time_t getCreationDate() const; + virtual U32 getCRC32() const; // really more of a checksum. + // construct a complete viewer inventory item LLViewerInventoryItem(const LLUUID& uuid, const LLUUID& parent_uuid, const LLPermissions& permissions, @@ -133,6 +142,9 @@ public: LLTransactionID getTransactionID() const { return mTransactionID; } protected: + const LLViewerInventoryItem *getLinkedItem() const; + const LLViewerInventoryCategory *getLinkedCategory() const; + BOOL mIsComplete; LLTransactionID mTransactionID; }; @@ -279,6 +291,14 @@ void copy_inventory_item( const std::string& new_name, LLPointer cb); +void link_inventory_item( + const LLUUID& agent_id, + const LLUUID& item_id, + const LLUUID& parent_id, + const std::string& new_name, + const LLAssetType::EType asset_type, + LLPointer cb); + void move_inventory_item( const LLUUID& agent_id, const LLUUID& session_id, diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index d629767bbe..e7d7d74f62 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -907,18 +907,43 @@ void LLVOAvatarSelf::wearableUpdated( EWearableType type ) //----------------------------------------------------------------------------- // isWearingAttachment() //----------------------------------------------------------------------------- -BOOL LLVOAvatarSelf::isWearingAttachment( const LLUUID& inv_item_id ) +// Warning: include_linked_items = TRUE makes this operation expensive. +BOOL LLVOAvatarSelf::isWearingAttachment( const LLUUID& inv_item_id , BOOL include_linked_items ) const { - for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); + for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ) { - attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; + attachment_map_t::const_iterator curiter = iter++; + const LLViewerJointAttachment* attachment = curiter->second; if( attachment->getItemID() == inv_item_id ) { return TRUE; } } + + if (include_linked_items) + { + LLInventoryModel::item_array_t item_array; + gInventory.collectLinkedItems(inv_item_id, item_array); + for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + const LLViewerInventoryItem *linked_item = (*iter); + const LLUUID &item_id = linked_item->getUUID(); + for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); ) + { + attachment_map_t::const_iterator curiter = iter++; + const LLViewerJointAttachment* attachment = curiter->second; + if( attachment->getItemID() == item_id ) + { + return TRUE; + } + } + } + } + return FALSE; } diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 431c814382..02a77cba90 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -267,7 +267,7 @@ public: //-------------------------------------------------------------------- public: void updateAttachmentVisibility(U32 camera_mode); - BOOL isWearingAttachment(const LLUUID& inv_item_id); + BOOL isWearingAttachment(const LLUUID& inv_item_id, BOOL include_linked_items = FALSE) const; LLViewerObject* getWornAttachment(const LLUUID& inv_item_id ) const; const std::string getAttachedPointName(const LLUUID& inv_item_id) const; /*virtual*/ LLViewerJointAttachment *attachObject(LLViewerObject *viewer_object); diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp index 92de94636b..1275312676 100644 --- a/indra/newview/llwearablelist.cpp +++ b/indra/newview/llwearablelist.cpp @@ -77,14 +77,17 @@ LLWearableList::~LLWearableList() void LLWearableList::getAsset(const LLAssetID& _assetID, const std::string& wearable_name, LLAssetType::EType asset_type, void(*asset_arrived_callback)(LLWearable*, void* userdata), void* userdata) { LLAssetID assetID = _assetID; - if (asset_type == LLAssetType::AT_LINK) + + // A bit of a hack since wearables database doesn't contain asset types... + // Perform indirection in case this assetID is in fact a link. This only works + // because of the assumption that all assetIDs and itemIDs are unique (i.e. + // no assetID is also used as an itemID elsewhere); therefore if the assetID + // exists as an itemID in the user's inventory, then this must be a link. + const LLInventoryItem *linked_item = gInventory.getItem(_assetID); + if (linked_item) { - LLInventoryItem *linked_item = gInventory.getItem(_assetID); - if (linked_item) - { - assetID = linked_item->getAssetUUID(); - asset_type = linked_item->getType(); - } + assetID = linked_item->getAssetUUID(); + asset_type = linked_item->getType(); } llassert( (asset_type == LLAssetType::AT_CLOTHING) || (asset_type == LLAssetType::AT_BODYPART) ); LLWearable* instance = get_if_there(mList, assetID, (LLWearable*)NULL ); diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 808618ba96..6f2fd5e5e5 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -320,6 +320,14 @@ function="Inventory.DoToSelected" parameter="paste" /> + + +