diff options
Diffstat (limited to 'indra/newview/llinventorybridge.cpp')
-rw-r--r-- | indra/newview/llinventorybridge.cpp | 2000 |
1 files changed, 1514 insertions, 486 deletions
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 5ba87423c7..b86c453d61 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -39,18 +39,21 @@ #include "llavataractions.h" #include "llfloateropenobject.h" #include "llfloaterreg.h" +#include "llfloatersidepanelcontainer.h" #include "llfloaterworldmap.h" +#include "llfolderview.h" #include "llfriendcard.h" #include "llgesturemgr.h" #include "llgiveinventory.h" #include "llimfloater.h" #include "llimview.h" -#include "llinventoryclipboard.h" +#include "llclipboard.h" #include "llinventorydefines.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" #include "llinventorypanel.h" +#include "llmarketplacefunctions.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llpreviewanim.h" @@ -58,7 +61,7 @@ #include "llpreviewtexture.h" #include "llselectmgr.h" #include "llsidepanelappearance.h" -#include "llsidetray.h" +#include "lltooldraganddrop.h" #include "lltrans.h" #include "llviewerassettype.h" #include "llviewerfoldertype.h" @@ -69,6 +72,11 @@ #include "llvoavatarself.h" #include "llwearablelist.h" +// Marketplace outbox current disabled +#define ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU 1 +#define ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU 0 +#define BLOCK_WORN_ITEMS_IN_OUTBOX 1 + typedef std::pair<LLUUID, LLUUID> two_uuids_t; typedef std::list<two_uuids_t> two_uuids_list_t; @@ -104,6 +112,72 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*); bool confirm_attachment_rez(const LLSD& notification, const LLSD& response); void teleport_via_landmark(const LLUUID& asset_id); +static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); +static bool check_category(LLInventoryModel* model, + const LLUUID& cat_id, + LLFolderView* active_folder_view, + LLInventoryFilter* filter); +static bool check_item(const LLUUID& item_id, + LLFolderView* active_folder_view, + LLInventoryFilter* filter); + +// Helper functions + +bool isAddAction(const std::string& action) +{ + return ("wear" == action || "attach" == action || "activate" == action); +} + +bool isRemoveAction(const std::string& action) +{ + return ("take_off" == action || "detach" == action || "deactivate" == action); +} + +bool isMarketplaceCopyAction(const std::string& action) +{ + return (("copy_to_outbox" == action) || ("move_to_outbox" == action)); +} + +bool isMarketplaceSendAction(const std::string& action) +{ + return ("send_to_marketplace" == action); +} + +// Used by LLFolderBridge as callback for directory fetching recursion +class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver +{ +public: + LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids) : LLInventoryFetchDescendentsObserver(ids) {} + ~LLRightClickInventoryFetchDescendentsObserver() {} + virtual void execute(bool clear_observer = false); + virtual void done() + { + execute(true); + } +}; + +// Used by LLFolderBridge as callback for directory content items fetching +class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver +{ +public: + LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) : LLInventoryFetchItemsObserver(ids) { }; + ~LLRightClickInventoryFetchObserver() {} + void execute(bool clear_observer = false) + { + if (clear_observer) + { + dec_busy_count(); + gInventory.removeObserver(this); + delete this; + } + // we've downloaded all the items, so repaint the dialog + LLFolderBridge::staticFolderOptionsMenu(); + } + virtual void done() + { + execute(true); + } +}; // +=================================================+ // | LLInvFVBridge | @@ -117,7 +191,7 @@ LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, mInvType(LLInventoryType::IT_NONE), mIsLink(FALSE) { - mInventoryPanel = inventory->getHandle(); + mInventoryPanel = inventory->getInventoryPanelHandle(); const LLInventoryObject* obj = getInventoryObject(); mIsLink = obj && obj->getIsLinkType(); } @@ -177,13 +251,27 @@ BOOL LLInvFVBridge::isLink() const /** * @brief Adds this item into clipboard storage */ -void LLInvFVBridge::cutToClipboard() +BOOL LLInvFVBridge::cutToClipboard() const { - if(isItemMovable()) + const LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj && isItemMovable() && isItemRemovable()) { - LLInventoryClipboard::instance().cut(mUUID); + LLClipboard::instance().setCutMode(true); + return LLClipboard::instance().addToClipboard(mUUID); } + return FALSE; +} + +BOOL LLInvFVBridge::copyToClipboard() const +{ + const LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj && isItemCopyable()) + { + return LLClipboard::instance().addToClipboard(mUUID); + } + return FALSE; } + // *TODO: make sure this does the right thing void LLInvFVBridge::showProperties() { @@ -358,6 +446,11 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* for(; it != end; ++it) { gInventory.moveObject((*it), trash_id); + LLViewerInventoryItem* item = gInventory.getItem(*it); + if (item) + { + model->updateItem(item); + } } // notify inventory observers. @@ -366,7 +459,8 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* BOOL LLInvFVBridge::isClipboardPasteable() const { - if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) + // Return FALSE on degenerated cases: empty clipboard, no inventory, no agent + if (!LLClipboard::instance().hasContents() || !isAgentInventory()) { return FALSE; } @@ -376,37 +470,42 @@ BOOL LLInvFVBridge::isClipboardPasteable() const return FALSE; } - const LLUUID &agent_id = gAgent.getID(); + // In cut mode, whatever is on the clipboard is always pastable + if (LLClipboard::instance().isCutMode()) + { + return TRUE; + } + // In normal mode, we need to check each element of the clipboard to know if we can paste or not LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); for(S32 i = 0; i < count; i++) { const LLUUID &item_id = objects.get(i); - // Can't paste folders + // Folders are pastable if all items in there are copyable const LLInventoryCategory *cat = model->getCategory(item_id); if (cat) { + LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, item_id); + if (!cat_br.isItemCopyable()) return FALSE; + // Skip to the next item in the clipboard + continue; } - const LLInventoryItem *item = model->getItem(item_id); - if (item) - { - if (!item->getPermissions().allowCopyBy(agent_id)) - { + // Each item must be copyable to be pastable + LLItemBridge item_br(mInventoryPanel.get(), mRoot, item_id); + if (!item_br.isItemCopyable()) return FALSE; } - } - } return TRUE; } BOOL LLInvFVBridge::isClipboardPasteableAsLink() const { - if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory()) + if (!LLClipboard::instance().hasContents() || !isAgentInventory()) { return FALSE; } @@ -417,7 +516,7 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const } LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); for(S32 i = 0; i < count; i++) { @@ -440,14 +539,13 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const void hide_context_entries(LLMenuGL& menu, const menuentry_vec_t &entries_to_show, - const menuentry_vec_t &disabled_entries, - BOOL append) // If append is TRUE, then new enabled entries + const menuentry_vec_t &disabled_entries) { const LLView::child_list_t *list = menu.getChildList(); // For removing double separators or leading separator. Start at true so that // if the first element is a separator, it will not be shown. - BOOL is_previous_entry_separator = TRUE; + bool is_previous_entry_separator = true; for (LLView::child_list_t::const_iterator itor = list->begin(); itor != list->end(); @@ -463,7 +561,6 @@ void hide_context_entries(LLMenuGL& menu, hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries); } - bool found = false; menuentry_vec_t::const_iterator itor2; for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2) @@ -471,6 +568,7 @@ void hide_context_entries(LLMenuGL& menu, if (*itor2 == name) { found = true; + break; } } @@ -478,9 +576,8 @@ void hide_context_entries(LLMenuGL& menu, // between two separators). if (found) { - const BOOL is_entry_separator = (dynamic_cast<LLMenuItemSeparatorGL *>(menu_item) != NULL); - if (is_entry_separator && is_previous_entry_separator) - found = false; + const bool is_entry_separator = (dynamic_cast<LLMenuItemSeparatorGL *>(menu_item) != NULL); + found = !(is_entry_separator && is_previous_entry_separator); is_previous_entry_separator = is_entry_separator; } @@ -490,6 +587,7 @@ void hide_context_entries(LLMenuGL& menu, { menu_item->setVisible(FALSE); } + menu_item->setEnabled(FALSE); } else @@ -498,17 +596,14 @@ void hide_context_entries(LLMenuGL& menu, // A bit of a hack so we can remember that some UI element explicitly set this to be visible // so that some other UI element from multi-select doesn't later set this invisible. menu_item->pushVisible(TRUE); - if (append) - { - menu_item->setEnabled(TRUE); - } - for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2) + + bool enabled = (menu_item->getEnabled() == TRUE); + for (itor2 = disabled_entries.begin(); enabled && (itor2 != disabled_entries.end()); ++itor2) { - if (*itor2 == name) - { - menu_item->setEnabled(FALSE); - } + enabled &= (*itor2 != name); } + + menu_item->setEnabled(enabled); } } } @@ -536,10 +631,14 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, { items.push_back(std::string("Find Links")); } - items.push_back(std::string("Rename")); - if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) + + if (!isInboxFolder()) { - disabled_items.push_back(std::string("Rename")); + items.push_back(std::string("Rename")); + if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Rename")); + } } if (show_asset_id) @@ -567,11 +666,28 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, { disabled_items.push_back(std::string("Copy")); } + + items.push_back(std::string("Cut")); + if (!isItemMovable() || !isItemRemovable()) + { + disabled_items.push_back(std::string("Cut")); + } + + if (canListOnMarketplace()) + { + items.push_back(std::string("Marketplace Separator")); + + items.push_back(std::string("Merchant Copy")); + if (!canListOnMarketplaceNow()) + { + disabled_items.push_back(std::string("Merchant Copy")); + } + } } } - // Don't allow items to be pasted directly into the COF. - if (!isCOFFolder()) + // Don't allow items to be pasted directly into the COF or the inbox/outbox + if (!isCOFFolder() && !isInboxFolder() && !isOutboxFolder()) { items.push_back(std::string("Paste")); } @@ -608,6 +724,10 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); + } + else if(isOutboxFolder()) + { + addOutboxContextMenuOptions(flags, items, disabled_items); } else { @@ -684,6 +804,32 @@ void LLInvFVBridge::addOpenRightClickMenuOption(menuentry_vec_t &items) items.push_back(std::string("Open")); } +void LLInvFVBridge::addOutboxContextMenuOptions(U32 flags, + menuentry_vec_t &items, + menuentry_vec_t &disabled_items) +{ + items.push_back(std::string("Rename")); + items.push_back(std::string("Delete")); + + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Rename")); + } + +#if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU + if (isOutboxFolderDirectParent()) + { + items.push_back(std::string("Marketplace Separator")); + items.push_back(std::string("Marketplace Send")); + + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Marketplace Send")); + } + } +#endif // ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU +} + // *TODO: remove this BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const { @@ -726,7 +872,7 @@ LLInventoryObject* LLInvFVBridge::getInventoryObject() const LLInventoryModel* LLInvFVBridge::getInventoryModel() const { - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); + LLInventoryPanel* panel = mInventoryPanel.get(); return panel ? panel->getModel() : NULL; } @@ -780,6 +926,53 @@ BOOL LLInvFVBridge::isCOFFolder() const return LLAppearanceMgr::instance().getIsInCOF(mUUID); } +BOOL LLInvFVBridge::isInboxFolder() const +{ + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false); + + if (inbox_id.isNull()) + { + return FALSE; + } + + return gInventory.isObjectDescendentOf(mUUID, inbox_id); +} + +BOOL LLInvFVBridge::isOutboxFolder() const +{ + const LLUUID outbox_id = getOutboxFolder(); + + if (outbox_id.isNull()) + { + return FALSE; + } + + return gInventory.isObjectDescendentOf(mUUID, outbox_id); +} + +BOOL LLInvFVBridge::isOutboxFolderDirectParent() const +{ + BOOL outbox_is_parent = FALSE; + + const LLInventoryCategory *cat = gInventory.getCategory(mUUID); + + if (cat) + { + const LLUUID outbox_id = getOutboxFolder(); + + outbox_is_parent = (outbox_id.notNull() && (outbox_id == cat->getParentUUID())); + } + + return outbox_is_parent; +} + +const LLUUID LLInvFVBridge::getOutboxFolder() const +{ + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + + return outbox_id; +} + BOOL LLInvFVBridge::isItemPermissive() const { return FALSE; @@ -791,7 +984,7 @@ void LLInvFVBridge::changeItemParent(LLInventoryModel* model, const LLUUID& new_parent_id, BOOL restamp) { - change_item_parent(model, item, new_parent_id, restamp); + model->changeItemParent(item, new_parent_id, restamp); } // static @@ -800,7 +993,7 @@ void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model, const LLUUID& new_parent_id, BOOL restamp) { - change_category_parent(model, cat, new_parent_id, restamp); + model->changeCategoryParent(cat, new_parent_id, restamp); } LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, @@ -916,9 +1109,22 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, // Only should happen for broken links. new_listener = new LLLinkItemBridge(inventory, root, uuid); break; + case LLAssetType::AT_MESH: + if(!(inv_type == LLInventoryType::IT_MESH)) + { + llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << llendl; + } + new_listener = new LLMeshBridge(inventory, root, uuid); + break; + + case LLAssetType::AT_IMAGE_TGA: + case LLAssetType::AT_IMAGE_JPEG: + //llwarns << LLAssetType::lookup(asset_type) << " asset type is unhandled for uuid " << uuid << llendl; + break; + default: llinfos << "Unhandled asset type (llassetstorage.h): " - << (S32)asset_type << llendl; + << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << llendl; break; } @@ -946,27 +1152,138 @@ void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid) } } -BOOL LLInvFVBridge::canShare() const +bool LLInvFVBridge::canShare() const { - if (!isAgentInventory()) return FALSE; + bool can_share = false; - const LLInventoryModel* model = getInventoryModel(); - if (!model) return FALSE; + if (isAgentInventory()) + { + const LLInventoryModel* model = getInventoryModel(); + if (model) + { + const LLViewerInventoryItem *item = model->getItem(mUUID); + if (item) + { + if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) + { + can_share = LLGiveInventory::isInventoryGiveAcceptable(item); + } + } + else + { + // Categories can be given. + can_share = (model->getCategory(mUUID) != NULL); + } + } + } - const LLViewerInventoryItem *item = model->getItem(mUUID); + return can_share; +} + +bool LLInvFVBridge::canListOnMarketplace() const +{ +#if ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU + + LLInventoryModel * model = getInventoryModel(); + + const LLViewerInventoryCategory * cat = model->getCategory(mUUID); + if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) + { + return false; + } + + if (!isAgentInventory()) + { + return false; + } + + if (getOutboxFolder().isNull()) + { + return false; + } + + if (isInboxFolder() || isOutboxFolder()) + { + return false; + } + + LLViewerInventoryItem * item = model->getItem(mUUID); if (item) { - if (!LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) - return FALSE; - return (BOOL)LLGiveInventory::isInventoryGiveAcceptable(item); + if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) + { + return false; + } + + if (LLAssetType::AT_CALLINGCARD == item->getType()) + { + return false; + } } - // Categories can be given. - if (model->getCategory(mUUID)) return TRUE; + return true; - return FALSE; +#else + return false; +#endif } +bool LLInvFVBridge::canListOnMarketplaceNow() const +{ +#if ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU + + bool can_list = true; + + // Do not allow listing while import is in progress + if (LLMarketplaceInventoryImporter::instanceExists()) + { + can_list = !LLMarketplaceInventoryImporter::instance().isImportInProgress(); + } + + const LLInventoryObject* obj = getInventoryObject(); + can_list &= (obj != NULL); + + if (can_list) + { + const LLUUID& object_id = obj->getLinkedUUID(); + can_list = object_id.notNull(); + + if (can_list) + { + LLFolderViewFolder * object_folderp = mRoot->getFolderByID(object_id); + if (object_folderp) + { + can_list = !object_folderp->isLoading(); + } + } + + if (can_list) + { + // Get outbox id + const LLUUID & outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + LLFolderViewItem * outbox_itemp = mRoot->getItemByID(outbox_id); + + if (outbox_itemp) + { + MASK mask = 0x0; + BOOL drop = FALSE; + EDragAndDropType cargo_type = LLViewerAssetType::lookupDragAndDropType(obj->getActualType()); + void * cargo_data = (void *) obj; + std::string tooltip_msg; + + can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + } + } + } + + return can_list; + +#else + return false; +#endif +} + + // +=================================================+ // | InventoryFVBridgeBuilder | // +=================================================+ @@ -1032,7 +1349,13 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) std::string buffer; asset_id.toString(buffer); - gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer)); + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer)); + return; + } + else if ("cut" == action) + { + cutToClipboard(); + LLFolderView::removeCutItems(); return; } else if ("copy" == action) @@ -1042,7 +1365,6 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) } else if ("paste" == action) { - // Single item only LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; @@ -1064,6 +1386,16 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) folder_view_itemp->getListener()->pasteLinkFromClipboard(); return; } + else if (isMarketplaceCopyAction(action)) + { + llinfos << "Copy item to marketplace action!" << llendl; + + LLInventoryItem* itemp = model->getItem(mUUID); + if (!itemp) return; + + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); + } } void LLItemBridge::selectItem() @@ -1071,7 +1403,8 @@ void LLItemBridge::selectItem() LLViewerInventoryItem* item = static_cast<LLViewerInventoryItem*>(getItem()); if(item && !item->isFinished()) { - item->fetchFromServer(); + //item->fetchFromServer(); + LLInventoryModelBackgroundFetch::instance().start(item->getUUID(), false); } } @@ -1204,7 +1537,7 @@ std::string LLItemBridge::getLabelSuffix() const { // String table is loaded before login screen and inventory items are // loaded after login, so LLTrans should be ready. - static std::string NO_COPY =LLTrans::getString("no_copy"); + static std::string NO_COPY = LLTrans::getString("no_copy"); static std::string NO_MOD = LLTrans::getString("no_modify"); static std::string NO_XFER = LLTrans::getString("no_transfer"); static std::string LINK = LLTrans::getString("link"); @@ -1213,16 +1546,17 @@ std::string LLItemBridge::getLabelSuffix() const LLInventoryItem* item = getItem(); if(item) { - // it's a bit confusing to put nocopy/nomod/etc on calling cards. + // Any type can have the link suffix... + BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType()); + if (broken_link) return BROKEN_LINK; + + BOOL link = item->getIsLinkType(); + if (link) return LINK; + + // ...but it's a bit confusing to put nocopy/nomod/etc suffixes on calling cards. if(LLAssetType::AT_CALLINGCARD != item->getType() && item->getPermissions().getOwner() == gAgent.getID()) { - BOOL broken_link = LLAssetType::lookupIsLinkType(item->getType()); - if (broken_link) return BROKEN_LINK; - - BOOL link = item->getIsLinkType(); - if (link) return LINK; - BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); if (!copy) { @@ -1272,6 +1606,11 @@ BOOL LLItemBridge::isItemRenameable() const return FALSE; } + if (isInboxFolder()) + { + return FALSE; + } + return (item->getPermissions().allowModifyBy(gAgent.getID())); } return FALSE; @@ -1395,21 +1734,7 @@ BOOL LLItemBridge::isItemCopyable() const return FALSE; } - // All items can be copied in god mode since you can - // at least paste-as-link the item, though you - // still may not be able paste the item. - return TRUE; - // return (item->getPermissions().allowCopyBy(gAgent.getID())); - } - return FALSE; -} - -BOOL LLItemBridge::copyToClipboard() const -{ - if(isItemCopyable()) - { - LLInventoryClipboard::instance().add(mUUID); - return TRUE; + return item->getPermissions().allowCopyBy(gAgent.getID()) || gSavedSettings.getBOOL("InventoryLinking"); } return FALSE; } @@ -1435,16 +1760,6 @@ BOOL LLItemBridge::isItemPermissive() const return FALSE; } -bool LLItemBridge::isAddAction(std::string action) const -{ - return ("wear" == action || "attach" == action || "activate" == action); -} - -bool LLItemBridge::isRemoveAction(std::string action) const -{ - return ("take_off" == action || "detach" == action || "deactivate" == action); -} - // +=================================================+ // | LLFolderBridge | // +=================================================+ @@ -1457,16 +1772,20 @@ BOOL LLFolderBridge::isItemMovable() const LLInventoryObject* obj = getInventoryObject(); if(obj) { - return (!LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType())); + // If it's a protected type folder, we can't move it + if (LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType())) + return FALSE; + return TRUE; } return FALSE; } void LLFolderBridge::selectItem() { + // Have no fear: the first thing start() does is to test if everything for that folder has been fetched... + LLInventoryModelBackgroundFetch::instance().start(getUUID(), true); } - // Iterate through a folder's children to determine if // all the children are removable. class LLIsItemRemovable : public LLFolderViewFunctor @@ -1492,7 +1811,7 @@ BOOL LLFolderBridge::isItemRemovable() const return FALSE; } - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); + LLInventoryPanel* panel = mInventoryPanel.get(); LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL); if (folderp) { @@ -1522,23 +1841,35 @@ BOOL LLFolderBridge::isUpToDate() const BOOL LLFolderBridge::isItemCopyable() const { - if (gSavedSettings.getBOOL("InventoryLinking")) - { - // Can copy folders to paste-as-link, but not for straight paste. - return TRUE; - } - return FALSE; + // Folders are copyable if items in them are, recursively, copyable. + + // Get the content of the folder + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(mUUID,cat_array,item_array); + + // Check the items + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + LLItemBridge item_br(mInventoryPanel.get(), mRoot, item->getUUID()); + if (!item_br.isItemCopyable()) + return FALSE; } -BOOL LLFolderBridge::copyToClipboard() const + // Check the folders + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { - if(isItemCopyable()) - { - LLInventoryClipboard::instance().add(mUUID); + LLViewerInventoryCategory* category = *iter; + LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, category->getUUID()); + if (!cat_br.isItemCopyable()) + return FALSE; + } + return TRUE; } - return FALSE; -} BOOL LLFolderBridge::isClipboardPasteable() const { @@ -1555,7 +1886,7 @@ BOOL LLFolderBridge::isClipboardPasteable() const } LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); const LLViewerInventoryCategory *current_cat = getCategory(); // Search for the direct descendent of current Friends subfolder among all pasted items, @@ -1593,7 +1924,7 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat ); const LLUUID ¤t_cat_id = current_cat->getUUID(); LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); S32 count = objects.count(); for(S32 i = 0; i < count; i++) { @@ -1626,8 +1957,88 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const } +static BOOL can_move_to_outbox(LLInventoryItem* inv_item, std::string& tooltip_msg) +{ + // Collapse links directly to items/folders + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); + if (linked_item != NULL) + { + inv_item = linked_item; + } + + bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + if (!allow_transfer) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer"); + return false; + } + +#if BLOCK_WORN_ITEMS_IN_OUTBOX + bool worn = get_is_item_worn(inv_item->getUUID()); + if (worn) + { + tooltip_msg = LLTrans::getString("TooltipOutboxWorn"); + return false; + } +#endif + + bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); + if (calling_card) + { + tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard"); + return false; + } + + return true; +} + + +int get_folder_levels(LLInventoryCategory* inv_cat) +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items); + + int max_child_levels = 0; + + for (S32 i=0; i < cats->count(); ++i) + { + LLInventoryCategory* category = cats->get(i); + max_child_levels = llmax(max_child_levels, get_folder_levels(category)); + } + + return 1 + max_child_levels; +} + +int get_folder_path_length(const LLUUID& ancestor_id, const LLUUID& descendant_id) +{ + int depth = 0; + + if (ancestor_id == descendant_id) return depth; + + const LLInventoryCategory* category = gInventory.getCategory(descendant_id); + + while(category) + { + LLUUID parent_id = category->getParentUUID(); + + if (parent_id.isNull()) break; + + depth++; + + if (parent_id == ancestor_id) return depth; + + category = gInventory.getCategory(parent_id); + } + + llwarns << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << llendl; + return -1; +} + BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, - BOOL drop) + BOOL drop, + std::string& tooltip_msg) { LLInventoryModel* model = getInventoryModel(); @@ -1637,22 +2048,31 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (!isAgentAvatarValid()) return FALSE; if (!isAgentInventory()) return FALSE; // cannot drag categories into library + LLInventoryPanel* destination_panel = mInventoryPanel.get(); + if (!destination_panel) return false; - // check to make sure source is agent inventory, and is represented there. - LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); - const BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL) - && (LLToolDragAndDrop::SOURCE_AGENT == source); + LLInventoryFilter* filter = destination_panel->getFilter(); + if (!filter) return false; + const LLUUID &cat_id = inv_cat->getUUID(); const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); + const BOOL move_is_from_outbox = model->isObjectDescendentOf(cat_id, outbox_id); + + // check to make sure source is agent inventory, and is represented there. + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + const BOOL is_agent_inventory = (model->getCategory(cat_id) != NULL) + && (LLToolDragAndDrop::SOURCE_AGENT == source); BOOL accept = FALSE; if (is_agent_inventory) { - const LLUUID &cat_id = inv_cat->getUUID(); const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); - + const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); const BOOL move_is_into_outfit = getCategory() && (getCategory()->getPreferredType() == LLFolderType::FT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); @@ -1662,26 +2082,50 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, // BOOL is_movable = TRUE; - if (LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + + if (is_movable && (mUUID == cat_id)) + { is_movable = FALSE; - if (move_is_into_outfit) + tooltip_msg = LLTrans::getString("TooltipDragOntoSelf"); + } + if (is_movable && (model->isObjectDescendentOf(mUUID, cat_id))) + { + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipDragOntoOwnChild"); + } + if (is_movable && LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + is_movable = FALSE; + // tooltip? + } + if (is_movable && move_is_into_outfit) + { is_movable = FALSE; - if (mUUID == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE)) + // tooltip? + } + if (is_movable && (mUUID == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE))) + { is_movable = FALSE; + // tooltip? + } + LLInventoryModel::cat_array_t descendent_categories; LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents(cat_id, descendent_categories, descendent_items, FALSE); - for (S32 i=0; i < descendent_categories.count(); ++i) + if (is_movable) { - LLInventoryCategory* category = descendent_categories[i]; - if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) + model->collectDescendents(cat_id, descendent_categories, descendent_items, FALSE); + for (S32 i=0; i < descendent_categories.count(); ++i) { - // Can't move "special folders" (e.g. Textures Folder). - is_movable = FALSE; - break; + LLInventoryCategory* category = descendent_categories[i]; + if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) + { + // Can't move "special folders" (e.g. Textures Folder). + is_movable = FALSE; + break; + } } } - if (move_is_into_trash) + if (is_movable && move_is_into_trash) { for (S32 i=0; i < descendent_items.count(); ++i) { @@ -1693,7 +2137,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } } - if (move_is_into_landmarks) + if (is_movable && move_is_into_landmarks) { for (S32 i=0; i < descendent_items.count(); ++i) { @@ -1708,14 +2152,133 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } } + if (is_movable && move_is_into_outbox) + { + const int nested_folder_levels = get_folder_path_length(outbox_id, mUUID) + get_folder_levels(inv_cat); + + if (nested_folder_levels > gSavedSettings.getU32("InventoryOutboxMaxFolderDepth")) + { + tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels"); + is_movable = FALSE; + } + else + { + int dragged_folder_count = descendent_categories.count(); + int existing_item_count = 0; + int existing_folder_count = 0; + + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(outbox_id, mUUID); + + if (master_folder != NULL) + { + if (model->isObjectDescendentOf(cat_id, master_folder->getUUID())) + { + // Don't use count because we're already inside the same category anyway + dragged_folder_count = 0; + } + else + { + existing_folder_count = 1; // Include the master folder in the count! + + // If we're in the drop operation as opposed to the drag without drop, we are doing a + // single category at a time so don't block based on the total amount of cargo data items + if (drop) + { + dragged_folder_count += 1; + } + else + { + // NOTE: The cargo id's count is a total of categories AND items but we err on the side of + // prevention rather than letting too many folders into the hierarchy of the outbox, + // when we're dragging the item to a new parent + dragged_folder_count += LLToolDragAndDrop::instance().getCargoCount(); + } + } + + // Tally the total number of categories and items inside the master folder + + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + + model->collectDescendents(master_folder->getUUID(), existing_categories, existing_items, FALSE); + + existing_folder_count += existing_categories.count(); + existing_item_count += existing_items.count(); + } + else + { + // Assume a single category is being dragged to the outbox since we evaluate one at a time + // when not putting them under a parent item. + dragged_folder_count += 1; + } + + const int nested_folder_count = existing_folder_count + dragged_folder_count; + const int nested_item_count = existing_item_count + descendent_items.count(); + + if (nested_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders"); + is_movable = FALSE; + } + else if (nested_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects"); + is_movable = FALSE; + } + + if (is_movable == TRUE) + { + for (S32 i=0; i < descendent_items.count(); ++i) + { + LLInventoryItem* item = descendent_items[i]; + if (!can_move_to_outbox(item, tooltip_msg)) + { + is_movable = FALSE; + break; + } + } + } + } + } + + if (is_movable) + { + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + is_movable = active_panel != NULL; + // For a folder to pass the filter all its descendants are required to pass. + // We make this exception to allow reordering folders within an inventory panel, + // which has a filter applied, like Recent tab for example. + // There may be folders which are displayed because some of their descendants pass + // the filter, but other don't, and thus remain hidden. Without this check, + // such folders would not be allowed to be moved within a panel. + if (destination_panel == active_panel) + { + is_movable = true; + } + else + { + LLFolderView* active_folder_view = NULL; + + if (is_movable) + { + active_folder_view = active_panel->getRootFolder(); + is_movable = active_folder_view != NULL; + } + + if (is_movable) + { + // Check whether the folder being dragged from active inventory panel + // passes the filter of the destination panel. + is_movable = check_category(model, cat_id, active_folder_view, filter); + } + } + } // //-------------------------------------------------------------------------------- - accept = is_movable - && (mUUID != cat_id) // Can't move a folder into itself - && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing - && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity + accept = is_movable; + if (accept && drop) { // Look for any gestures and deactivate them @@ -1747,7 +2310,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, // Recursively create links in target outfit. LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - gInventory.collectDescendents(inv_cat->getUUID(), cats, items, LLInventoryModel::EXCLUDE_TRASH); + model->collectDescendents(cat_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); LLAppearanceMgr::instance().linkAll(mUUID,items,NULL); } } @@ -1765,7 +2328,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, const std::string empty_description = ""; link_inventory_item( gAgent.getID(), - inv_cat->getUUID(), + cat_id, mUUID, inv_cat->getName(), empty_description, @@ -1775,8 +2338,16 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, #endif } } + else if (move_is_into_outbox && !move_is_from_outbox) + { + copy_folder_to_outbox(inv_cat, mUUID, cat_id, LLToolDragAndDrop::getOperationId()); + } else { + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + { + set_dad_inbox_object(cat_id); + } // Reparent the folder and restamp children if it's moving // into trash. @@ -1790,15 +2361,28 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else if (LLToolDragAndDrop::SOURCE_WORLD == source) { - // content category has same ID as object itself - LLUUID object_id = inv_cat->getUUID(); - LLUUID category_id = mUUID; - accept = move_inv_category_world_to_agent(object_id, category_id, drop); + if (move_is_into_outbox) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else + { + accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, NULL, NULL, filter); + } } else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) { - // Accept folders that contain complete outfits. - accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(inv_cat->getUUID()); + if (move_is_into_outbox) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else + { + // Accept folders that contain complete outfits. + accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(cat_id); + } if (accept && drop) { @@ -1829,7 +2413,8 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, const LLUUID& category_id, BOOL drop, void (*callback)(S32, void*), - void* user_data) + void* user_data, + LLInventoryFilter* filter) { // Make sure the object exists. If we allowed dragging from // anonymous objects, it would be possible to bypass @@ -1853,7 +2438,7 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, return FALSE; } - BOOL accept = TRUE; + BOOL accept = FALSE; BOOL is_move = FALSE; // coming from a task. Need to figure out if the person can @@ -1862,9 +2447,16 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, LLInventoryObject::object_list_t::iterator end = inventory_objects.end(); for ( ; it != end; ++it) { + LLInventoryItem* item = dynamic_cast<LLInventoryItem*>(it->get()); + if (!item) + { + llwarns << "Invalid inventory item for drop" << llendl; + continue; + } + // coming from a task. Need to figure out if the person can // move/copy this item. - LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions()); + LLPermissions perm(item->getPermissions()); if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) && perm.allowTransferTo(gAgent.getID()))) // || gAgent.isGodlike()) @@ -1879,9 +2471,14 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, is_move = TRUE; accept = TRUE; } - else + + if (filter && accept) + { + accept = filter->check(item); + } + + if (!accept) { - accept = FALSE; break; } } @@ -1917,122 +2514,114 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, return accept; } -//Used by LLFolderBridge as callback for directory recursion. -class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver +void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer) { -public: - LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) : - LLInventoryFetchItemsObserver(ids), - mCopyItems(false) - { }; - LLRightClickInventoryFetchObserver(const uuid_vec_t& ids, - const LLUUID& cat_id, - bool copy_items) : - LLInventoryFetchItemsObserver(ids), - mCatID(cat_id), - mCopyItems(copy_items) - { }; - virtual void done() - { - // we've downloaded all the items, so repaint the dialog - LLFolderBridge::staticFolderOptionsMenu(); - - gInventory.removeObserver(this); - delete this; - } - - -protected: - LLUUID mCatID; - bool mCopyItems; - -}; - -//Used by LLFolderBridge as callback for directory recursion. -class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver -{ -public: - LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids, - bool copy_items) : - LLInventoryFetchDescendentsObserver(ids), - mCopyItems(copy_items) - {} - ~LLRightClickInventoryFetchDescendentsObserver() {} - virtual void done(); -protected: - bool mCopyItems; -}; - -void LLRightClickInventoryFetchDescendentsObserver::done() -{ - // Avoid passing a NULL-ref as mCompleteFolders.front() down to - // gInventory.collectDescendents() + // Bail out immediately if no descendents if( mComplete.empty() ) { llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl; + if (clear_observer) + { dec_busy_count(); gInventory.removeObserver(this); delete this; + } return; } - // What we do here is get the complete information on the items in - // the library, and set up an observer that will wait for that to - // happen. - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - gInventory.collectDescendents(mComplete.front(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); - S32 count = item_array.count(); -#if 0 // HACK/TODO: Why? - // This early causes a giant menu to get produced, and doesn't seem to be needed. - if(!count) - { - llwarns << "Nothing fetched in category " << mCompleteFolders.front() - << llendl; + // Copy the list of complete fetched folders while "this" is still valid + uuid_vec_t completed_folder = mComplete; + + // Clean up, and remove this as an observer now since recursive calls + // could notify observers and throw us into an infinite loop. + if (clear_observer) + { dec_busy_count(); gInventory.removeObserver(this); delete this; - return; } -#endif - uuid_vec_t ids; - for(S32 i = 0; i < count; ++i) + for (uuid_vec_t::iterator current_folder = completed_folder.begin(); current_folder != completed_folder.end(); ++current_folder) { - ids.push_back(item_array.get(i)->getUUID()); - } + // Get the information on the fetched folder items and subfolders and fetch those + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array); - LLRightClickInventoryFetchObserver* outfit = new LLRightClickInventoryFetchObserver(ids, mComplete.front(), mCopyItems); + S32 item_count = item_array->count(); + S32 cat_count = cat_array->count(); + + // Move to next if current folder empty + if ((item_count == 0) && (cat_count == 0)) + { + continue; + } - // clean up, and remove this as an observer since the call to the - // outfit could notify observers and throw us into an infinite - // loop. - dec_busy_count(); - gInventory.removeObserver(this); - delete this; + uuid_vec_t ids; + LLRightClickInventoryFetchObserver* outfit = NULL; + LLRightClickInventoryFetchDescendentsObserver* categories = NULL; - // increment busy count and either tell the inventory to check & - // call done, or add this object to the inventory for observation. - inc_busy_count(); + // Fetch the items + if (item_count) + { + for (S32 i = 0; i < item_count; ++i) + { + ids.push_back(item_array->get(i)->getUUID()); + } + outfit = new LLRightClickInventoryFetchObserver(ids); + } + // Fetch the subfolders + if (cat_count) + { + for (S32 i = 0; i < cat_count; ++i) + { + ids.push_back(cat_array->get(i)->getUUID()); + } + categories = new LLRightClickInventoryFetchDescendentsObserver(ids); + } - // do the fetch + // Perform the item fetch + if (outfit) + { outfit->startFetch(); - outfit->done(); //Not interested in waiting and this will be right 99% of the time. + outfit->execute(); // Not interested in waiting and this will be right 99% of the time. + delete outfit; //Uncomment the following code for laggy Inventory UI. -/* if(outfit->isFinished()) + /* + if (outfit->isFinished()) { // everything is already here - call done. - outfit->done(); + outfit->execute(); + delete outfit; } else { - // it's all on it's way - add an observer, and the inventory + // it's all on its way - add an observer, and the inventory // will call done for us when everything is here. + inc_busy_count(); gInventory.addObserver(outfit); - }*/ + } + */ + } + // Perform the subfolders fetch : this is where we truly recurse down the folder hierarchy + if (categories) + { + categories->startFetch(); + if (categories->isFinished()) + { + // everything is already here - call done. + categories->execute(); + delete categories; + } + else + { + // it's all on its way - add an observer, and the inventory + // will call done for us when everything is here. + inc_busy_count(); + gInventory.addObserver(categories); + } + } + } } @@ -2045,15 +2634,15 @@ void LLRightClickInventoryFetchDescendentsObserver::done() class LLInventoryCopyAndWearObserver : public LLInventoryObserver { public: - LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) : - mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {} + LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count, bool folder_added=false) : + mCatID(cat_id), mContentsCount(count), mFolderAdded(folder_added) {} virtual ~LLInventoryCopyAndWearObserver() {} virtual void changed(U32 mask); protected: LLUUID mCatID; int mContentsCount; - BOOL mFolderAdded; + bool mFolderAdded; }; @@ -2081,7 +2670,6 @@ void LLInventoryCopyAndWearObserver::changed(U32 mask) if (mFolderAdded) { LLViewerInventoryCategory* category = gInventory.getCategory(mCatID); - if (NULL == category) { llwarns << "gInventory.getCategory(" << mCatID @@ -2152,6 +2740,12 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) modifyOutfit(TRUE); return; } + else if ("cut" == action) + { + cutToClipboard(); + LLFolderView::removeCutItems(); + return; + } else if ("copy" == action) { copyToClipboard(); @@ -2183,6 +2777,27 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) removeSystemFolder(); } #endif + else if (isMarketplaceCopyAction(action)) + { + llinfos << "Copy folder to marketplace action!" << llendl; + + LLInventoryCategory * cat = gInventory.getCategory(mUUID); + if (!cat) return; + + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); + } +#if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU + else if (isMarketplaceSendAction(action)) + { + llinfos << "Send to marketplace action!" << llendl; + + LLInventoryCategory * cat = gInventory.getCategory(mUUID); + if (!cat) return; + + send_to_marketplace(cat); + } +#endif // ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU } void LLFolderBridge::openItem() @@ -2265,6 +2880,9 @@ LLUIImagePtr LLFolderBridge::getIcon() const LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE)); + /*case LLAssetType::AT_MESH: + control = "inv_folder_mesh.tga"; + break;*/ } LLUIImagePtr LLFolderBridge::getOpenIcon() const @@ -2330,7 +2948,7 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re { // move it to the trash LLPreview::hide(mUUID); - remove_category(getInventoryModel(), mUUID); + getInventoryModel()->removeCategory(mUUID); return TRUE; } return FALSE; @@ -2341,22 +2959,89 @@ void LLFolderBridge::pasteFromClipboard() LLInventoryModel* model = getInventoryModel(); if(model && isClipboardPasteable()) { - const LLUUID parent_id(mUUID); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); + + if (move_is_into_outbox) + { + LLFolderViewItem * outbox_itemp = mRoot->getItemByID(mUUID); + + if (outbox_itemp) + { + LLToolDragAndDrop::instance().setCargoCount(objects.size()); + + BOOL can_list = TRUE; + + for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); + (iter != objects.end()) && (can_list == TRUE); + ++iter) + { + const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); + + if (item) + { + MASK mask = 0x0; + BOOL drop = FALSE; + EDragAndDropType cargo_type = LLViewerAssetType::lookupDragAndDropType(item->getActualType()); + void * cargo_data = (void *) item; + std::string tooltip_msg; + + can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + } + } + + LLToolDragAndDrop::instance().resetCargoCount(); + + if (can_list == FALSE) + { + // Notify user of failure somehow -- play error sound? modal dialog? + return; + } + } + } + + const LLUUID parent_id(mUUID); + for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { const LLUUID& item_id = (*iter); + LLInventoryItem *item = model->getItem(item_id); - if (item) + LLInventoryObject *obj = model->getObject(item_id); + if (obj) { - if(LLInventoryClipboard::instance().isCutMode()) + if (move_is_into_current_outfit || move_is_into_outfit) + { + if (can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit); + } + } + else if (LLClipboard::instance().isCutMode()) + { + // Do a move to "paste" a "cut" + // move_inventory_item() is not enough, as we have to update inventory locally too + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + llassert(vicat); + if (vicat) + { + changeCategoryParent(model, vicat, parent_id, FALSE); + } + } + else { - // move_inventory_item() is not enough, - //we have to update inventory locally too LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item); llassert(viitem); if (viitem) @@ -2364,6 +3049,19 @@ void LLFolderBridge::pasteFromClipboard() changeItemParent(model, viitem, parent_id, FALSE); } } + } + else + { + // Do a "copy" to "paste" a regular copy clipboard + if (LLAssetType::AT_CATEGORY == obj->getType()) + { + LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); + llassert(vicat); + if (vicat) + { + copy_inventory_category(model, vicat, parent_id); + } + } else { copy_inventory_item( @@ -2377,23 +3075,47 @@ void LLFolderBridge::pasteFromClipboard() } } } + // Change mode to paste for next paste + LLClipboard::instance().setCutMode(false); + } } void LLFolderBridge::pasteLinkFromClipboard() { - const LLInventoryModel* model = getInventoryModel(); + LLInventoryModel* model = getInventoryModel(); if(model) { + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); + const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + + const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); + const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); + + if (move_is_into_outbox) + { + // Notify user of failure somehow -- play error sound? modal dialog? + return; + } + const LLUUID parent_id(mUUID); LLDynamicArray<LLUUID> objects; - LLInventoryClipboard::instance().retrieve(objects); + LLClipboard::instance().pasteFromClipboard(objects); for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) { const LLUUID &object_id = (*iter); - if (LLInventoryCategory *cat = model->getCategory(object_id)) + if (move_is_into_current_outfit || move_is_into_outfit) + { + LLInventoryItem *item = model->getItem(object_id); + if (item && can_move_to_outfit(item, move_is_into_current_outfit)) + { + dropToOutfit(item, move_is_into_current_outfit); + } + } + else if (LLInventoryCategory *cat = model->getCategory(object_id)) { const std::string empty_description = ""; link_inventory_item( @@ -2417,104 +3139,18 @@ void LLFolderBridge::pasteLinkFromClipboard() LLPointer<LLInventoryCallback>(NULL)); } } + // Change mode to paste for next paste + LLClipboard::instance().setCutMode(false); } } void LLFolderBridge::staticFolderOptionsMenu() { LLFolderBridge* selfp = sSelf.get(); - if (selfp) - { - selfp->folderOptionsMenu(); - } -} -void LLFolderBridge::folderOptionsMenu() -{ - menuentry_vec_t disabled_items; - - LLInventoryModel* model = getInventoryModel(); - if(!model) return; - - const LLInventoryCategory* category = model->getCategory(mUUID); - if(!category) return; - - const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - if (trash_id == mUUID) return; - if (isItemInTrash()) return; - if (!isAgentInventory()) return; - - LLFolderType::EType type = category->getPreferredType(); - const bool is_system_folder = LLFolderType::lookupIsProtectedType(type); - // BAP change once we're no longer treating regular categories as ensembles. - const bool is_ensemble = (type == LLFolderType::FT_NONE || - LLFolderType::lookupIsEnsembleType(type)); - - // Only enable calling-card related options for non-system folders. - if (!is_system_folder) - { - LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); - if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) - { - mItems.push_back(std::string("Calling Card Separator")); - mItems.push_back(std::string("Conference Chat Folder")); - mItems.push_back(std::string("IM All Contacts In Folder")); - } - } - -#ifndef LL_RELEASE_FOR_DOWNLOAD - if (LLFolderType::lookupIsProtectedType(type)) - { - mItems.push_back(std::string("Delete System Folder")); - } -#endif - - // wearables related functionality for folders. - //is_wearable - LLFindWearables is_wearable; - LLIsType is_object( LLAssetType::AT_OBJECT ); - LLIsType is_gesture( LLAssetType::AT_GESTURE ); - - if (mWearables || - checkFolderForContentsOfType(model, is_wearable) || - checkFolderForContentsOfType(model, is_object) || - checkFolderForContentsOfType(model, is_gesture) ) - { - mItems.push_back(std::string("Folder Wearables Separator")); - - // Only enable add/replace outfit for non-system folders. - if (!is_system_folder) - { - // Adding an outfit onto another (versus replacing) doesn't make sense. - if (type != LLFolderType::FT_OUTFIT) - { - mItems.push_back(std::string("Add To Outfit")); - } - mItems.push_back(std::string("Replace Outfit")); - } - if (is_ensemble) - { - mItems.push_back(std::string("Wear As Ensemble")); - } - mItems.push_back(std::string("Remove From Outfit")); - if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) - { - disabled_items.push_back(std::string("Remove From Outfit")); - } - if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) - { - disabled_items.push_back(std::string("Replace Outfit")); - } - mItems.push_back(std::string("Outfit Separator")); - } - LLMenuGL* menup = dynamic_cast<LLMenuGL*>(mMenu.get()); - if (menup) + if (selfp && selfp->mRoot) { - hide_context_entries(*menup, mItems, disabled_items, TRUE); - - // Reposition the menu, in case we're adding items to an existing menu. - menup->needsArrange(); - menup->arrangeAndClear(); + selfp->mRoot->updateMenu(); } } @@ -2530,17 +3166,11 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv return ((item_array.count() > 0) ? TRUE : FALSE ); } -// Flags unused -void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) { - mItems.clear(); - mDisabledItems.clear(); - - lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; - -// menuentry_vec_t disabled_items; LLInventoryModel* model = getInventoryModel(); - if(!model) return; + llassert(model != NULL); + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); const LLUUID lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); @@ -2557,10 +3187,6 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) mDisabledItems.push_back(std::string("New Body Parts")); } - // clear out old menu and folder pointers - mMenu.markDead(); - sSelf.markDead(); - if(trash_id == mUUID) { // This is the trash. @@ -2572,22 +3198,31 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) mItems.clear(); // clear any items that used to exist addTrashContextMenuOptions(mItems, mDisabledItems); } + else if(isOutboxFolder()) + { + addOutboxContextMenuOptions(flags, mItems, mDisabledItems); + } else if(isAgentInventory()) // do not allow creating in library { - LLViewerInventoryCategory *cat = getCategory(); + LLViewerInventoryCategory *cat = getCategory(); // BAP removed protected check to re-enable standard ops in untyped folders. // Not sure what the right thing is to do here. if (!isCOFFolder() && cat && (cat->getPreferredType() != LLFolderType::FT_OUTFIT)) { - // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. - if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) - mItems.push_back(std::string("New Folder")); - mItems.push_back(std::string("New Script")); - mItems.push_back(std::string("New Note")); - mItems.push_back(std::string("New Gesture")); - mItems.push_back(std::string("New Clothes")); - mItems.push_back(std::string("New Body Parts")); + if (!isInboxFolder() && !isOutboxFolder()) // don't allow creation in inbox or outbox + { + // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. + if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) + { + mItems.push_back(std::string("New Folder")); + } + mItems.push_back(std::string("New Script")); + mItems.push_back(std::string("New Note")); + mItems.push_back(std::string("New Gesture")); + mItems.push_back(std::string("New Clothes")); + mItems.push_back(std::string("New Body Parts")); + } #if SUPPORT_ENSEMBLES // Changing folder types is an unfinished unsupported feature // and can lead to unexpected behavior if enabled. @@ -2644,39 +3279,149 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) mDisabledItems.push_back(std::string("Delete System Folder")); } - mItems.push_back(std::string("Share")); - if (!canShare()) + if (!isOutboxFolder()) { - mDisabledItems.push_back(std::string("Share")); + mItems.push_back(std::string("Share")); + if (!canShare()) + { + mDisabledItems.push_back(std::string("Share")); + } } +} - hide_context_entries(menu, mItems, mDisabledItems); +void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) +{ + // Build folder specific options back up + LLInventoryModel* model = getInventoryModel(); + if(!model) return; - // Add menu items that are dependent on the contents of the folder. - uuid_vec_t folders; - LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID); - if (category) + const LLInventoryCategory* category = model->getCategory(mUUID); + if(!category) return; + + const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (trash_id == mUUID) return; + if (isItemInTrash()) return; + if (!isAgentInventory()) return; + if (isOutboxFolder()) return; + + LLFolderType::EType type = category->getPreferredType(); + const bool is_system_folder = LLFolderType::lookupIsProtectedType(type); + // BAP change once we're no longer treating regular categories as ensembles. + const bool is_ensemble = (type == LLFolderType::FT_NONE || + LLFolderType::lookupIsEnsembleType(type)); + + // Only enable calling-card related options for non-system folders. + if (!is_system_folder) { - folders.push_back(category->getUUID()); + LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); + if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) + { + mItems.push_back(std::string("Calling Card Separator")); + mItems.push_back(std::string("Conference Chat Folder")); + mItems.push_back(std::string("IM All Contacts In Folder")); + } } - mMenu = menu.getHandle(); - sSelf = getHandle(); - LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders, FALSE); - fetch->startFetch(); - inc_busy_count(); - if(fetch->isFinished()) + if (!isItemRemovable()) { - // everything is already here - call done. - fetch->done(); + mDisabledItems.push_back(std::string("Delete")); } - else + +#ifndef LL_RELEASE_FOR_DOWNLOAD + if (LLFolderType::lookupIsProtectedType(type)) + { + mItems.push_back(std::string("Delete System Folder")); + } +#endif + + // wearables related functionality for folders. + //is_wearable + LLFindWearables is_wearable; + LLIsType is_object( LLAssetType::AT_OBJECT ); + LLIsType is_gesture( LLAssetType::AT_GESTURE ); + + if (mWearables || + checkFolderForContentsOfType(model, is_wearable) || + checkFolderForContentsOfType(model, is_object) || + checkFolderForContentsOfType(model, is_gesture) ) { - // it's all on its way - add an observer, and the inventory will call done for us when everything is here. - gInventory.addObserver(fetch); + mItems.push_back(std::string("Folder Wearables Separator")); + + // Only enable add/replace outfit for non-system folders. + if (!is_system_folder) + { + // Adding an outfit onto another (versus replacing) doesn't make sense. + if (type != LLFolderType::FT_OUTFIT) + { + mItems.push_back(std::string("Add To Outfit")); + } + + mItems.push_back(std::string("Replace Outfit")); + } + if (is_ensemble) + { + mItems.push_back(std::string("Wear As Ensemble")); + } + mItems.push_back(std::string("Remove From Outfit")); + if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) + { + mDisabledItems.push_back(std::string("Remove From Outfit")); + } + if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) + { + mDisabledItems.push_back(std::string("Replace Outfit")); + } + mItems.push_back(std::string("Outfit Separator")); } } +// Flags unused +void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + sSelf.markDead(); + + mItems.clear(); + mDisabledItems.clear(); + + lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; + + LLInventoryModel* model = getInventoryModel(); + if(!model) return; + + buildContextMenuBaseOptions(flags); + + // Add menu items that are dependent on the contents of the folder. + LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); + if (category) + { + uuid_vec_t folders; + folders.push_back(category->getUUID()); + + sSelf = getHandle(); + LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); + fetch->startFetch(); + if (fetch->isFinished()) + { + // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down + // This saves lots of time as buildContextMenu() is called a lot + delete fetch; + buildContextMenuFolderOptions(flags); + } + else + { + // it's all on its way - add an observer, and the inventory will call done for us when everything is here. + inc_busy_count(); + gInventory.addObserver(fetch); + } + } + + hide_context_entries(menu, mItems, mDisabledItems); + + // Reposition the menu, in case we're adding items to an existing menu. + menu.needsArrange(); + menu.arrangeAndClear(); +} + BOOL LLFolderBridge::hasChildren() const { LLInventoryModel* model = getInventoryModel(); @@ -2688,7 +3433,8 @@ BOOL LLFolderBridge::hasChildren() const BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, - void* cargo_data) + void* cargo_data, + std::string& tooltip_msg) { LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; @@ -2701,13 +3447,14 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, case DAD_CALLINGCARD: case DAD_LANDMARK: case DAD_SCRIPT: + case DAD_CLOTHING: case DAD_OBJECT: case DAD_NOTECARD: - case DAD_CLOTHING: case DAD_BODYPART: case DAD_ANIMATION: case DAD_GESTURE: - accept = dragItemIntoFolder(inv_item, drop); + case DAD_MESH: + accept = dragItemIntoFolder(inv_item, drop, tooltip_msg); break; case DAD_LINK: // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. @@ -2718,12 +3465,12 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); if (linked_category) { - accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop); + accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg); } } else { - accept = dragItemIntoFolder(inv_item, drop); + accept = dragItemIntoFolder(inv_item, drop, tooltip_msg); } break; case DAD_CATEGORY: @@ -2733,10 +3480,14 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, } else { - accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop); + accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg); } break; + case DAD_ROOT_CATEGORY: + case DAD_NONE: + break; default: + llwarns << "Unhandled cargo type for drag&drop " << cargo_type << llendl; break; } return accept; @@ -2765,7 +3516,7 @@ void LLFolderBridge::createNewCategory(void* user_data) { LLFolderBridge* bridge = (LLFolderBridge*)user_data; if(!bridge) return; - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(bridge->mInventoryPanel.get()); + LLInventoryPanel* panel = bridge->mInventoryPanel.get(); if (!panel) return; LLInventoryModel* model = panel->getModel(); if(!model) return; @@ -2872,13 +3623,13 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response if(option == 0 && object) { - if (cat_and_wear && cat_and_wear->mWear) + if (cat_and_wear && cat_and_wear->mWear) // && !cat_and_wear->mFolderResponded) { LLInventoryObject::object_list_t inventory_objects; object->getInventoryContents(inventory_objects); int contents_count = inventory_objects.size()-1; //subtract one for containing folder - - LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count); + LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count, cat_and_wear->mFolderResponded); + gInventory.addObserver(inventoryObserver); } @@ -2945,7 +3696,7 @@ void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) // use callback to rearrange favorite landmarks after adding // to have new one placed before target (on which it was dropped). See EXT-4312. LLPointer<AddFavoriteLandmarkCallback> cb = new AddFavoriteLandmarkCallback(); - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); + LLInventoryPanel* panel = mInventoryPanel.get(); LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; if (drag_over_item && drag_over_item->getListener()) { @@ -2986,22 +3737,32 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c // into the folder, as well as performing the actual drop, depending // if drop == TRUE. BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, - BOOL drop) + BOOL drop, + std::string& tooltip_msg) { LLInventoryModel* model = getInventoryModel(); - if(!model || !inv_item) return FALSE; - if(!isAgentInventory()) return FALSE; // cannot drag into library + if (!model || !inv_item) return FALSE; + if (!isAgentInventory()) return FALSE; // cannot drag into library if (!isAgentAvatarValid()) return FALSE; + LLInventoryPanel* destination_panel = mInventoryPanel.get(); + if (!destination_panel) return false; + + LLInventoryFilter* filter = destination_panel->getFilter(); + if (!filter) return false; + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); + const LLUUID &outbox_id = model->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_favorites = (mUUID == favorites_id); const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); + const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id); + const BOOL move_is_from_outbox = model->isObjectDescendentOf(inv_item->getUUID(), outbox_id); LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); BOOL accept = FALSE; @@ -3056,10 +3817,14 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, accept = TRUE; if (!is_movable) + { accept = FALSE; - if ((mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) + } + else if ((mUUID == inv_item->getParentUUID()) && !move_is_into_favorites) + { accept = FALSE; - if (move_is_into_current_outfit || move_is_into_outfit) + } + else if (move_is_into_current_outfit || move_is_into_outfit) { accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); } @@ -3067,8 +3832,50 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = can_move_to_landmarks(inv_item); } + else if (move_is_into_outbox) + { + accept = can_move_to_outbox(inv_item, tooltip_msg); + + if (accept) + { + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(outbox_id, mUUID); + + int existing_item_count = LLToolDragAndDrop::instance().getCargoCount(); + + if (master_folder != NULL) + { + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + + gInventory.collectDescendents(master_folder->getUUID(), existing_categories, existing_items, FALSE); + + existing_item_count += existing_items.count(); + } + + if (existing_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects"); + accept = FALSE; + } + } + } + + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + + // Check whether the item being dragged from active inventory panel + // passes the filter of the destination panel. + if (accept && active_panel) + { + LLFolderView* active_folder_view = active_panel->getRootFolder(); + if (!active_folder_view) return false; + + LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + if (!fv_item) return false; + + accept = filter->check(fv_item); + } - if(accept && drop) + if (accept && drop) { if (inv_item->getType() == LLAssetType::AT_GESTURE && LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash) @@ -3077,15 +3884,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } // If an item is being dragged between windows, unselect everything in the active window // so that we don't follow the selection to its new location (which is very annoying). - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); - if (active_panel) - { - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); - if (active_panel && (panel != active_panel)) + if (active_panel && (destination_panel != active_panel)) { active_panel->unSelectAll(); } - } //-------------------------------------------------------------------------------- // Destination folder logic @@ -3095,8 +3897,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // (only reorder the item in Favorites folder) if ((mUUID == inv_item->getParentUUID()) && move_is_into_favorites) { - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); - LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; + LLFolderViewItem* itemp = destination_panel->getRootFolder()->getDraggingOverItem(); if (itemp) { LLUUID srcItemId = inv_item->getUUID(); @@ -3117,10 +3918,27 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { dropToOutfit(inv_item, move_is_into_current_outfit); } + else if (move_is_into_outbox) + { + if (move_is_from_outbox) + { + move_item_within_outbox(inv_item, mUUID, LLToolDragAndDrop::getOperationId()); + } + else + { + copy_item_to_outbox(inv_item, mUUID, LLUUID::null, LLToolDragAndDrop::getOperationId()); + } + } // NORMAL or TRASH folder // (move the item, restamp if into trash) else { + // set up observer to select item once drag and drop from inbox is complete + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + { + set_dad_inbox_object(inv_item->getUUID()); + } + LLInvFVBridge::changeItemParent( model, (LLViewerInventoryItem*)inv_item, @@ -3130,7 +3948,6 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // //-------------------------------------------------------------------------------- - } } else if (LLToolDragAndDrop::SOURCE_WORLD == source) @@ -3139,7 +3956,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // anonymous objects, it would be possible to bypass // permissions. object = gObjectList.findObject(inv_item->getParentUUID()); - if(!object) + if (!object) { llinfos << "Object not found for drop." << llendl; return FALSE; @@ -3149,10 +3966,9 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // move/copy this item. LLPermissions perm(inv_item->getPermissions()); BOOL is_move = FALSE; - if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) + if ((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) && perm.allowTransferTo(gAgent.getID()))) // || gAgent.isGodlike()) - { accept = TRUE; } @@ -3168,7 +3984,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // Don't allow placing an original item into Current Outfit or an outfit folder // because they must contain only links to wearable items. // *TODO: Probably we should create a link to an item if it was dragged to outfit or COF. - if(move_is_into_current_outfit || move_is_into_outfit) + if (move_is_into_current_outfit || move_is_into_outfit) { accept = FALSE; } @@ -3179,8 +3995,20 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = FALSE; } + else if (move_is_into_outbox) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + + // Check whether the item being dragged from in world + // passes the filter of the destination panel. + if (accept) + { + accept = filter->check(inv_item); + } - if(drop && accept) + if (accept && drop) { LLMoveInv* move_inv = new LLMoveInv; move_inv->mObjectID = inv_item->getParentUUID(); @@ -3202,15 +4030,34 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLNotifications::instance().forceResponse(params, 0); } } - } else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) { - accept = TRUE; - if(drop) + if (move_is_into_outbox) { - copy_inventory_from_notecard(LLToolDragAndDrop::getInstance()->getObjectID(), - LLToolDragAndDrop::getInstance()->getSourceID(), inv_item); + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else + { + // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder + // because they must contain only links to wearable items. + accept = !(move_is_into_current_outfit || move_is_into_outfit); + } + + // Check whether the item being dragged from notecard + // passes the filter of the destination panel. + if (accept) + { + accept = filter->check(inv_item); + } + + if (accept && drop) + { + copy_inventory_from_notecard(mUUID, // Drop to the chosen destination folder + LLToolDragAndDrop::getInstance()->getObjectID(), + LLToolDragAndDrop::getInstance()->getSourceID(), + inv_item); } } else if(LLToolDragAndDrop::SOURCE_LIBRARY == source) @@ -3220,7 +4067,12 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, { accept = TRUE; - if (move_is_into_current_outfit || move_is_into_outfit) + if (move_is_into_outbox) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else if (move_is_into_current_outfit || move_is_into_outfit) { accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); } @@ -3231,6 +4083,21 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, accept = can_move_to_landmarks(inv_item); } + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + + // Check whether the item being dragged from the library + // passes the filter of the destination panel. + if (accept && active_panel) + { + LLFolderView* active_folder_view = active_panel->getRootFolder(); + if (!active_folder_view) return false; + + LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + if (!fv_item) return false; + + accept = filter->check(fv_item); + } + if (accept && drop) { // FAVORITES folder @@ -3265,6 +4132,69 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, return accept; } +// static +bool check_category(LLInventoryModel* model, + const LLUUID& cat_id, + LLFolderView* active_folder_view, + LLInventoryFilter* filter) +{ + if (!model || !active_folder_view || !filter) + return false; + + if (!filter->checkFolder(cat_id)) + { + return false; + } + + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + model->collectDescendents(cat_id, descendent_categories, descendent_items, TRUE); + + S32 num_descendent_categories = descendent_categories.count(); + S32 num_descendent_items = descendent_items.count(); + + if (num_descendent_categories + num_descendent_items == 0) + { + // Empty folder should be checked as any other folder view item. + // If we are filtering by date the folder should not pass because + // it doesn't have its own creation date. See LLInvFVBridge::getCreationDate(). + return check_item(cat_id, active_folder_view, filter); + } + + for (S32 i = 0; i < num_descendent_categories; ++i) + { + LLInventoryCategory* category = descendent_categories[i]; + if(!check_category(model, category->getUUID(), active_folder_view, filter)) + { + return false; + } + } + + for (S32 i = 0; i < num_descendent_items; ++i) + { + LLViewerInventoryItem* item = descendent_items[i]; + if(!check_item(item->getUUID(), active_folder_view, filter)) + { + return false; + } + } + + return true; +} + +// static +bool check_item(const LLUUID& item_id, + LLFolderView* active_folder_view, + LLInventoryFilter* filter) +{ + if (!active_folder_view || !filter) return false; + + LLFolderViewItem* fv_item = active_folder_view->getItemByID(item_id); + if (!fv_item) return false; + + return filter->check(fv_item); +} + // +=================================================+ // | LLTextureBridge | // +=================================================+ @@ -3308,6 +4238,10 @@ void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); + } + else if(isOutboxFolder()) + { + addOutboxContextMenuOptions(flags, items, disabled_items); } else { @@ -3381,26 +4315,33 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t items; menuentry_vec_t disabled_items; - if(isItemInTrash()) + if (isOutboxFolder()) { - addTrashContextMenuOptions(items, disabled_items); + addOutboxContextMenuOptions(flags, items, disabled_items); } else { - items.push_back(std::string("Share")); - if (!canShare()) + if (isItemInTrash()) { - disabled_items.push_back(std::string("Share")); + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + items.push_back(std::string("Sound Open")); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); } - items.push_back(std::string("Sound Open")); - items.push_back(std::string("Properties")); - getClipboardEntries(true, items, disabled_items, flags); + items.push_back(std::string("Sound Separator")); + items.push_back(std::string("Sound Play")); } - items.push_back(std::string("Sound Separator")); - items.push_back(std::string("Sound Play")); - hide_context_entries(menu, items, disabled_items); } @@ -3432,26 +4373,33 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items; lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl; - if(isItemInTrash()) + if(isOutboxFolder()) { - addTrashContextMenuOptions(items, disabled_items); + addOutboxContextMenuOptions(flags, items, disabled_items); } else { - items.push_back(std::string("Share")); - if (!canShare()) + if(isItemInTrash()) { - disabled_items.push_back(std::string("Share")); + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + items.push_back(std::string("Landmark Open")); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); } - items.push_back(std::string("Landmark Open")); - items.push_back(std::string("Properties")); - getClipboardEntries(true, items, disabled_items, flags); + items.push_back(std::string("Landmark Separator")); + items.push_back(std::string("About Landmark")); } - items.push_back(std::string("Landmark Separator")); - items.push_back(std::string("About Landmark")); - // Disable "About Landmark" menu item for // multiple landmarks selected. Only one landmark // info panel can be shown at a time. @@ -3497,7 +4445,7 @@ void LLLandmarkBridge::performAction(LLInventoryModel* model, std::string action key["type"] = "landmark"; key["id"] = item->getUUID(); - LLSideTray::getInstance()->showPanel("panel_places", key); + LLFloaterSidePanelContainer::showPanel("places", key); } } else @@ -3569,7 +4517,7 @@ LLCallingCardBridge::~LLCallingCardBridge() void LLCallingCardBridge::refreshFolderViewItem() { - LLInventoryPanel* panel = dynamic_cast<LLInventoryPanel*>(mInventoryPanel.get()); + LLInventoryPanel* panel = mInventoryPanel.get(); LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL; if (itemp) { @@ -3664,6 +4612,10 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); + } + else if(isOutboxFolder()) + { + items.push_back(std::string("Delete")); } else { @@ -3706,7 +4658,8 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, - void* cargo_data) + void* cargo_data, + std::string& tooltip_msg) { LLViewerInventoryItem* item = getItem(); BOOL rv = FALSE; @@ -3725,6 +4678,7 @@ BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop, case DAD_BODYPART: case DAD_ANIMATION: case DAD_GESTURE: + case DAD_MESH: { LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; const LLPermissions& perm = inv_item->getPermissions(); @@ -3920,6 +4874,10 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } + else if(isOutboxFolder()) + { + items.push_back(std::string("Delete")); + } else { items.push_back(std::string("Share")); @@ -3970,29 +4928,35 @@ void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items; lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl; - if(isItemInTrash()) + if(isOutboxFolder()) { - addTrashContextMenuOptions(items, disabled_items); + items.push_back(std::string("Delete")); } else { - items.push_back(std::string("Share")); - if (!canShare()) + if(isItemInTrash()) { - disabled_items.push_back(std::string("Share")); + addTrashContextMenuOptions(items, disabled_items); + } + else + { + items.push_back(std::string("Share")); + if (!canShare()) + { + disabled_items.push_back(std::string("Share")); + } + items.push_back(std::string("Animation Open")); + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); } - items.push_back(std::string("Animation Open")); - items.push_back(std::string("Properties")); - getClipboardEntries(true, items, disabled_items, flags); + items.push_back(std::string("Animation Separator")); + items.push_back(std::string("Animation Play")); + items.push_back(std::string("Animation Audition")); } - items.push_back(std::string("Animation Separator")); - items.push_back(std::string("Animation Play")); - items.push_back(std::string("Animation Audition")); - hide_context_entries(menu, items, disabled_items); - } // virtual @@ -4002,14 +4966,14 @@ void LLAnimationBridge::performAction(LLInventoryModel* model, std::string actio { if (getItem()) { - LLPreviewAnim::e_activation_type activate = LLPreviewAnim::NONE; - if ("playworld" == action) activate = LLPreviewAnim::PLAY; - if ("playlocal" == action) activate = LLPreviewAnim::AUDITION; + LLSD::String activate = "NONE"; + if ("playworld" == action) activate = "Inworld"; + if ("playlocal" == action) activate = "Locally"; LLPreviewAnim* preview = LLFloaterReg::showTypedInstance<LLPreviewAnim>("preview_anim", LLSD(mUUID)); if (preview) { - preview->activate(activate); + preview->play(activate); } } } @@ -4246,6 +5210,10 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) if(isItemInTrash()) { addTrashContextMenuOptions(items, disabled_items); + } + else if(isOutboxFolder()) + { + items.push_back(std::string("Delete")); } else { @@ -4405,7 +5373,7 @@ void remove_inventory_category_from_avatar( LLInventoryCategory* category ) if (gAgentCamera.cameraCustomizeAvatar()) { // switching to outfit editor should automagically save any currently edited wearable - LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_outfit")); + LLFloaterSidePanelContainer::showPanel("appearance", LLSD().with("type", "edit_outfit")); } remove_inventory_category_from_avatar_step2(TRUE, category->getUUID() ); @@ -4579,6 +5547,10 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { addTrashContextMenuOptions(items, disabled_items); } + else if(isOutboxFolder()) + { + items.push_back(std::string("Delete")); + } else { // FWIW, it looks like SUPPRESS_OPEN_ITEM is not set anywhere BOOL can_open = ((flags & SUPPRESS_OPEN_ITEM) != SUPPRESS_OPEN_ITEM); @@ -4647,10 +5619,18 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else { items.push_back(std::string("Wearable And Object Wear")); - items.push_back(std::string("Wearable Add")); disabled_items.push_back(std::string("Take Off")); disabled_items.push_back(std::string("Wearable Edit")); } + + if (LLWearableType::getAllowMultiwear(mWearableType)) + { + items.push_back(std::string("Wearable Add")); + if (gAgentWearables.getWearableCount(mWearableType) >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) + { + disabled_items.push_back(std::string("Wearable Add")); + } + } break; default: break; @@ -4844,31 +5824,22 @@ void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable, // static void LLWearableBridge::removeAllClothesFromAvatar() { - // Remove COF links. - for (S32 itype = LLWearableType::WT_SHAPE; itype < LLWearableType::WT_COUNT; ++itype) - { - if (itype == LLWearableType::WT_SHAPE || itype == LLWearableType::WT_SKIN || itype == LLWearableType::WT_HAIR || itype == LLWearableType::WT_EYES) - continue; + // Fetch worn clothes (i.e. the ones in COF). + LLInventoryModel::item_array_t clothing_items; + LLInventoryModel::cat_array_t dummy; + LLIsType is_clothing(LLAssetType::AT_CLOTHING); + gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(), + dummy, + clothing_items, + LLInventoryModel::EXCLUDE_TRASH, + is_clothing, + false); - for (S32 index = gAgentWearables.getWearableCount(itype)-1; index >= 0 ; --index) - { - LLViewerInventoryItem *item = dynamic_cast<LLViewerInventoryItem*>( - gAgentWearables.getWearableInventoryItem((LLWearableType::EType)itype, index)); - if (!item) - continue; - const LLUUID &item_id = item->getUUID(); - const LLWearable *wearable = gAgentWearables.getWearableFromItemID(item_id); - if (!wearable) - continue; - - // Find and remove this item from the COF. - LLAppearanceMgr::instance().removeCOFItemLinks(item_id,false); - } + // Take them off by removing from COF. + for (LLInventoryModel::item_array_t::const_iterator it = clothing_items.begin(); it != clothing_items.end(); ++it) + { + LLAppearanceMgr::instance().removeItemFromAvatar((*it)->getUUID()); } - gInventory.notifyObservers(); - - // Remove wearables from gAgentWearables - LLAgentWearables::userRemoveAllClothes(); } // static @@ -4893,6 +5864,7 @@ void LLWearableBridge::removeFromAvatar() } } + // +=================================================+ // | LLLinkItemBridge | // +=================================================+ @@ -4923,6 +5895,66 @@ void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags) } // +=================================================+ +// | LLMeshBridge | +// +=================================================+ + +LLUIImagePtr LLMeshBridge::getIcon() const +{ + return LLInventoryIcon::getIcon(LLAssetType::AT_MESH, LLInventoryType::IT_MESH, 0, FALSE); +} + +void LLMeshBridge::openItem() +{ + LLViewerInventoryItem* item = getItem(); + + if (item) + { + // open mesh + } +} + +void LLMeshBridge::previewItem() +{ + LLViewerInventoryItem* item = getItem(); + if(item) + { + // preview mesh + } +} + + +void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLMeshBridge::buildContextMenu()" << llendl; + std::vector<std::string> items; + std::vector<std::string> disabled_items; + + if(isItemInTrash()) + { + 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 if(isOutboxFolder()) + { + addOutboxContextMenuOptions(flags, items, disabled_items); + } + else + { + items.push_back(std::string("Properties")); + + getClipboardEntries(true, items, disabled_items, flags); + } + + hide_context_entries(menu, items, disabled_items); +} + + +// +=================================================+ // | LLLinkBridge | // +=================================================+ // For broken folder links. @@ -5228,6 +6260,7 @@ public: { wearOnAvatar(); } + virtual ~LLWearableBridgeAction(){} protected: LLWearableBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} @@ -5320,11 +6353,6 @@ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menuentry_vec_t disabled_items, items = getMenuItems(); - items.erase(std::remove(items.begin(), items.end(), std::string("New Body Parts")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Clothes")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Note")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Gesture")), items.end()); - items.erase(std::remove(items.begin(), items.end(), std::string("New Script")), items.end()); items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); hide_context_entries(menu, items, disabled_items); |