From 2b31dad40026d8078ea30d0da0656a4078d0f5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Fri, 9 Feb 2024 22:26:02 +0100 Subject: miscellaneous: BOOL (int) to real bool --- indra/newview/llinventoryfunctions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 4aeacae6ed..7056eeb496 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -2066,7 +2066,7 @@ void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) LLPointer new_item = new LLViewerInventoryItem(inv_item); new_item->setParent(new_parent_id); - new_item->updateParentOnServer(FALSE); + new_item->updateParentOnServer(false); gInventory.updateItem(new_item); gInventory.notifyObservers(); } -- cgit v1.2.3 From a657b9b52d2818e2ba5b36ab3c58d0025dcb08e3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 17 Feb 2024 01:28:42 +0200 Subject: jira-archive-internal#71115 Add Images to Objects in Bulk SL-20725 --- indra/newview/llinventoryfunctions.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 7056eeb496..0d393952c7 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -3264,6 +3264,23 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root ungroup_folder_items(*ids.begin()); } } + else if ("thumbnail" == action) + { + if (selected_items.size() > 0) + { + LLSD data; + std::set::iterator set_iter; + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = *set_iter; + if (!folder_item) continue; + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); + if (!bridge) continue; + data.append(bridge->getUUID()); + } + LLFloaterReg::showInstance("change_item_thumbnail", data); + } + } else { std::set::iterator set_iter; -- cgit v1.2.3 From a5261a5fa8fad810ecb5c260d92c3e771822bf58 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Tue, 20 Feb 2024 23:46:23 +0100 Subject: Convert BOOL to bool in llui --- indra/newview/llinventoryfunctions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 0d393952c7..712f8dbb78 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -2788,7 +2788,7 @@ bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem (item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID. } -void LLSaveFolderState::setApply(BOOL apply) +void LLSaveFolderState::setApply(bool apply) { mApply = apply; // before generating new list of open folders, clear the old one -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/llinventoryfunctions.cpp | 166 ++++++++++++++++----------------- 1 file changed, 83 insertions(+), 83 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 712f8dbb78..cbf6b8b59e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -93,7 +93,7 @@ #include -BOOL LLInventoryState::sWearNewClothing = FALSE; +bool LLInventoryState::sWearNewClothing = false; LLUUID LLInventoryState::sWearNewClothingTransactionID; std::list LLInventoryAction::sMarketplaceFolders; bool LLInventoryAction::sDeleteConfirmationDisplayed = false; @@ -536,12 +536,12 @@ public: } }; -BOOL get_is_parent_to_worn_item(const LLUUID& id) +bool get_is_parent_to_worn_item(const LLUUID& id) { const LLViewerInventoryCategory* cat = gInventory.getCategory(id); if (!cat) { - return FALSE; + return false; } LLInventoryModel::cat_array_t cats; @@ -568,7 +568,7 @@ BOOL get_is_parent_to_worn_item(const LLUUID& id) if (cat == parent_cat) { - return TRUE; + return true; } parent_id = parent_cat->getParentUUID(); @@ -576,24 +576,24 @@ BOOL get_is_parent_to_worn_item(const LLUUID& id) } } - return FALSE; + return false; } -BOOL get_is_item_worn(const LLUUID& id) +bool get_is_item_worn(const LLUUID& id) { const LLViewerInventoryItem* item = gInventory.getItem(id); if (!item) - return FALSE; + return false; if (item->getIsLinkType() && !gInventory.getItem(item->getLinkedUUID())) { - return FALSE; + return false; } // Consider the item as worn if it has links in COF. if (LLAppearanceMgr::instance().isLinkedInCOF(id)) { - return TRUE; + return true; } switch(item->getType()) @@ -601,40 +601,40 @@ BOOL get_is_item_worn(const LLUUID& id) case LLAssetType::AT_OBJECT: { if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) - return TRUE; + return true; break; } case LLAssetType::AT_BODYPART: case LLAssetType::AT_CLOTHING: if(gAgentWearables.isWearingItem(item->getLinkedUUID())) - return TRUE; + return true; break; case LLAssetType::AT_GESTURE: if (LLGestureMgr::instance().isGestureActive(item->getLinkedUUID())) - return TRUE; + return true; break; default: break; } - return FALSE; + return false; } -BOOL get_can_item_be_worn(const LLUUID& id) +bool get_can_item_be_worn(const LLUUID& id) { const LLViewerInventoryItem* item = gInventory.getItem(id); if (!item) - return FALSE; + return false; if (LLAppearanceMgr::instance().isLinkedInCOF(item->getLinkedUUID())) { // an item having links in COF (i.e. a worn item) - return FALSE; + return false; } if (gInventory.isObjectDescendentOf(id, LLAppearanceMgr::instance().getCOF())) { // a non-link object in COF (should not normally happen) - return FALSE; + return false; } const LLUUID trash_id = gInventory.findCategoryUUIDForType( @@ -654,12 +654,12 @@ BOOL get_can_item_be_worn(const LLUUID& id) if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) { // Already being worn - return FALSE; + return false; } else { // Not being worn yet. - return TRUE; + return true; } break; } @@ -668,31 +668,31 @@ BOOL get_can_item_be_worn(const LLUUID& id) if(gAgentWearables.isWearingItem(item->getLinkedUUID())) { // Already being worn - return FALSE; + return false; } else { // Not being worn yet. - return TRUE; + return true; } break; default: break; } - return FALSE; + return false; } -BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) +bool get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) { if (!model) { - return FALSE; + return false; } // Can't delete an item that's in the library. if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) { - return FALSE; + return false; } // Disable delete from COF folder; have users explicitly choose "detach/take off", @@ -701,20 +701,20 @@ BOOL get_is_item_removable(const LLInventoryModel* model, const LLUUID& id) { if (get_is_item_worn(id)) { - return FALSE; + return false; } } const LLInventoryObject *obj = model->getItem(id); if (obj && obj->getIsLinkType()) { - return TRUE; + return true; } if (get_is_item_worn(id)) { - return FALSE; + return false; } - return TRUE; + return true; } bool get_is_item_editable(const LLUUID& inv_item_id) @@ -761,7 +761,7 @@ void handle_item_edit(const LLUUID& inv_item_id) } } -BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) +bool get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) { // NOTE: This function doesn't check the folder's children. // See LLFolderBridge::isItemRemovable for a function that does @@ -769,27 +769,27 @@ BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) if (!model) { - return FALSE; + return false; } if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) { - return FALSE; + return false; } - if (!isAgentAvatarValid()) return FALSE; + if (!isAgentAvatarValid()) return false; const LLInventoryCategory* category = model->getCategory(id); if (!category) { - return FALSE; + return false; } const LLFolderType::EType folder_type = category->getPreferredType(); if (LLFolderType::lookupIsProtectedType(folder_type)) { - return FALSE; + return false; } // Can't delete the outfit that is currently being worn. @@ -798,18 +798,18 @@ BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); if (base_outfit_link && (category == base_outfit_link->getLinkedCategory())) { - return FALSE; + return false; } } - return TRUE; + return true; } -BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) +bool get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) { if (!model) { - return FALSE; + return false; } LLViewerInventoryCategory* cat = model->getCategory(id); @@ -817,9 +817,9 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) && cat->getOwnerID() == gAgent.getID()) { - return TRUE; + return true; } - return FALSE; + return false; } void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id) @@ -1255,7 +1255,7 @@ bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInve LLInventoryModel::cat_array_t existing_categories; LLInventoryModel::item_array_t existing_items; - gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, false); existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); existing_stock_count += count_stock_items(existing_items); @@ -1329,7 +1329,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn { LLInventoryModel::cat_array_t descendent_categories; LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, FALSE); + gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, false); int dragged_folder_count = descendent_categories.size() + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... int dragged_item_count = count_copyable_items(descendent_items) + count_stock_folders(descendent_categories); @@ -1351,7 +1351,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn // Tally the total number of categories and items inside the root folder LLInventoryModel::cat_array_t existing_categories; LLInventoryModel::item_array_t existing_items; - gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, FALSE); + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, false); existing_folder_count += existing_categories.size(); existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); @@ -2106,7 +2106,7 @@ void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected LLFolderViewItem* fv_folder = sidepanel_inventory->getActivePanel()->getItemByID(new_cat_uuid); if (fv_folder) { - fv_folder->setOpen(TRUE); + fv_folder->setOpen(true); } } } @@ -2152,7 +2152,7 @@ void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::st } // Returns true if the item can be moved to Current Outfit or any outfit folder. -bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +bool can_move_to_outfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit) { LLInventoryType::EType inv_type = inv_item->getInventoryType(); if ((inv_type != LLInventoryType::IT_WEARABLE) && @@ -2184,7 +2184,7 @@ bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_out return true; } -// Returns TRUE if item is a landmark or a link to a landmark +// Returns true if item is a landmark or a link to a landmark // and can be moved to Favorites or Landmarks folder. bool can_move_to_landmarks(LLInventoryItem* inv_item) { @@ -2518,40 +2518,40 @@ bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { if(mType == LLAssetType::AT_CATEGORY) { - if(cat) return TRUE; + if(cat) return true; } if(item) { - if(item->getType() == mType) return TRUE; + if(item->getType() == mType) return true; } - return FALSE; + return false; } bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { if(mType == LLAssetType::AT_CATEGORY) { - if(cat) return FALSE; + if(cat) return false; } if(item) { - if(item->getType() == mType) return FALSE; - else return TRUE; + if(item->getType() == mType) return false; + else return true; } - return TRUE; + return true; } bool LLIsOfAssetType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { if(mType == LLAssetType::AT_CATEGORY) { - if(cat) return TRUE; + if(cat) return true; } if(item) { - if(item->getActualType() == mType) return TRUE; + if(item->getActualType() == mType) return true; } - return FALSE; + return false; } bool LLIsValidItemLink::operator()(LLInventoryCategory* cat, LLInventoryItem* item) @@ -2567,7 +2567,7 @@ bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryIt { if(cat) { - return TRUE; + return true; } } if(item) @@ -2577,11 +2577,11 @@ bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryIt LLPermissions perm = item->getPermissions(); if ((perm.getMaskBase() & mPerm) == mPerm) { - return TRUE; + return true; } } } - return FALSE; + return false; } bool LLBuddyCollector::operator()(LLInventoryCategory* cat, @@ -2625,10 +2625,10 @@ bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat, if((LLAssetType::AT_CALLINGCARD == item->getType()) && (item->getCreatorUUID() == mBuddyID)) { - return TRUE; + return true; } } - return FALSE; + return false; } @@ -2680,9 +2680,9 @@ bool LLFindBrokenLinks::operator()(LLInventoryCategory* cat, // it is linked too if (item && LLAssetType::lookupIsLinkType(item->getType())) { - return TRUE; + return true; } - return FALSE; + return false; } bool LLFindWearables::operator()(LLInventoryCategory* cat, @@ -2693,10 +2693,10 @@ bool LLFindWearables::operator()(LLInventoryCategory* cat, if((item->getType() == LLAssetType::AT_CLOTHING) || (item->getType() == LLAssetType::AT_BODYPART)) { - return TRUE; + return true; } } - return FALSE; + return false; } LLFindWearablesEx::LLFindWearablesEx(bool is_worn, bool include_body_parts) @@ -2811,7 +2811,7 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) { if (!folder->isOpen()) { - folder->setOpen(TRUE); + folder->setOpen(true); } } else @@ -2819,7 +2819,7 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) // keep selected filter in its current state, this is less jarring to user if (!folder->isSelected() && folder->isOpen()) { - folder->setOpen(FALSE); + folder->setOpen(false); } } } @@ -2837,7 +2837,7 @@ void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) { if (item->passedFilter()) { - item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); + item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); } } @@ -2845,12 +2845,12 @@ void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) { if (folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) { - folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); + folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); } // if this folder didn't pass the filter, and none of its descendants did else if (!folder->getViewModelItem()->passedFilter() && !folder->getViewModelItem()->descendantsPassedFilter()) { - folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_NO); + folder->setOpenArrangeRecursively(false, LLFolderViewFolder::RECURSE_NO); } } @@ -2858,12 +2858,12 @@ void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) { if (item->passedFilter() && !mItemSelected) { - item->getRoot()->setSelection(item, FALSE, FALSE); + item->getRoot()->setSelection(item, false, false); if (item->getParentFolder()) { - item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); + item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); } - mItemSelected = TRUE; + mItemSelected = true; } } @@ -2872,9 +2872,9 @@ void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder) // Skip if folder or item already found, if not filtered or if no parent (root folder is not selectable) if (!mFolderSelected && !mItemSelected && folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) { - folder->getRoot()->setSelection(folder, FALSE, FALSE); - folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); - mFolderSelected = TRUE; + folder->getRoot()->setSelection(folder, false, false); + folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); + mFolderSelected = true; } } @@ -2882,7 +2882,7 @@ void LLOpenFoldersWithSelection::doItem(LLFolderViewItem *item) { if (item->getParentFolder() && item->isSelected()) { - item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); + item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); } } @@ -2890,7 +2890,7 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) { if (folder->getParentFolder() && folder->isSelected()) { - folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); + folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); } } @@ -2900,7 +2900,7 @@ void LLInventoryAction::callback_doToSelected(const LLSD& notification, const LL S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0) // YES { - doToSelected(model, root, action, FALSE); + doToSelected(model, root, action, false); } } @@ -2909,11 +2909,11 @@ void LLInventoryAction::callback_copySelected(const LLSD& notification, const LL S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (option == 0) // YES, Move no copy item(s) { - doToSelected(model, root, "copy_or_move_to_marketplace_listings", FALSE); + doToSelected(model, root, "copy_or_move_to_marketplace_listings", false); } else if (option == 1) // NO, Don't move no copy item(s) (leave them behind) { - doToSelected(model, root, "copy_to_marketplace_listings", FALSE); + doToSelected(model, root, "copy_to_marketplace_listings", false); } } @@ -2946,7 +2946,7 @@ bool get_selection_object_uuids(LLFolderView *root, uuid_vec_t& ids) } -void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action, BOOL user_confirm) +void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action, bool user_confirm) { std::set selected_items = root->getSelectionList(); -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/llinventoryfunctions.cpp | 7422 ++++++++++++++++---------------- 1 file changed, 3711 insertions(+), 3711 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 91f5f18671..5533c0328e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1,3711 +1,3711 @@ -/** - * @file llinventoryfunctions.cpp - * @brief Implementation of the inventory view and associated stuff. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include // for std::pair<> - -#include "llinventoryfunctions.h" - -// library includes -#include "llagent.h" -#include "llagentwearables.h" -#include "llcallingcard.h" -#include "llfloaterreg.h" -#include "llinventorydefines.h" -#include "llsdserialize.h" -#include "llfiltereditor.h" -#include "llspinctrl.h" -#include "llui.h" -#include "message.h" - -// newview includes -#include "llappearancemgr.h" -#include "llappviewer.h" -#include "llavataractions.h" -#include "llavatarnamecache.h" -#include "llclipboard.h" -#include "lldirpicker.h" -#include "lldonotdisturbnotificationstorage.h" -#include "llfloatermarketplacelistings.h" -#include "llfloatersidepanelcontainer.h" -#include "llfocusmgr.h" -#include "llfolderview.h" -#include "llgesturemgr.h" -#include "llgiveinventory.h" -#include "lliconctrl.h" -#include "llimview.h" -#include "llinventorybridge.h" -#include "llinventorymodel.h" -#include "llinventorypanel.h" -#include "lllineeditor.h" -#include "llmarketplacenotifications.h" -#include "llmarketplacefunctions.h" -#include "llmenugl.h" -#include "llnotificationsutil.h" -#include "llpanelmaininventory.h" -#include "llpreviewanim.h" -#include "llpreviewgesture.h" -#include "llpreviewnotecard.h" -#include "llpreviewscript.h" -#include "llpreviewsound.h" -#include "llpreviewtexture.h" -#include "llresmgr.h" -#include "llscrollbar.h" -#include "llscrollcontainer.h" -#include "llselectmgr.h" -#include "llsidepanelinventory.h" -#include "lltabcontainer.h" -#include "lltooldraganddrop.h" -#include "lltrans.h" -#include "lluictrlfactory.h" -#include "llviewermenu.h" -#include "llviewermessage.h" -#include "llviewerfoldertype.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llviewerwindow.h" -#include "llvoavatarself.h" -#include "llwearablelist.h" - -bool LLInventoryState::sWearNewClothing = false; -LLUUID LLInventoryState::sWearNewClothingTransactionID; -std::list LLInventoryAction::sMarketplaceFolders; -bool LLInventoryAction::sDeleteConfirmationDisplayed = false; - -// Helper function : callback to update a folder after inventory action happened in the background -void update_folder_cb(const LLUUID& dest_folder) -{ - LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); - gInventory.updateCategory(dest_cat); - gInventory.notifyObservers(); -} - -// Helper function : Count only the copyable items, i.e. skip the stock items (which are no copy) -S32 count_copyable_items(LLInventoryModel::item_array_t& items) -{ - S32 count = 0; - for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) - { - LLViewerInventoryItem* item = *it; - if (item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - count++; - } - } - return count; -} - -// Helper function : Count only the non-copyable items, i.e. the stock items, skip the others -S32 count_stock_items(LLInventoryModel::item_array_t& items) -{ - S32 count = 0; - for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) - { - LLViewerInventoryItem* item = *it; - if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - count++; - } - } - return count; -} - -// Helper function : Count the number of stock folders -S32 count_stock_folders(LLInventoryModel::cat_array_t& categories) -{ - S32 count = 0; - for (LLInventoryModel::cat_array_t::const_iterator it = categories.begin(); it != categories.end(); ++it) - { - LLInventoryCategory* cat = *it; - if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) - { - count++; - } - } - return count; -} - -// Helper funtion : Count the number of items (not folders) in the descending hierarchy -S32 count_descendants_items(const LLUUID& cat_id) -{ - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); - - S32 count = item_array->size(); - - LLInventoryModel::cat_array_t cat_array_copy = *cat_array; - for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) - { - LLViewerInventoryCategory* category = *iter; - count += count_descendants_items(category->getUUID()); - } - - return count; -} - -// Helper function : Returns true if the hierarchy contains nocopy items -bool contains_nocopy_items(const LLUUID& id) -{ - LLInventoryCategory* cat = gInventory.getCategory(id); - - if (cat) - { - // Get the content - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(id,cat_array,item_array); - - // Check all the items: returns true upon encountering a nocopy item - for (LLInventoryModel::item_array_t::iterator iter = item_array->begin(); iter != item_array->end(); iter++) - { - LLInventoryItem* item = *iter; - LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; - if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - return true; - } - } - - // Check all the sub folders recursively - for (LLInventoryModel::cat_array_t::iterator iter = cat_array->begin(); iter != cat_array->end(); iter++) - { - LLViewerInventoryCategory* cat = *iter; - if (contains_nocopy_items(cat->getUUID())) - { - return true; - } - } - } - else - { - LLInventoryItem* item = gInventory.getItem(id); - LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; - if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - return true; - } - } - - // Exit without meeting a nocopy item - return false; -} - -// Generates a string containing the path to the item specified by id. -void append_path(const LLUUID& id, std::string& path) -{ - std::string temp; - const LLInventoryObject* obj = gInventory.getObject(id); - LLUUID parent_id; - if(obj) parent_id = obj->getParentUUID(); - std::string forward_slash("/"); - while(obj) - { - obj = gInventory.getCategory(parent_id); - if(obj) - { - temp.assign(forward_slash + obj->getName() + temp); - parent_id = obj->getParentUUID(); - } - } - path.append(temp); -} - -// Generates a string containing the path name of the object. -std::string make_path(const LLInventoryObject* object) -{ - std::string path; - append_path(object->getUUID(), path); - return path + "/" + object->getName(); -} - -// Generates a string containing the path name of the object specified by id. -std::string make_inventory_path(const LLUUID& id) -{ - if (LLInventoryObject* object = gInventory.getObject(id)) - return make_path(object); - return ""; -} - -// Generates a string containing the path name and id of the object. -std::string make_info(const LLInventoryObject* object) -{ - return "'" + make_path(object) + "' (" + object->getUUID().asString() + ")"; -} - -// Generates a string containing the path name and id of the object specified by id. -std::string make_inventory_info(const LLUUID& id) -{ - if (LLInventoryObject* object = gInventory.getObject(id)) - return make_info(object); - return " (" + id.asString() + ")"; -} - -void update_marketplace_folder_hierarchy(const LLUUID cat_id) -{ - // When changing the marketplace status of a folder, the only thing that needs to happen is - // for all observers of the folder to, possibly, change the display label of the folder - // so that's the only thing we change on the update mask. - gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id); - - // Update all descendent folders down - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); - - 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++) - { - LLInventoryCategory* category = *iter; - update_marketplace_folder_hierarchy(category->getUUID()); - } - return; -} - -void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistency_enforcement, bool skip_clear_listing) -{ - // When changing the marketplace status of an item, we usually have to change the status of all - // folders in the same listing. This is because the display of each folder is affected by the - // overall status of the whole listing. - // Consequently, the only way to correctly update an item anywhere in the marketplace is to - // update the whole listing from its listing root. - // This is not as bad as it seems as we only update folders, not items, and the folder nesting depth - // is limited to 4. - // We also take care of degenerated cases so we don't update all folders in the inventory by mistake. - - if (cur_uuid.isNull() - || gInventory.getCategory(cur_uuid) == NULL - || gInventory.getCategory(cur_uuid)->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) - { - return; - } - - // Grab marketplace listing data for this item - S32 depth = depth_nesting_in_marketplace(cur_uuid); - if (depth > 0) - { - // Retrieve the listing uuid this object is in - LLUUID listing_uuid = nested_parent_id(cur_uuid, depth); - LLViewerInventoryCategory* listing_cat = gInventory.getCategory(listing_uuid); - bool listing_cat_loaded = listing_cat != NULL && listing_cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; - - // Verify marketplace data consistency for this listing - if (perform_consistency_enforcement - && listing_cat_loaded - && LLMarketplaceData::instance().isListed(listing_uuid)) - { - LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); - S32 version_depth = depth_nesting_in_marketplace(version_folder_uuid); - if (version_folder_uuid.notNull() && (!gInventory.isObjectDescendentOf(version_folder_uuid, listing_uuid) || (version_depth != 2))) - { - LL_INFOS("SLM") << "Unlist and clear version folder as the version folder is not at the right place anymore!!" << LL_ENDL; - LLMarketplaceData::instance().setVersionFolder(listing_uuid, LLUUID::null,1); - } - else if (version_folder_uuid.notNull() - && gInventory.isCategoryComplete(version_folder_uuid) - && LLMarketplaceData::instance().getActivationState(version_folder_uuid) - && (count_descendants_items(version_folder_uuid) == 0) - && !LLMarketplaceData::instance().isUpdating(version_folder_uuid,version_depth)) - { - LL_INFOS("SLM") << "Unlist as the version folder is empty of any item!!" << LL_ENDL; - LLNotificationsUtil::add("AlertMerchantVersionFolderEmpty"); - LLMarketplaceData::instance().activateListing(listing_uuid, false,1); - } - } - - // Check if the count on hand needs to be updated on SLM - if (perform_consistency_enforcement - && listing_cat_loaded - && (compute_stock_count(listing_uuid) != LLMarketplaceData::instance().getCountOnHand(listing_uuid))) - { - LLMarketplaceData::instance().updateCountOnHand(listing_uuid,1); - } - // Update all descendents starting from the listing root - update_marketplace_folder_hierarchy(listing_uuid); - } - else if (depth == 0) - { - // If this is the marketplace listings root itself, update all descendents - if (gInventory.getCategory(cur_uuid)) - { - update_marketplace_folder_hierarchy(cur_uuid); - } - } - else - { - // If the folder is outside the marketplace listings root, clear its SLM data if needs be - if (perform_consistency_enforcement && !skip_clear_listing && LLMarketplaceData::instance().isListed(cur_uuid)) - { - LL_INFOS("SLM") << "Disassociate as the listing folder is not under the marketplace folder anymore!!" << LL_ENDL; - LLMarketplaceData::instance().clearListing(cur_uuid); - } - // Update all descendents if this is a category - if (gInventory.getCategory(cur_uuid)) - { - update_marketplace_folder_hierarchy(cur_uuid); - } - } - - return; -} - -// Iterate through the marketplace and flag for label change all categories that countain a stock folder (i.e. stock folders and embedding folders up the hierarchy) -void update_all_marketplace_count(const LLUUID& cat_id) -{ - // Get all descendent folders down - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); - - 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++) - { - LLInventoryCategory* category = *iter; - if (category->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) - { - // Listing containing stock folders needs to be updated but not others - // Note: we take advantage of the fact that stock folder *do not* contain sub folders to avoid a recursive call here - update_marketplace_category(category->getUUID()); - } - else - { - // Explore the contained folders recursively - update_all_marketplace_count(category->getUUID()); - } - } -} - -void update_all_marketplace_count() -{ - // Get the marketplace root and launch the recursive exploration - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); - if (!marketplace_listings_uuid.isNull()) - { - update_all_marketplace_count(marketplace_listings_uuid); - } - return; -} - -void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) -{ - LLViewerInventoryCategory* cat; - - if (!model || - !get_is_category_renameable(model, cat_id) || - (cat = model->getCategory(cat_id)) == NULL || - cat->getName() == new_name) - { - return; - } - - LLSD updates; - updates["name"] = new_name; - update_inventory_category(cat_id, updates, NULL); -} - -void copy_inventory_category(LLInventoryModel* model, - LLViewerInventoryCategory* cat, - const LLUUID& parent_id, - const LLUUID& root_copy_id, - bool move_no_copy_items) -{ - // Create the initial folder - inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items](const LLUUID& new_id) - { - copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); - }; - gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); -} - -void copy_inventory_category(LLInventoryModel* model, - LLViewerInventoryCategory* cat, - const LLUUID& parent_id, - const LLUUID& root_copy_id, - bool move_no_copy_items, - inventory_func_type callback) -{ - // Create the initial folder - inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items, callback](const LLUUID &new_id) - { - copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); - if (callback) - { - callback(new_id); - } - }; - gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); -} - -void copy_cb(const LLUUID& dest_folder, const LLUUID& root_id) -{ - // Decrement the count in root_id since that one item won't be copied over - LLMarketplaceData::instance().decrementValidationWaiting(root_id); - update_folder_cb(dest_folder); -}; - -void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items) -{ - model->notifyObservers(); - - // We need to exclude the initial root of the copy to avoid recursively copying the copy, etc... - LLUUID root_id = (root_copy_id.isNull() ? new_cat_uuid : root_copy_id); - - // Get the content of the folder - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array); - - // If root_copy_id is null, tell the marketplace model we'll be waiting for new items to be copied over for this folder - if (root_copy_id.isNull()) - { - LLMarketplaceData::instance().setValidationWaiting(root_id, count_descendants_items(cat->getUUID())); - } - - LLPointer cb; - if (root_copy_id.isNull()) - { - cb = new LLBoostFuncInventoryCallback(boost::bind(copy_cb, new_cat_uuid, root_id)); - } - else - { - cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid)); - } - - // Copy all the items - LLInventoryModel::item_array_t item_array_copy = *item_array; - for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) - { - LLInventoryItem* item = *iter; - - if (item->getIsLinkType()) - { - link_inventory_object(new_cat_uuid, item->getLinkedUUID(), cb); - } - else if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - // If the item is nocopy, we do nothing or, optionally, move it - if (move_no_copy_items) - { - // Reparent the item - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *)item; - gInventory.changeItemParent(viewer_inv_item, new_cat_uuid, true); - } - if (root_copy_id.isNull()) - { - // Decrement the count in root_id since that one item won't be copied over - LLMarketplaceData::instance().decrementValidationWaiting(root_id); - } - } - else - { - copy_inventory_item( - gAgent.getID(), - item->getPermissions().getOwner(), - item->getUUID(), - new_cat_uuid, - std::string(), - cb); - } - } - - // Copy all the folders - LLInventoryModel::cat_array_t cat_array_copy = *cat_array; - for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) - { - LLViewerInventoryCategory* category = *iter; - if (category->getUUID() != root_id) - { - copy_inventory_category(model, category, new_cat_uuid, root_id, move_no_copy_items); - } - } -} - -class LLInventoryCollectAllItems : public LLInventoryCollectFunctor -{ -public: - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) - { - return true; - } -}; - -bool get_is_parent_to_worn_item(const LLUUID& id) -{ - const LLViewerInventoryCategory* cat = gInventory.getCategory(id); - if (!cat) - { - return false; - } - - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLInventoryCollectAllItems collect_all; - gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(), cats, items, LLInventoryModel::EXCLUDE_TRASH, collect_all); - - for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) - { - const LLViewerInventoryItem * const item = *it; - - llassert(item->getIsLinkType()); - - LLUUID linked_id = item->getLinkedUUID(); - const LLViewerInventoryItem * const linked_item = gInventory.getItem(linked_id); - - if (linked_item) - { - LLUUID parent_id = linked_item->getParentUUID(); - - while (!parent_id.isNull()) - { - LLInventoryCategory * parent_cat = gInventory.getCategory(parent_id); - - if (cat == parent_cat) - { - return true; - } - - parent_id = parent_cat->getParentUUID(); - } - } - } - - return false; -} - -bool get_is_item_worn(const LLUUID& id, const LLViewerInventoryItem* item) -{ - if (!item) - return false; - - if (item->getIsLinkType() && !gInventory.getItem(item->getLinkedUUID())) - { - return false; - } - - // Consider the item as worn if it has links in COF. - if (LLAppearanceMgr::instance().isLinkedInCOF(id)) - { - return true; - } - - switch(item->getType()) - { - case LLAssetType::AT_OBJECT: - { - if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) - return true; - break; - } - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - if(gAgentWearables.isWearingItem(item->getLinkedUUID())) - return true; - break; - case LLAssetType::AT_GESTURE: - if (LLGestureMgr::instance().isGestureActive(item->getLinkedUUID())) - return true; - break; - default: - break; - } - return false; -} - -bool get_is_item_worn(const LLUUID& id) -{ - const LLViewerInventoryItem* item = gInventory.getItem(id); - return get_is_item_worn(id, item); -} - -bool get_is_item_worn(const LLViewerInventoryItem* item) -{ - if (!item) - { - return false; - } - return get_is_item_worn(item->getUUID(), item); -} - -bool get_can_item_be_worn(const LLUUID& id) -{ - const LLViewerInventoryItem* item = gInventory.getItem(id); - if (!item) - return false; - - if (LLAppearanceMgr::instance().isLinkedInCOF(item->getLinkedUUID())) - { - // an item having links in COF (i.e. a worn item) - return false; - } - - if (gInventory.isObjectDescendentOf(id, LLAppearanceMgr::instance().getCOF())) - { - // a non-link object in COF (should not normally happen) - return false; - } - - const LLUUID trash_id = gInventory.findCategoryUUIDForType( - LLFolderType::FT_TRASH); - - // item can't be worn if base obj in trash, see EXT-7015 - if (gInventory.isObjectDescendentOf(item->getLinkedUUID(), - trash_id)) - { - return false; - } - - switch(item->getType()) - { - case LLAssetType::AT_OBJECT: - { - if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) - { - // Already being worn - return false; - } - else - { - // Not being worn yet. - return true; - } - break; - } - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - if(gAgentWearables.isWearingItem(item->getLinkedUUID())) - { - // Already being worn - return false; - } - else - { - // Not being worn yet. - return true; - } - break; - default: - break; - } - return false; -} - -bool get_is_item_removable(const LLInventoryModel* model, const LLUUID& id, bool check_worn) -{ - if (!model) - { - return false; - } - - // Can't delete an item that's in the library. - if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) - { - return false; - } - - // Disable delete from COF folder; have users explicitly choose "detach/take off", - // unless the item is not worn but in the COF (i.e. is bugged). - const LLViewerInventoryItem* obj = model->getItem(id); - if (LLAppearanceMgr::instance().getIsProtectedCOFItem(obj)) - { - if (get_is_item_worn(id, obj)) - { - return false; - } - } - - if (obj && obj->getIsLinkType()) - { - return true; - } - if (check_worn && get_is_item_worn(id, obj)) - { - return false; - } - return true; -} - -bool get_is_item_editable(const LLUUID& inv_item_id) -{ - if (const LLInventoryItem* inv_item = gInventory.getLinkedItem(inv_item_id)) - { - switch (inv_item->getType()) - { - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - return gAgentWearables.isWearableModifiable(inv_item_id); - case LLAssetType::AT_OBJECT: - return true; - default: - return false;; - } - } - return gAgentAvatarp->getWornAttachment(inv_item_id) != nullptr; -} - -void handle_item_edit(const LLUUID& inv_item_id) -{ - if (get_is_item_editable(inv_item_id)) - { - if (const LLInventoryItem* inv_item = gInventory.getLinkedItem(inv_item_id)) - { - switch (inv_item->getType()) - { - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - LLAgentWearables::editWearable(inv_item_id); - break; - case LLAssetType::AT_OBJECT: - handle_attachment_edit(inv_item_id); - break; - default: - break; - } - } - else - { - handle_attachment_edit(inv_item_id); - } - } -} - -bool get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) -{ - // NOTE: This function doesn't check the folder's children. - // See LLFolderBridge::isItemRemovable for a function that does - // consider the children. - - if (!model) - { - return false; - } - - if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) - { - return false; - } - - if (!isAgentAvatarValid()) return false; - - const LLInventoryCategory* category = model->getCategory(id); - if (!category) - { - return false; - } - - const LLFolderType::EType folder_type = category->getPreferredType(); - - if (LLFolderType::lookupIsProtectedType(folder_type)) - { - return false; - } - - // Can't delete the outfit that is currently being worn. - if (folder_type == LLFolderType::FT_OUTFIT) - { - const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); - if (base_outfit_link && (category == base_outfit_link->getLinkedCategory())) - { - return false; - } - } - - return true; -} - -bool get_is_category_and_children_removable(LLInventoryModel* model, const LLUUID& folder_id, bool check_worn) -{ - if (!get_is_category_removable(model, folder_id)) - { - return false; - } - - const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); - if (mp_id.notNull() && gInventory.isObjectDescendentOf(folder_id, mp_id)) - { - return false; - } - - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - model->collectDescendents( - folder_id, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); - - if (check_worn) - { - for (LLInventoryModel::item_array_t::value_type& item : item_array) - { - // Disable delete/cut from COF folder; have users explicitly choose "detach/take off", - // unless the item is not worn but in the COF (i.e. is bugged). - if (item) - { - if (LLAppearanceMgr::instance().getIsProtectedCOFItem(item)) - { - if (get_is_item_worn(item)) - { - return false; - } - } - - if (!item->getIsLinkType() && get_is_item_worn(item)) - { - return false; - } - } - } - } - - const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); - LLViewerInventoryCategory* outfit_linked_category = base_outfit_link ? base_outfit_link->getLinkedCategory() : nullptr; - for (LLInventoryModel::cat_array_t::value_type& cat : cat_array) - { - const LLFolderType::EType folder_type = cat->getPreferredType(); - if (LLFolderType::lookupIsProtectedType(folder_type)) - { - return false; - } - - // Can't delete the outfit that is currently being worn. - if (folder_type == LLFolderType::FT_OUTFIT) - { - if (cat == outfit_linked_category) - { - return false; - } - } - } - - return true; -} - -bool get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) -{ - if (!model) - { - return false; - } - - LLViewerInventoryCategory* cat = model->getCategory(id); - - if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) && - cat->getOwnerID() == gAgent.getID()) - { - return true; - } - return false; -} - -void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id) -{ - LLSD params; - params["id"] = item_uuid; - params["object"] = object_id; - - LLFloaterReg::showInstance("item_properties", params); -} - -void show_item_profile(const LLUUID& item_uuid) -{ - LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); - LLFloaterReg::showInstance("item_properties", LLSD().with("id", linked_uuid)); -} - -void show_item_original(const LLUUID& item_uuid) -{ - static LLUICachedControl find_original_new_floater("FindOriginalOpenWindow", false); - - //show in a new single-folder window - if(find_original_new_floater) - { - const LLUUID& linked_item_uuid = gInventory.getLinkedItemID(item_uuid); - const LLInventoryObject *obj = gInventory.getObject(linked_item_uuid); - if (obj && obj->getParentUUID().notNull()) - { - LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), linked_item_uuid); - } - } - //show in main Inventory - else - { - LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); - if (!floater_inventory) - { - LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; - return; - } - LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); - if (sidepanel_inventory) - { - LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); - if (main_inventory) - { - if(main_inventory->isSingleFolderMode()) - { - main_inventory->toggleViewMode(); - } - main_inventory->resetAllItemsFilters(); - } - reset_inventory_filter(); - - if (!LLFloaterReg::getTypedInstance("inventory")->isInVisibleChain()) - { - LLFloaterReg::toggleInstanceOrBringToFront("inventory"); - } - - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); - if (gInventory.isObjectDescendentOf(gInventory.getLinkedItemID(item_uuid), inbox_id)) - { - if (sidepanel_inventory->getInboxPanel()) - { - sidepanel_inventory->openInbox(); - sidepanel_inventory->getInboxPanel()->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_YES); - } - } - else - { - sidepanel_inventory->selectAllItemsPanel(); - if (sidepanel_inventory->getActivePanel()) - { - sidepanel_inventory->getActivePanel()->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_YES); - } - } - } - } -} - - -void reset_inventory_filter() -{ - LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); - if (sidepanel_inventory) - { - LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); - if (main_inventory) - { - main_inventory->onFilterEdit(""); - } - } -} - -void open_marketplace_listings() -{ - LLFloaterReg::showInstance("marketplace_listings"); -} - -///---------------------------------------------------------------------------- -// Marketplace functions -// -// Handles Copy and Move to or within the Marketplace listings folder. -// Handles creation of stock folders, nesting of listings and version folders, -// permission checking and listings validation. -///---------------------------------------------------------------------------- - -S32 depth_nesting_in_marketplace(LLUUID cur_uuid) -{ - // Get the marketplace listings root, exit with -1 (i.e. not under the marketplace listings root) if none - // Todo: findCategoryUUIDForType is somewhat expensive with large - // flat root folders yet we use depth_nesting_in_marketplace at - // every turn, find a way to correctly cache this id. - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); - if (marketplace_listings_uuid.isNull()) - { - return -1; - } - // If not a descendant of the marketplace listings root, then the nesting depth is -1 by definition - if (!gInventory.isObjectDescendentOf(cur_uuid, marketplace_listings_uuid)) - { - return -1; - } - - // Iterate through the parents till we hit the marketplace listings root - // Note that the marketplace listings root itself will return 0 - S32 depth = 0; - LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); - while (cur_uuid != marketplace_listings_uuid) - { - depth++; - cur_uuid = cur_object->getParentUUID(); - cur_object = gInventory.getCategory(cur_uuid); - } - return depth; -} - -// Returns the UUID of the marketplace listing this object is in -LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth) -{ - if (depth < 1) - { - // For objects outside the marketplace listings root (or root itself), we return a NULL UUID - return LLUUID::null; - } - else if (depth == 1) - { - // Just under the root, we return the passed UUID itself if it's a folder, NULL otherwise (not a listing) - LLViewerInventoryCategory* cat = gInventory.getCategory(cur_uuid); - return (cat ? cur_uuid : LLUUID::null); - } - - // depth > 1 - LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); - while (depth > 1) - { - depth--; - cur_uuid = cur_object->getParentUUID(); - cur_object = gInventory.getCategory(cur_uuid); - } - return cur_uuid; -} - -S32 compute_stock_count(LLUUID cat_uuid, bool force_count /* false */) -{ - // Handle the case of the folder being a stock folder immediately - LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); - if (!cat) - { - // Not a category so no stock count to speak of - return COMPUTE_STOCK_INFINITE; - } - if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) - { - if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) - { - // If the folder is not completely fetched, we do not want to return any confusing value that could lead to unlisting - // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder has a count that cannot be evaluated at this time (folder not up to date) - return COMPUTE_STOCK_NOT_EVALUATED; - } - // Note: stock folders are *not* supposed to have nested subfolders so we stop recursion here but we count only items (subfolders will be ignored) - // Note: we *always* give a stock count for stock folders, it's useful even if the listing is unassociated - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); - return item_array->size(); - } - - // When force_count is true, we do not do any verification of the marketplace status and simply compute - // the stock amount based on the descendent hierarchy. This is used specifically when creating a listing. - if (!force_count) - { - // Grab marketplace data for this folder - S32 depth = depth_nesting_in_marketplace(cat_uuid); - LLUUID listing_uuid = nested_parent_id(cat_uuid, depth); - if (!LLMarketplaceData::instance().isListed(listing_uuid)) - { - // If not listed, the notion of stock is meaningless so it won't be computed for any level - return COMPUTE_STOCK_INFINITE; - } - - LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); - // Handle the case of the first 2 levels : listing and version folders - if (depth == 1) - { - if (version_folder_uuid.notNull()) - { - // If there is a version folder, the stock value for the listing is the version folder stock - return compute_stock_count(version_folder_uuid, true); - } - else - { - // If there's no version folder associated, the notion of stock count has no meaning - return COMPUTE_STOCK_INFINITE; - } - } - else if (depth == 2) - { - if (version_folder_uuid.notNull() && (version_folder_uuid != cat_uuid)) - { - // If there is a version folder but we're not it, our stock count is meaningless - return COMPUTE_STOCK_INFINITE; - } - } - } - - // In all other cases, the stock count is the min of stock folders count found in the descendents - // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder in the hierarchy has a count that cannot be evaluated at this time (folder not up to date) - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); - - // "COMPUTE_STOCK_INFINITE" denotes a folder that doesn't countain any stock folders in its descendents - S32 curr_count = COMPUTE_STOCK_INFINITE; - - // Note: marketplace listings have a maximum depth nesting of 4 - 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++) - { - LLInventoryCategory* category = *iter; - S32 count = compute_stock_count(category->getUUID(), true); - if ((curr_count == COMPUTE_STOCK_INFINITE) || ((count != COMPUTE_STOCK_INFINITE) && (count < curr_count))) - { - curr_count = count; - } - } - - return curr_count; -} - -// local helper -bool can_move_to_marketplace(LLInventoryItem* inv_item, std::string& tooltip_msg, bool resolve_links) -{ - // Collapse links directly to items/folders - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); - LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); - - // Linked items and folders cannot be put for sale - if (linked_category || linked_item) - { - tooltip_msg = LLTrans::getString("TooltipOutboxLinked"); - return false; - } - - // A category is always considered as passing... - if (linked_category != NULL) - { - return true; - } - - // Take the linked item if necessary - if (linked_item != NULL) - { - inv_item = linked_item; - } - - // Check that the agent has transfer permission on the item: this is required as a resident cannot - // put on sale items she cannot transfer. Proceed with move if we have permission. - bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); - if (!allow_transfer) - { - tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer"); - return false; - } - - // Check worn/not worn status: worn items cannot be put on the marketplace - bool worn = get_is_item_worn(inv_item->getUUID()); - if (worn) - { - tooltip_msg = LLTrans::getString("TooltipOutboxWorn"); - return false; - } - - // Check library status: library items cannot be put on the marketplace - if (!gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.getRootFolderID())) - { - tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); - return false; - } - - // Check type: for the moment, calling cards cannot be put on the marketplace - bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); - if (calling_card) - { - tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard"); - return false; - } - - return true; -} - -// local helper -// Returns the max tree length (in folder nodes) down from the argument folder -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->size(); ++i) - { - LLInventoryCategory* category = cats->at(i); - max_child_levels = llmax(max_child_levels, get_folder_levels(category)); - } - - return 1 + max_child_levels; -} - -// local helper -// Returns the distance (in folder nodes) between the ancestor and its descendant. Returns -1 if not related. -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); - } - - LL_WARNS("SLM") << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << LL_ENDL; - return -1; -} - -// local helper -// Returns true if all items within the argument folder are fit for sale, false otherwise -bool has_correct_permissions_for_sale(LLInventoryCategory* cat, std::string& error_msg) -{ - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); - - LLInventoryModel::item_array_t item_array_copy = *item_array; - - for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) - { - LLInventoryItem* item = *iter; - if (!can_move_to_marketplace(item, error_msg, false)) - { - return false; - } - } - - 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++) - { - LLInventoryCategory* category = *iter; - if (!has_correct_permissions_for_sale(category, error_msg)) - { - return false; - } - } - return true; -} - -// Returns true if inv_item can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root -// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). -// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. -bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size, bool from_paste) -{ - // Check stock folder type matches item type in marketplace listings or merchant outbox (even if of no use there for the moment) - LLViewerInventoryCategory* view_folder = dynamic_cast(dest_folder); - bool move_in_stock = (view_folder && (view_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)); - bool accept = (view_folder && view_folder->acceptItem(inv_item)); - if (!accept) - { - tooltip_msg = LLTrans::getString("TooltipOutboxMixedStock"); - } - - // Check that the item has the right type and permissions to be sold on the marketplace - if (accept) - { - accept = can_move_to_marketplace(inv_item, tooltip_msg, true); - } - - // Check that the total amount of items won't violate the max limit on the marketplace - if (accept) - { - // If the dest folder is a stock folder, we do not count the incoming items toward the total (stock items are seen as one) - int existing_item_count = (move_in_stock ? 0 : bundle_size); - - // If the dest folder is a stock folder, we do assume that the incoming items are also stock items (they should anyway) - int existing_stock_count = (move_in_stock ? bundle_size : 0); - - int existing_folder_count = 0; - - // Get the version folder: that's where the counts start from - const LLViewerInventoryCategory * version_folder = ((root_folder && (root_folder != dest_folder)) ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); - - if (version_folder) - { - if (!from_paste && gInventory.isObjectDescendentOf(inv_item->getUUID(), version_folder->getUUID())) - { - // Clear those counts or they will be counted twice because we're already inside the version category - existing_item_count = 0; - } - - LLInventoryModel::cat_array_t existing_categories; - LLInventoryModel::item_array_t existing_items; - - gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, false); - - existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); - existing_stock_count += count_stock_items(existing_items); - existing_folder_count += existing_categories.size(); - - // If the incoming item is a nocopy (stock) item, we need to consider that it will create a stock folder - if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && !move_in_stock) - { - // Note : we do not assume that all incoming items are nocopy of different kinds... - existing_folder_count += 1; - } - } - - if (existing_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) - { - LLStringUtil::format_map_t args; - U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); - args["[AMOUNT]"] = llformat("%d",amount); - tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); - accept = false; - } - else if (existing_stock_count > gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) - { - LLStringUtil::format_map_t args; - U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); - args["[AMOUNT]"] = llformat("%d",amount); - tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); - accept = false; - } - else if (existing_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) - { - LLStringUtil::format_map_t args; - U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); - args["[AMOUNT]"] = llformat("%d",amount); - tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); - accept = false; - } - } - - return accept; -} - -// Returns true if inv_cat can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root -// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). -// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. -bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size, bool check_items, bool from_paste) -{ - bool accept = true; - - // Compute the nested folders level we'll add into with that incoming folder - int incoming_folder_depth = get_folder_levels(inv_cat); - // Compute the nested folders level we're inserting ourselves in - // Note: add 1 when inserting under a listing folder as we need to take the root listing folder in the count - int insertion_point_folder_depth = (root_folder ? get_folder_path_length(root_folder->getUUID(), dest_folder->getUUID()) + 1 : 1); - - // Get the version folder: that's where the folders and items counts start from - const LLViewerInventoryCategory * version_folder = (insertion_point_folder_depth >= 2 ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); - - // Compare the whole with the nested folders depth limit - // Note: substract 2 as we leave root and version folder out of the count threshold - if ((incoming_folder_depth + insertion_point_folder_depth - 2) > (S32)(gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"))) - { - LLStringUtil::format_map_t args; - U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"); - args["[AMOUNT]"] = llformat("%d",amount); - tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels", args); - accept = false; - } - - if (accept) - { - LLInventoryModel::cat_array_t descendent_categories; - LLInventoryModel::item_array_t descendent_items; - gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, false); - - int dragged_folder_count = descendent_categories.size() + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... - int dragged_item_count = count_copyable_items(descendent_items) + count_stock_folders(descendent_categories); - int dragged_stock_count = count_stock_items(descendent_items); - int existing_item_count = 0; - int existing_stock_count = 0; - int existing_folder_count = 0; - - if (version_folder) - { - if (!from_paste && gInventory.isObjectDescendentOf(inv_cat->getUUID(), version_folder->getUUID())) - { - // Clear those counts or they will be counted twice because we're already inside the version category - dragged_folder_count = 0; - dragged_item_count = 0; - dragged_stock_count = 0; - } - - // Tally the total number of categories and items inside the root folder - LLInventoryModel::cat_array_t existing_categories; - LLInventoryModel::item_array_t existing_items; - gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, false); - - existing_folder_count += existing_categories.size(); - existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); - existing_stock_count += count_stock_items(existing_items); - } - - const int total_folder_count = existing_folder_count + dragged_folder_count; - const int total_item_count = existing_item_count + dragged_item_count; - const int total_stock_count = existing_stock_count + dragged_stock_count; - - if (total_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) - { - LLStringUtil::format_map_t args; - U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); - args["[AMOUNT]"] = llformat("%d",amount); - tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); - accept = false; - } - else if (total_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) - { - LLStringUtil::format_map_t args; - U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); - args["[AMOUNT]"] = llformat("%d",amount); - tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); - accept = false; - } - else if (total_stock_count > gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) - { - LLStringUtil::format_map_t args; - U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); - args["[AMOUNT]"] = llformat("%d",amount); - tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); - accept = false; - } - - // Now check that each item in the folder can be moved in the marketplace - if (accept && check_items) - { - for (S32 i=0; i < descendent_items.size(); ++i) - { - LLInventoryItem* item = descendent_items[i]; - if (!can_move_to_marketplace(item, tooltip_msg, false)) - { - accept = false; - break; - } - } - } - } - - return accept; -} - -// Can happen asynhroneously!!! -bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy) -{ - // Get the marketplace listings depth of the destination folder, exit with error if not under marketplace - S32 depth = depth_nesting_in_marketplace(dest_folder); - if (depth < 0) - { - LLSD subs; - subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Merchant"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return false; - } - - // We will collapse links into items/folders - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; - LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); - - if (linked_category != NULL) - { - // Move the linked folder directly - return move_folder_to_marketplacelistings(linked_category, dest_folder, copy); - } - else - { - // Grab the linked item if any - LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); - viewer_inv_item = (linked_item != NULL ? linked_item : viewer_inv_item); - - // If we want to copy but the item is no copy, fail silently (this is a common case that doesn't warrant notification) - if (copy && !viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - return false; - } - - // Check that the agent has transfer permission on the item: this is required as a resident cannot - // put on sale items she cannot transfer. Proceed with move if we have permission. - std::string error_msg; - if (can_move_to_marketplace(inv_item, error_msg, true)) - { - // When moving an isolated item, we might need to create the folder structure to support it - - LLUUID item_id = inv_item->getUUID(); - std::function callback_create_stock = [copy, item_id](const LLUUID& new_cat_id) - { - if (new_cat_id.isNull()) - { - LL_WARNS() << "Failed to create category" << LL_ENDL; - LLSD subs; - subs["[ERROR_CODE]"] = - LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return; - } - - // Verify we can have this item in that destination category - LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); - LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); - if (!dest_cat || !viewer_inv_item) - { - LL_WARNS() << "Move to marketplace: item or folder do not exist" << LL_ENDL; - - LLSD subs; - subs["[ERROR_CODE]"] = - LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return; - } - if (!dest_cat->acceptItem(viewer_inv_item)) - { - LLSD subs; - subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - } - - if (copy) - { - // Copy the item - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_id)); - copy_inventory_item( - gAgent.getID(), - viewer_inv_item->getPermissions().getOwner(), - viewer_inv_item->getUUID(), - new_cat_id, - std::string(), - cb); - } - else - { - // Reparent the item - gInventory.changeItemParent(viewer_inv_item, new_cat_id, true); - } - }; - - std::function callback_dest_create = [item_id, callback_create_stock](const LLUUID& new_cat_id) - { - if (new_cat_id.isNull()) - { - LL_WARNS() << "Failed to create category" << LL_ENDL; - LLSD subs; - subs["[ERROR_CODE]"] = - LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return; - } - - LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); - LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); - if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && - (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) - { - // We need to create a stock folder to move a no copy item - gInventory.createNewCategory(new_cat_id, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName(), callback_create_stock); - } - else - { - callback_create_stock(new_cat_id); - } - }; - - if (depth == 0) - { - // We need a listing folder - gInventory.createNewCategory(dest_folder, - LLFolderType::FT_NONE, - viewer_inv_item->getName(), - [callback_dest_create](const LLUUID &new_cat_id) - { - if (new_cat_id.isNull()) - { - LL_WARNS() << "Failed to create listing folder for marketpace" << LL_ENDL; - return; - } - LLViewerInventoryCategory *dest_cat = gInventory.getCategory(new_cat_id); - if (!dest_cat) - { - LL_WARNS() << "Failed to find freshly created listing folder" << LL_ENDL; - return; - } - // version folder - gInventory.createNewCategory(new_cat_id, - LLFolderType::FT_NONE, - dest_cat->getName(), - callback_dest_create); - }); - } - else if (depth == 1) - { - // We need a version folder - gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create); - } - else - { - callback_dest_create(dest_folder); - } - } - else - { - LLSD subs; - subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return false; - } - } - - open_marketplace_listings(); - return true; -} - -bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy, bool move_no_copy_items) -{ - // Check that we have adequate permission on all items being moved. Proceed if we do. - std::string error_msg; - if (has_correct_permissions_for_sale(inv_cat, error_msg)) - { - // Get the destination folder - LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); - - // Check it's not a stock folder - if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) - { - LLSD subs; - subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return false; - } - - // Get the parent folder of the moved item : we may have to update it - LLUUID src_folder = inv_cat->getParentUUID(); - - LLViewerInventoryCategory * viewer_inv_cat = (LLViewerInventoryCategory *) inv_cat; - if (copy) - { - // Copy the folder - copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder, LLUUID::null, move_no_copy_items); - } - else - { - LL_INFOS("SLM") << "Move category " << make_info(viewer_inv_cat) << " to '" << make_inventory_path(dest_folder) << "'" << LL_ENDL; - // Reparent the folder - gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false); - // Check the destination folder recursively for no copy items and promote the including folders if any - LLMarketplaceValidator::getInstance()->validateMarketplaceListings(dest_folder); - } - - // Update the modified folders - update_marketplace_category(src_folder); - update_marketplace_category(dest_folder); - gInventory.notifyObservers(); - } - else - { - LLSD subs; - subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return false; - } - - open_marketplace_listings(); - return true; -} - -bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCategory* cat2) -{ - return cat1->getName().compare(cat2->getName()) < 0; -} - -// Make all relevant business logic checks on the marketplace listings starting with the folder as argument. -// This function does no deletion of listings but a mere audit and raises issues to the user (through the -// optional callback cb). -// The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders. -// @pending_callbacks - how many callbacks we are waiting for, must be inited before use -// @result - true if things validate, false if issues are raised, must be inited before use -typedef boost::function validation_result_callback_t; -void validate_marketplacelistings( - LLInventoryCategory* cat, - validation_result_callback_t cb_result, - LLMarketplaceValidator::validation_msg_callback_t cb_msg, - bool fix_hierarchy, - S32 depth, - bool notify_observers, - S32 &pending_callbacks, - bool &result) -{ - // Get the type and the depth of the folder - LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (cat); - const LLFolderType::EType folder_type = cat->getPreferredType(); - if (depth < 0) - { - // If the depth argument was not provided, evaluate the depth directly - depth = depth_nesting_in_marketplace(cat->getUUID()); - } - if (depth < 0) - { - // If the folder is not under the marketplace listings root, we run validation as if it was a listing folder and prevent any hierarchy fix - // This allows the function to be used to pre-validate a folder anywhere in the inventory - depth = 1; - fix_hierarchy = false; - } - - // Set the indentation for print output (typically, audit button in marketplace folder floater) - std::string indent; - for (int i = 1; i < depth; i++) - { - indent += " "; - } - - // Check out that version folders are marketplace ready - if (depth == 2) - { - std::string message; - // Note: if we fix the hierarchy, we want to check the items individually, hence the last argument here - if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy)) - { - result = false; - if (cb_msg) - { - message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - } - } - - // Check out that stock folders are at the right level - if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth <= 2)) - { - if (fix_hierarchy) - { - if (cb_msg) - { - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb_msg(message,depth,LLError::LEVEL_WARN); - } - - // Nest the stock folder one level deeper in a normal folder and restart from there - pending_callbacks++; - LLUUID parent_uuid = cat->getParentUUID(); - LLUUID cat_uuid = cat->getUUID(); - gInventory.createNewCategory(parent_uuid, - LLFolderType::FT_NONE, - cat->getName(), - [cat_uuid, cb_result, cb_msg, fix_hierarchy, depth](const LLUUID &new_cat_id) - { - if (new_cat_id.isNull()) - { - cb_result(0, false); - return; - } - LLInventoryCategory * move_cat = gInventory.getCategory(cat_uuid); - LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *)(move_cat); - LLInventoryCategory * new_cat = gInventory.getCategory(new_cat_id); - gInventory.changeCategoryParent(viewer_cat, new_cat_id, false); - S32 pending = 0; - bool result = true; - validate_marketplacelistings(new_cat, cb_result, cb_msg, fix_hierarchy, depth + 1, true, pending, result); - cb_result(pending, result); - } - ); - result = false; - return; - } - else - { - result = false; - if (cb_msg) - { - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - } - } - - // Item sorting and validation : sorting and moving the various stock items is complicated as the set of constraints is high - // We need to: - // * separate non stock items, stock items per types in different folders - // * have stock items nested at depth 2 at least - // * never ever move the non-stock items - - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); - - // We use a composite (type,permission) key on that map to store UUIDs of items of same (type,permissions) - std::map > items_vector; - - // Parse the items and create vectors of item UUIDs sorting copyable items and stock items of various types - bool has_bad_items = false; - 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; - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; - - // Test but skip items that shouldn't be there to start with, raise an error message for those - std::string error_msg; - if (!can_move_to_marketplace(item, error_msg, false)) - { - has_bad_items = true; - if (cb_msg && fix_hierarchy) - { - std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - continue; - } - // Update the appropriate vector item for that type - LLInventoryType::EType type = LLInventoryType::IT_COUNT; // Default value for non stock items - U32 perms = 0; - if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - // Get the item type for stock items - type = viewer_inv_item->getInventoryType(); - perms = viewer_inv_item->getPermissions().getMaskNextOwner(); - } - U32 key = (((U32)(type) & 0xFF) << 24) | (perms & 0xFFFFFF); - items_vector[key].push_back(viewer_inv_item->getUUID()); - } - - // How many types of items? Which type is it if only one? - S32 count = items_vector.size(); - U32 default_key = (U32)(LLInventoryType::IT_COUNT) << 24; // This is the key for any normal copyable item - U32 unique_key = (count == 1 ? items_vector.begin()->first : default_key); // The key in the case of one item type only - - // If we have no items in there (only folders or empty), analyze a bit further - if ((count == 0) && !has_bad_items) - { - if (cat_array->size() == 0) - { - // So we have no item and no folder. That's at least a warning. - if (depth == 2) - { - // If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist) - if (cb_msg) - { - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); - cb_msg(message,depth,LLError::LEVEL_WARN); - } - } - else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) - { - // If this is a legit but empty stock folder, warn only (listing must stay searchable when out of stock) - if (cb_msg) - { - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); - cb_msg(message,depth,LLError::LEVEL_WARN); - } - } - else if (cb_msg) - { - // We warn if there's nothing in a regular folder (may be it's an under construction listing) - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); - cb_msg(message,depth,LLError::LEVEL_WARN); - } - } - else - { - // Done with that folder : Print out the folder name unless we already found an error here - if (cb_msg && result && (depth >= 1)) - { - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb_msg(message,depth,LLError::LEVEL_INFO); - } - } - } - // If we have a single type of items of the right type in the right place, we're done - else if ((count == 1) && !has_bad_items && (((unique_key == default_key) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) - { - // Done with that folder : Print out the folder name unless we already found an error here - if (cb_msg && result && (depth >= 1)) - { - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb_msg(message,depth,LLError::LEVEL_INFO); - } - } - else - { - if (fix_hierarchy && !has_bad_items) - { - // Alert the user when an existing stock folder has to be split - if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && ((count >= 2) || (cat_array->size() > 0))) - { - LLNotificationsUtil::add("AlertMerchantStockFolderSplit"); - } - // If we have more than 1 type of items or we are at the listing level or we have stock/no stock type mismatch, wrap the items in subfolders - if ((count > 1) || (depth == 1) || - ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key == default_key)) || - ((folder_type != LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key != default_key))) - { - // Create one folder per vector at the right depth and of the right type - std::map >::iterator items_vector_it = items_vector.begin(); - while (items_vector_it != items_vector.end()) - { - // Create a new folder - const LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); - const LLUUID origin_uuid = viewer_cat->getUUID(); - LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); - std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName()); - LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); - if (cb_msg) - { - std::string message = ""; - if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK) - { - message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Stock"); - } - else - { - message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); - } - cb_msg(message,depth,LLError::LEVEL_WARN); - } - - pending_callbacks++; - std::vector uuid_vector = items_vector_it->second; // needs to be a copy for lambda - gInventory.createNewCategory( - parent_uuid, - new_folder_type, - folder_name, - [uuid_vector, cb_result, cb_msg, depth, parent_uuid, origin_uuid, notify_observers](const LLUUID &new_category_id) - { - // Move each item to the new folder - std::vector::const_reverse_iterator iter = uuid_vector.rbegin(); - while (iter != uuid_vector.rend()) - { - LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(*iter); - if (cb_msg) - { - std::string indent; - for (int i = 1; i < depth; i++) - { - indent += " "; - } - std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); - cb_msg(message, depth, LLError::LEVEL_WARN); - } - gInventory.changeItemParent(viewer_inv_item, new_category_id, true); - iter++; - } - - if (origin_uuid != parent_uuid) - { - // We might have moved last item from a folder, check if it needs to be removed - LLViewerInventoryCategory* cat = gInventory.getCategory(origin_uuid); - if (cat->getDescendentCount() == 0) - { - // Remove previous folder if it ends up empty - if (cb_msg) - { - std::string indent; - for (int i = 1; i < depth; i++) - { - indent += " "; - } - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); - cb_msg(message, depth, LLError::LEVEL_WARN); - } - gInventory.removeCategory(cat->getUUID()); - if (notify_observers) - { - gInventory.notifyObservers(); - } - } - } - - // Next type - update_marketplace_category(parent_uuid); - update_marketplace_category(new_category_id); - if (notify_observers) - { - gInventory.notifyObservers(); - } - cb_result(0, true); - } - ); - items_vector_it++; - } - } - // Stock folder should have no sub folder so reparent those up - if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK) - { - LLUUID parent_uuid = cat->getParentUUID(); - gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); - LLInventoryModel::cat_array_t cat_array_copy = *cat_array; - for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) - { - LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (*iter); - gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); - validate_marketplacelistings(viewer_cat, cb_result, cb_msg, fix_hierarchy, depth, false, pending_callbacks, result); - } - } - } - else if (cb_msg) - { - // We are not fixing the hierarchy but reporting problems, report everything we can find - // Print the folder name - if (result && (depth >= 1)) - { - if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (count >= 2)) - { - // Report if a stock folder contains a mix of items - result = false; - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0)) - { - // Report if a stock folder contains subfolders - result = false; - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock"); - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - else - { - // Simply print the folder name - std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb_msg(message,depth,LLError::LEVEL_INFO); - } - } - // Scan each item and report if there's a problem - 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; - LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; - std::string error_msg; - if (!can_move_to_marketplace(item, error_msg, false)) - { - // Report items that shouldn't be there to start with - result = false; - std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) - { - // Report stock items that are misplaced - result = false; - std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - else if (depth == 1) - { - // Report items not wrapped in version folder - result = false; - std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); - cb_msg(message,depth,LLError::LEVEL_ERROR); - } - } - } - - // Clean up - if (viewer_cat->getDescendentCount() == 0) - { - // Remove the current folder if it ends up empty - if (cb_msg) - { - std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); - cb_msg(message,depth,LLError::LEVEL_WARN); - } - gInventory.removeCategory(cat->getUUID()); - if (notify_observers) - { - gInventory.notifyObservers(); - } - result &=!has_bad_items; - return; - } - } - - // Recursion : Perform the same validation on each nested folder - gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); - LLInventoryModel::cat_array_t cat_array_copy = *cat_array; - // Sort the folders in alphabetical order first - std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha); - - for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) - { - LLInventoryCategory* category = *iter; - validate_marketplacelistings(category, cb_result, cb_msg, fix_hierarchy, depth + 1, false, pending_callbacks, result); - } - - update_marketplace_category(cat->getUUID(), true, true); - if (notify_observers) - { - gInventory.notifyObservers(); - } - result &= !has_bad_items; -} - -void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) -{ - LLInventoryItem* inv_item = gInventory.getItem(item_id); - if (inv_item) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(inv_item->getParentUUID(), -1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - LLPointer new_item = new LLViewerInventoryItem(inv_item); - new_item->setParent(new_parent_id); - new_item->updateParentOnServer(false); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - } -} - -void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected_uuids) -{ - for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) - { - LLInventoryItem* inv_item = gInventory.getItem(*it); - if (inv_item) - { - change_item_parent(*it, new_cat_uuid); - } - else - { - LLInventoryCategory* inv_cat = gInventory.getCategory(*it); - if (inv_cat && !LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) - { - gInventory.changeCategoryParent((LLViewerInventoryCategory*)inv_cat, new_cat_uuid, false); - } - } - } - - LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); - if (!floater_inventory) - { - LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; - return; - } - LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); - if (sidepanel_inventory) - { - if (sidepanel_inventory->getActivePanel()) - { - sidepanel_inventory->getActivePanel()->setSelection(new_cat_uuid, TAKE_FOCUS_YES); - LLFolderViewItem* fv_folder = sidepanel_inventory->getActivePanel()->getItemByID(new_cat_uuid); - if (fv_folder) - { - fv_folder->setOpen(true); - } - } - } -} - -bool is_only_cats_selected(const uuid_vec_t& selected_uuids) -{ - for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) - { - LLInventoryCategory* inv_cat = gInventory.getCategory(*it); - if (!inv_cat) - { - return false; - } - } - return true; -} - -bool is_only_items_selected(const uuid_vec_t& selected_uuids) -{ - for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) - { - LLViewerInventoryItem* inv_item = gInventory.getItem(*it); - if (!inv_item) - { - return false; - } - } - return true; -} - - -void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::string& folder_name) -{ - LLInventoryObject* first_item = gInventory.getObject(*selected_uuids.begin()); - if (!first_item) - { - return; - } - - inventory_func_type func = boost::bind(&move_items_to_folder, _1, selected_uuids); - gInventory.createNewCategory(first_item->getParentUUID(), LLFolderType::FT_NONE, folder_name, func); -} - -std::string get_category_path(LLUUID cat_id) -{ - LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); - std::string localized_cat_name; - if (!LLTrans::findString(localized_cat_name, "InvFolder " + cat->getName())) - { - localized_cat_name = cat->getName(); - } - - if (cat->getParentUUID().notNull()) - { - return get_category_path(cat->getParentUUID()) + " > " + localized_cat_name; - } - else - { - return localized_cat_name; - } -} -// Returns true if the item can be moved to Current Outfit or any outfit folder. -bool can_move_to_outfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit) -{ - LLInventoryType::EType inv_type = inv_item->getInventoryType(); - if ((inv_type != LLInventoryType::IT_WEARABLE) && - (inv_type != LLInventoryType::IT_GESTURE) && - (inv_type != LLInventoryType::IT_ATTACHMENT) && - (inv_type != LLInventoryType::IT_OBJECT) && - (inv_type != LLInventoryType::IT_SNAPSHOT) && - (inv_type != LLInventoryType::IT_TEXTURE)) - { - return false; - } - - U32 flags = inv_item->getFlags(); - if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS) - { - return false; - } - - if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT)) - { - return !move_is_into_current_outfit; - } - - if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) - { - return false; - } - - return true; -} - -// Returns true if item is a landmark or a link to a landmark -// and can be moved to Favorites or Landmarks folder. -bool can_move_to_landmarks(LLInventoryItem* inv_item) -{ - // Need to get the linked item to know its type because LLInventoryItem::getType() - // returns actual type AT_LINK for links, not the asset type of a linked item. - if (LLAssetType::AT_LINK == inv_item->getType()) - { - LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); - if (linked_item) - { - return LLAssetType::AT_LANDMARK == linked_item->getType(); - } - } - - return LLAssetType::AT_LANDMARK == inv_item->getType(); -} - -// Returns true if folder's content can be moved to Current Outfit or any outfit folder. -bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit) -{ - LLInventoryModel::cat_array_t *cats; - LLInventoryModel::item_array_t *items; - model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items); - - if (items->size() > wear_limit) - { - return false; - } - - if (items->size() == 0) - { - // Nothing to move(create) - return false; - } - - if (cats->size() > 0) - { - // We do not allow subfolders in outfits of "My Outfits" yet - return false; - } - - LLInventoryModel::item_array_t::iterator iter = items->begin(); - LLInventoryModel::item_array_t::iterator end = items->end(); - - while (iter != end) - { - LLViewerInventoryItem *item = *iter; - if (!can_move_to_outfit(item, false)) - { - return false; - } - iter++; - } - - return true; -} - -std::string get_localized_folder_name(LLUUID cat_uuid) -{ - std::string localized_root_name; - const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); - if (cat) - { - LLFolderType::EType preferred_type = cat->getPreferredType(); - - // Translation of Accessories folder in Library inventory folder - bool accessories = false; - if(cat->getName() == "Accessories") - { - const LLUUID& parent_folder_id = cat->getParentUUID(); - accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); - } - - //"Accessories" inventory category has folder type FT_NONE. So, this folder - //can not be detected as protected with LLFolderType::lookupIsProtectedType - localized_root_name.assign(cat->getName()); - if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) - { - LLTrans::findString(localized_root_name, std::string("InvFolder ") + cat->getName(), LLSD()); - } - } - - return localized_root_name; -} - -void new_folder_window(const LLUUID& folder_id) -{ - LLPanelMainInventory::newFolderWindow(folder_id); -} - -void ungroup_folder_items(const LLUUID& folder_id) -{ - LLInventoryCategory* inv_cat = gInventory.getCategory(folder_id); - if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) - { - return; - } - const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); - LLInventoryModel::cat_array_t cats = *cat_array; - LLInventoryModel::item_array_t items = *item_array; - - for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) - { - LLViewerInventoryCategory* cat = *cat_iter; - if (cat) - { - gInventory.changeCategoryParent(cat, new_cat_uuid, false); - } - } - for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) - { - LLViewerInventoryItem* item = *item_iter; - if(item) - { - gInventory.changeItemParent(item, new_cat_uuid, false); - } - } - gInventory.removeCategory(inv_cat->getUUID()); - gInventory.notifyObservers(); -} - -std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id) -{ - if (model) - { - const LLInventoryItem *item = model->getItem(item_id); - if(item) - { - std::string desc = item->getDescription(); - LLStringUtil::toUpper(desc); - return desc; - } - } - return LLStringUtil::null; -} - -std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id) -{ - if (model) - { - const LLInventoryItem *item = model->getItem(item_id); - if(item) - { - LLAvatarName av_name; - if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) - { - std::string username = av_name.getUserName(); - LLStringUtil::toUpper(username); - return username; - } - } - } - return LLStringUtil::null; -} - -std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id) -{ - if (model) - { - const LLViewerInventoryItem *item = model->getItem(item_id); - if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery())) - { - std::string uuid = item->getAssetUUID().asString(); - LLStringUtil::toUpper(uuid); - return uuid; - } - } - return LLStringUtil::null; -} - -bool can_share_item(const LLUUID& item_id) -{ - bool can_share = false; - - if (gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID())) - { - const LLViewerInventoryItem *item = gInventory.getItem(item_id); - if (item) - { - if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) - { - can_share = LLGiveInventory::isInventoryGiveAcceptable(item); - } - } - else - { - can_share = (gInventory.getCategory(item_id) != NULL); - } - - const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - if ((item_id == trash_id) || gInventory.isObjectDescendentOf(item_id, trash_id)) - { - can_share = false; - } - } - - return can_share; -} -///---------------------------------------------------------------------------- -/// LLMarketplaceValidator implementations -///---------------------------------------------------------------------------- - - -LLMarketplaceValidator::LLMarketplaceValidator() - : mPendingCallbacks(0) - , mValidationInProgress(false) -{ -} - -LLMarketplaceValidator::~LLMarketplaceValidator() -{ -} - -void LLMarketplaceValidator::validateMarketplaceListings( - const LLUUID &category_id, - LLMarketplaceValidator::validation_done_callback_t cb_done, - LLMarketplaceValidator::validation_msg_callback_t cb_msg, - bool fix_hierarchy, - S32 depth) -{ - - mValidationQueue.emplace(category_id, cb_done, cb_msg, fix_hierarchy, depth); - if (!mValidationInProgress) - { - start(); - } -} - -void LLMarketplaceValidator::start() -{ - if (mValidationQueue.empty()) - { - mValidationInProgress = false; - return; - } - mValidationInProgress = true; - - const ValidationRequest &first = mValidationQueue.front(); - LLViewerInventoryCategory* cat = gInventory.getCategory(first.mCategoryId); - if (!cat) - { - LL_WARNS() << "Tried to validate a folder that doesn't exist" << LL_ENDL; - if (first.mCbDone) - { - first.mCbDone(false); - } - mValidationQueue.pop(); - start(); - return; - } - - validation_result_callback_t result_callback = [](S32 pending, bool result) - { - LLMarketplaceValidator* validator = LLMarketplaceValidator::getInstance(); - validator->mPendingCallbacks--; // we just got a callback - validator->mPendingCallbacks += pending; - validator->mPendingResult &= result; - if (validator->mPendingCallbacks <= 0) - { - llassert(validator->mPendingCallbacks == 0); // shouldn't be below 0 - const ValidationRequest &first = validator->mValidationQueue.front(); - if (first.mCbDone) - { - first.mCbDone(validator->mPendingResult); - } - validator->mValidationQueue.pop(); // done; - validator->start(); - } - }; - - mPendingResult = true; - mPendingCallbacks = 1; // do '1' in case something decides to callback immediately - - S32 pending_calbacks = 0; - bool result = true; - validate_marketplacelistings( - cat, - result_callback, - first.mCbMsg, - first.mFixHierarchy, - first.mDepth, - true, - pending_calbacks, - result); - - result_callback(pending_calbacks, result); -} - -LLMarketplaceValidator::ValidationRequest::ValidationRequest( - LLUUID category_id, - validation_done_callback_t cb_done, - validation_msg_callback_t cb_msg, - bool fix_hierarchy, - S32 depth) -: mCategoryId(category_id) -, mCbDone(cb_done) -, mCbMsg(cb_msg) -, mFixHierarchy(fix_hierarchy) -, mDepth(depth) -{} - -///---------------------------------------------------------------------------- -/// LLInventoryCollectFunctor implementations -///---------------------------------------------------------------------------- - -// static -bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryItem* item) -{ - if (!item) - return false; - - switch(item->getType()) - { - case LLAssetType::AT_OBJECT: - case LLAssetType::AT_BODYPART: - case LLAssetType::AT_CLOTHING: - if (!get_is_item_worn(item->getUUID())) - return true; - break; - default: - return true; - break; - } - return false; -} - -bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - if(mType == LLAssetType::AT_CATEGORY) - { - if(cat) return true; - } - if(item) - { - if(item->getType() == mType) return true; - } - return false; -} - -bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - if(mType == LLAssetType::AT_CATEGORY) - { - if(cat) return false; - } - if(item) - { - if(item->getType() == mType) return false; - else return true; - } - return true; -} - -bool LLIsOfAssetType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - if(mType == LLAssetType::AT_CATEGORY) - { - if(cat) return true; - } - if(item) - { - if(item->getActualType() == mType) return true; - } - return false; -} - -bool LLAssetIDAndTypeMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - if (!item) return false; - return (item->getActualType() == mType && item->getAssetUUID() == mAssetID); -} - -bool LLIsValidItemLink::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - LLViewerInventoryItem *vitem = dynamic_cast(item); - if (!vitem) return false; - return (vitem->getActualType() == LLAssetType::AT_LINK && !vitem->getIsBrokenLink()); -} - -bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - if(mType == LLAssetType::AT_CATEGORY) - { - if(cat) - { - return true; - } - } - if(item) - { - if(item->getType() == mType) - { - LLPermissions perm = item->getPermissions(); - if ((perm.getMaskBase() & mPerm) == mPerm) - { - return true; - } - } - } - return false; -} - -bool LLBuddyCollector::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item) - { - if((LLAssetType::AT_CALLINGCARD == item->getType()) - && (!item->getCreatorUUID().isNull()) - && (item->getCreatorUUID() != gAgent.getID())) - { - return true; - } - } - return false; -} - - -bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item) - { - if((LLAssetType::AT_CALLINGCARD == item->getType()) - && (item->getCreatorUUID().notNull()) - && (item->getCreatorUUID() != gAgent.getID())) - { - mSeen.insert(item->getCreatorUUID()); - return true; - } - } - return false; -} - - -bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item) - { - if((LLAssetType::AT_CALLINGCARD == item->getType()) - && (item->getCreatorUUID() == mBuddyID)) - { - return true; - } - } - return false; -} - - -bool LLNameCategoryCollector::operator()( - LLInventoryCategory* cat, LLInventoryItem* item) -{ - if(cat) - { - if (!LLStringUtil::compareInsensitive(mName, cat->getName())) - { - return true; - } - } - return false; -} - -bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - // Valid COF items are: - // - links to wearables (body parts or clothing) - // - links to attachments - // - links to gestures - // - links to ensemble folders - LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); - if (linked_item) - { - LLAssetType::EType type = linked_item->getType(); - return (type == LLAssetType::AT_CLOTHING || - type == LLAssetType::AT_BODYPART || - type == LLAssetType::AT_GESTURE || - type == LLAssetType::AT_OBJECT); - } - else - { - LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); - // BAP remove AT_NONE support after ensembles are fully working? - return (linked_category && - ((linked_category->getPreferredType() == LLFolderType::FT_NONE) || - (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType())))); - } -} - -bool LLFindBrokenLinks::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - // only for broken links getType will be a link - // otherwise it's supposed to have the type of an item - // it is linked too - if (item && LLAssetType::lookupIsLinkType(item->getType())) - { - return true; - } - return false; -} - -bool LLFindWearables::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item) - { - if((item->getType() == LLAssetType::AT_CLOTHING) - || (item->getType() == LLAssetType::AT_BODYPART)) - { - return true; - } - } - return false; -} - -LLFindWearablesEx::LLFindWearablesEx(bool is_worn, bool include_body_parts) -: mIsWorn(is_worn) -, mIncludeBodyParts(include_body_parts) -{} - -bool LLFindWearablesEx::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - LLViewerInventoryItem *vitem = dynamic_cast(item); - if (!vitem) return false; - - // Skip non-wearables. - if (!vitem->isWearableType() && vitem->getType() != LLAssetType::AT_OBJECT && vitem->getType() != LLAssetType::AT_GESTURE) - { - return false; - } - - // Skip body parts if requested. - if (!mIncludeBodyParts && vitem->getType() == LLAssetType::AT_BODYPART) - { - return false; - } - - // Skip broken links. - if (vitem->getIsBrokenLink()) - { - return false; - } - - return (bool) get_is_item_worn(item->getUUID()) == mIsWorn; -} - -bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - if (!item) return false; - if (item->getType() != LLAssetType::AT_CLOTHING && - item->getType() != LLAssetType::AT_BODYPART) - { - return false; - } - - LLViewerInventoryItem *vitem = dynamic_cast(item); - if (!vitem || vitem->getWearableType() != mWearableType) return false; - - return true; -} - -void LLFindWearablesOfType::setType(LLWearableType::EType type) -{ - mWearableType = type; -} - -bool LLIsTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - return item && (item->getType() == LLAssetType::AT_TEXTURE); -} - -bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - if (item) - { - return !get_is_item_removable(&gInventory, item->getUUID(), true); - } - if (cat) - { - return !get_is_category_removable(&gInventory, cat->getUUID()); - } - - LL_WARNS() << "Not a category and not an item?" << LL_ENDL; - return false; -} - -///---------------------------------------------------------------------------- -/// LLAssetIDMatches -///---------------------------------------------------------------------------- -bool LLAssetIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - return (item && item->getAssetUUID() == mAssetID); -} - -///---------------------------------------------------------------------------- -/// LLLinkedItemIDMatches -///---------------------------------------------------------------------------- -bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - return (item && - (item->getIsLinkType()) && - (item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID. -} - -void LLSaveFolderState::setApply(bool apply) -{ - mApply = apply; - // before generating new list of open folders, clear the old one - if(!apply) - { - clearOpenFolders(); - } -} - -void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) -{ - LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getViewModelItem(); - if(!bridge) return; - - if(mApply) - { - // we're applying the open state - LLUUID id(bridge->getUUID()); - if(mOpenFolders.find(id) != mOpenFolders.end()) - { - if (!folder->isOpen()) - { - folder->setOpen(true); - } - } - else - { - // keep selected filter in its current state, this is less jarring to user - if (!folder->isSelected() && folder->isOpen()) - { - folder->setOpen(false); - } - } - } - else - { - // we're recording state at this point - if(folder->isOpen()) - { - mOpenFolders.insert(bridge->getUUID()); - } - } -} - -void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) -{ - if (item->passedFilter()) - { - item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); - } -} - -void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) -{ - if (folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) - { - folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); - } - // if this folder didn't pass the filter, and none of its descendants did - else if (!folder->getViewModelItem()->passedFilter() && !folder->getViewModelItem()->descendantsPassedFilter()) - { - folder->setOpenArrangeRecursively(false, LLFolderViewFolder::RECURSE_NO); - } -} - -void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) -{ - if (item->passedFilter() && !mItemSelected) - { - item->getRoot()->setSelection(item, false, false); - if (item->getParentFolder()) - { - item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); - } - mItemSelected = true; - } -} - -void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder) -{ - // Skip if folder or item already found, if not filtered or if no parent (root folder is not selectable) - if (!mFolderSelected && !mItemSelected && folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) - { - folder->getRoot()->setSelection(folder, false, false); - folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); - mFolderSelected = true; - } -} - -void LLOpenFoldersWithSelection::doItem(LLFolderViewItem *item) -{ - if (item->getParentFolder() && item->isSelected()) - { - item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); - } -} - -void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) -{ - if (folder->getParentFolder() && folder->isSelected()) - { - folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); - } -} - -// Callback for doToSelected if DAMA required... -void LLInventoryAction::callback_doToSelected(const LLSD& notification, const LLSD& response, class LLInventoryModel* model, class LLFolderView* root, const std::string& action) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0) // YES - { - doToSelected(model, root, action, false); - } -} - -void LLInventoryAction::callback_copySelected(const LLSD& notification, const LLSD& response, class LLInventoryModel* model, class LLFolderView* root, const std::string& action) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0) // YES, Move no copy item(s) - { - doToSelected(model, root, "copy_or_move_to_marketplace_listings", false); - } - else if (option == 1) // NO, Don't move no copy item(s) (leave them behind) - { - doToSelected(model, root, "copy_to_marketplace_listings", false); - } -} - -// Succeeds iff all selected items are bridges to objects, in which -// case returns their corresponding uuids. -bool get_selection_object_uuids(LLFolderView *root, uuid_vec_t& ids) -{ - uuid_vec_t results; - S32 non_object = 0; - LLFolderView::selected_items_t selectedItems = root->getSelectedItems(); - for(LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) - { - LLObjectBridge *view_model = dynamic_cast((*it)->getViewModelItem()); - - if(view_model && view_model->getUUID().notNull()) - { - results.push_back(view_model->getUUID()); - } - else - { - non_object++; - } - } - if (non_object == 0) - { - ids = results; - return true; - } - return false; -} - - -void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action, bool user_confirm) -{ - std::set selected_items = root->getSelectionList(); - if (selected_items.empty() - && action != "wear" - && action != "wear_add" - && !isRemoveAction(action)) - { - // Was item removed while user was checking menu? - // "wear" and removal exlusions are due to use of - // getInventorySelectedUUIDs() below - LL_WARNS("Inventory") << "Menu tried to operate on empty selection" << LL_ENDL; - - if (("copy" == action) || ("cut" == action)) - { - LLClipboard::instance().reset(); - } - - return; - } - - // Prompt the user and check for authorization for some marketplace active listing edits - if (user_confirm && (("delete" == action) || ("cut" == action) || ("rename" == action) || ("properties" == action) || ("task_properties" == action) || ("open" == action))) - { - std::set::iterator set_iter = selected_items.begin(); - LLFolderViewModelItemInventory * viewModel = NULL; - for (; set_iter != selected_items.end(); ++set_iter) - { - viewModel = dynamic_cast((*set_iter)->getViewModelItem()); - if (viewModel && (depth_nesting_in_marketplace(viewModel->getUUID()) >= 0)) - { - break; - } - } - if (set_iter != selected_items.end()) - { - if ("open" == action) - { - if (get_can_item_be_worn(viewModel->getUUID())) - { - // Wearing an object from any listing, active or not, is verbotten - LLNotificationsUtil::add("AlertMerchantListingCannotWear"); - return; - } - // Note: we do not prompt for change when opening items (e.g. textures or note cards) on the marketplace... - } - else if (LLMarketplaceData::instance().isInActiveFolder(viewModel->getUUID()) || - LLMarketplaceData::instance().isListedAndActive(viewModel->getUUID())) - { - // If item is in active listing, further confirmation is required - if ((("cut" == action) || ("delete" == action)) && (LLMarketplaceData::instance().isListed(viewModel->getUUID()) || LLMarketplaceData::instance().isVersionFolder(viewModel->getUUID()))) - { - // Cut or delete of the active version folder or listing folder itself will unlist the listing so ask that question specifically - LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); - return; - } - // Any other case will simply modify but not unlist a listing - LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); - return; - } - // Cutting or deleting a whole listing needs confirmation as SLM will be archived and inaccessible to the user - else if (LLMarketplaceData::instance().isListed(viewModel->getUUID()) && (("cut" == action) || ("delete" == action))) - { - LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); - return; - } - } - } - // Copying to the marketplace needs confirmation if nocopy items are involved - if (user_confirm && ("copy_to_marketplace_listings" == action)) - { - std::set::iterator set_iter = selected_items.begin(); - LLFolderViewModelItemInventory * viewModel = dynamic_cast((*set_iter)->getViewModelItem()); - if (contains_nocopy_items(viewModel->getUUID())) - { - LLNotificationsUtil::add("ConfirmCopyToMarketplace", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_copySelected, _1, _2, model, root, action)); - return; - } - } - - // Keep track of the marketplace folders that will need update of their status/name after the operation is performed - buildMarketplaceFolders(root); - - if ("rename" == action) - { - root->startRenamingSelectedItem(); - // Update the marketplace listings that have been affected by the operation - updateMarketplaceFolders(); - return; - } - - if ("delete" == action) - { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); - bool marketplacelistings_item = false; - bool has_worn = false; - bool needs_replacement = false; - LLAllDescendentsPassedFilter f; - for (std::set::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it) - { - if (LLFolderViewFolder* folder = dynamic_cast(*it)) - { - folder->applyFunctorRecursively(f); - } - LLFolderViewModelItemInventory * viewModel = dynamic_cast((*it)->getViewModelItem()); - LLUUID obj_id = viewModel->getUUID(); - if (viewModel && gInventory.isObjectDescendentOf(obj_id, marketplacelistings_id)) - { - marketplacelistings_item = true; - break; - } - - LLViewerInventoryCategory* cat = gInventory.getCategory(obj_id); - if (cat) - { - LLInventoryModel::cat_array_t categories; - LLInventoryModel::item_array_t items; - - gInventory.collectDescendents(obj_id, categories, items, false); - - for (LLInventoryModel::item_array_t::value_type& item : items) - { - if (get_is_item_worn(item)) - { - has_worn = true; - LLWearableType::EType type = item->getWearableType(); - if (type == LLWearableType::WT_SHAPE - || type == LLWearableType::WT_SKIN - || type == LLWearableType::WT_HAIR - || type == LLWearableType::WT_EYES) - { - needs_replacement = true; - break; - } - } - } - if (needs_replacement) - { - break; - } - } - LLViewerInventoryItem* item = gInventory.getItem(obj_id); - if (item && get_is_item_worn(item)) - { - has_worn = true; - LLWearableType::EType type = item->getWearableType(); - if (type == LLWearableType::WT_SHAPE - || type == LLWearableType::WT_SKIN - || type == LLWearableType::WT_HAIR - || type == LLWearableType::WT_EYES) - { - needs_replacement = true; - break; - } - } - } - // Fall through to the generic confirmation if the user choose to ignore the specialized one - if (needs_replacement) - { - LLNotificationsUtil::add("CantDeleteRequiredClothing"); - } - else if (has_worn) - { - LLSD payload; - payload["has_worn"] = true; - LLNotificationsUtil::add("DeleteWornItems", LLSD(), payload, boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); - } - else if ( (!f.allDescendentsPassedFilter()) && !marketplacelistings_item && (!LLNotifications::instance().getIgnored("DeleteFilteredItems")) ) - { - LLNotificationsUtil::add("DeleteFilteredItems", LLSD(), LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); - } - else - { - if (!sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session - { - LLNotifications::instance().setIgnored("DeleteItems", false); - sDeleteConfirmationDisplayed = true; - } - - LLSD args; - args["QUESTION"] = LLTrans::getString(root->getSelectedCount() > 1 ? "DeleteItems" : "DeleteItem"); - LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); - } - // Note: marketplace listings will be updated in the callback if delete confirmed - return; - } - if (("copy" == action) || ("cut" == action)) - { - // Clear the clipboard before we start adding things on it - LLClipboard::instance().reset(); - } - if ("replace_links" == action) - { - LLSD params; - if (root->getSelectedCount() == 1) - { - LLFolderViewItem* folder_item = root->getSelectedItems().front(); - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); - - if (bridge) - { - LLInventoryObject* obj = bridge->getInventoryObject(); - if (obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getActualType() != LLAssetType::AT_LINK_FOLDER) - { - params = LLSD(obj->getUUID()); - } - } - } - LLFloaterReg::showInstance("linkreplace", params); - return; - } - - static const std::string change_folder_string = "change_folder_type_"; - if (action.length() > change_folder_string.length() && - (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) - { - LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); - LLFolderViewModelItemInventory* inventory_item = static_cast(root->getViewModelItem()); - LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID()); - if (!cat) return; - cat->changeType(new_folder_type); - // Update the marketplace listings that have been affected by the operation - updateMarketplaceFolders(); - return; - } - - - LLMultiPreview* multi_previewp = NULL; - LLMultiItemProperties* multi_itempropertiesp = nullptr; - - if (("task_open" == action || "open" == action) && selected_items.size() > 1) - { - bool open_multi_preview = true; - - if ("open" == action) - { - for (std::set::iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = *set_iter; - if (folder_item) - { - LLInvFVBridge* bridge = dynamic_cast(folder_item->getViewModelItem()); - if (!bridge || !bridge->isMultiPreviewAllowed()) - { - open_multi_preview = false; - break; - } - } - } - } - - if (open_multi_preview) - { - multi_previewp = new LLMultiPreview(); - gFloaterView->addChild(multi_previewp); - - LLFloater::setFloaterHost(multi_previewp); - } - - } - else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) - { - multi_itempropertiesp = new LLMultiItemProperties("item_properties"); - gFloaterView->addChild(multi_itempropertiesp); - LLFloater::setFloaterHost(multi_itempropertiesp); - } - - std::set selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); - - // copy list of applicable items into a vector for bulk handling - uuid_vec_t ids; - if (action == "wear" || action == "wear_add") - { - const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); - std::copy_if(selected_uuid_set.begin(), - selected_uuid_set.end(), - std::back_inserter(ids), - [trash_id, mp_id](LLUUID id) - { - if (get_is_item_worn(id) - || LLAppearanceMgr::instance().getIsInCOF(id) - || gInventory.isObjectDescendentOf(id, trash_id)) - { - return false; - } - if (mp_id.notNull() && gInventory.isObjectDescendentOf(id, mp_id)) - { - return false; - } - LLInventoryObject* obj = (LLInventoryObject*)gInventory.getObject(id); - if (!obj) - { - return false; - } - if (obj->getIsLinkType() && gInventory.isObjectDescendentOf(obj->getLinkedUUID(), trash_id)) - { - return false; - } - if (obj->getIsLinkType() && LLAssetType::lookupIsLinkType(obj->getType())) - { - // missing - return false; - } - return true; - } - ); - } - else if (isRemoveAction(action)) - { - std::copy_if(selected_uuid_set.begin(), - selected_uuid_set.end(), - std::back_inserter(ids), - [](LLUUID id) - { - return get_is_item_worn(id); - } - ); - } - else - { - for (std::set::iterator it = selected_items.begin(), end_it = selected_items.end(); - it != end_it; - ++it) - { - ids.push_back(static_cast((*it)->getViewModelItem())->getUUID()); - } - } - - // Check for actions that get handled in bulk - if (action == "wear") - { - wear_multiple(ids, true); - } - else if (action == "wear_add") - { - wear_multiple(ids, false); - } - else if (isRemoveAction(action)) - { - LLAppearanceMgr::instance().removeItemsFromAvatar(ids); - } - else if ("save_selected_as" == action) - { - (new LLDirPickerThread(boost::bind(&LLInventoryAction::saveMultipleTextures, _1, selected_items, model), std::string()))->getFile(); - } - else if ("new_folder_from_selected" == action) - { - - LLInventoryObject* first_item = gInventory.getObject(*ids.begin()); - if (!first_item) - { - return; - } - const LLUUID& parent_uuid = first_item->getParentUUID(); - for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) - { - LLInventoryObject *item = gInventory.getObject(*it); - if (!item || item->getParentUUID() != parent_uuid) - { - LLNotificationsUtil::add("SameFolderRequired"); - return; - } - } - - LLSD args; - args["DESC"] = LLTrans::getString("New Folder"); - - LLNotificationsUtil::add("CreateSubfolder", args, LLSD(), - [ids](const LLSD& notification, const LLSD& response) - { - S32 opt = LLNotificationsUtil::getSelectedOption(notification, response); - if (opt == 0) - { - std::string settings_name = response["message"].asString(); - - LLInventoryObject::correctInventoryName(settings_name); - if (settings_name.empty()) - { - settings_name = LLTrans::getString("New Folder"); - } - move_items_to_new_subfolder(ids, settings_name); - } - }); - } - else if ("ungroup_folder_items" == action) - { - if (ids.size() == 1) - { - ungroup_folder_items(*ids.begin()); - } - } - else if ("thumbnail" == action) - { - if (selected_items.size() > 0) - { - LLSD data; - std::set::iterator set_iter; - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = *set_iter; - if (!folder_item) continue; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); - if (!bridge) continue; - data.append(bridge->getUUID()); - } - LLFloaterReg::showInstance("change_item_thumbnail", data); - } - } - else - { - std::set::iterator set_iter; - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = *set_iter; - if(!folder_item) continue; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); - if(!bridge) continue; - bridge->performAction(model, action); - } - if(root->isSingleFolderMode() && selected_items.empty()) - { - LLInvFVBridge* bridge = (LLInvFVBridge*)root->getViewModelItem(); - if(bridge) - { - bridge->performAction(model, action); - } - } - } - - // Update the marketplace listings that have been affected by the operation - updateMarketplaceFolders(); - - LLFloater::setFloaterHost(NULL); - if (multi_previewp) - { - multi_previewp->openFloater(LLSD()); - } - else if (multi_itempropertiesp) - { - multi_itempropertiesp->openFloater(LLSD()); - } -} - -void LLInventoryAction::saveMultipleTextures(const std::vector& filenames, std::set selected_items, LLInventoryModel* model) -{ - gSavedSettings.setString("TextureSaveLocation", filenames[0]); - - LLMultiPreview* multi_previewp = new LLMultiPreview(); - gFloaterView->addChild(multi_previewp); - - LLFloater::setFloaterHost(multi_previewp); - - std::map tex_names_map; - std::set::iterator set_iter; - - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = *set_iter; - if(!folder_item) continue; - LLTextureBridge* bridge = (LLTextureBridge*)folder_item->getViewModelItem(); - if(!bridge) continue; - - std::string tex_name = bridge->getName(); - if(!tex_names_map.insert(std::pair(tex_name, 0)).second) - { - tex_names_map[tex_name]++; - bridge->setFileName(tex_name + llformat("_%.3d", tex_names_map[tex_name])); - } - bridge->performAction(model, "save_selected_as"); - } - - LLFloater::setFloaterHost(NULL); - if (multi_previewp) - { - multi_previewp->openFloater(LLSD()); - } -} - -void LLInventoryAction::removeItemFromDND(LLFolderView* root) -{ - if(gAgent.isDoNotDisturb()) - { - //Get selected items - LLFolderView::selected_items_t selectedItems = root->getSelectedItems(); - LLFolderViewModelItemInventory * viewModel = NULL; - - //If user is in DND and deletes item, make sure the notification is not displayed by removing the notification - //from DND history and .xml file. Once this is done, upon exit of DND mode the item deleted will not show a notification. - for(LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) - { - viewModel = dynamic_cast((*it)->getViewModelItem()); - - if(viewModel && viewModel->getUUID().notNull()) - { - //Will remove the item offer notification - LLDoNotDisturbNotificationStorage::instance().removeNotification(LLDoNotDisturbNotificationStorage::offerName, viewModel->getUUID()); - } - } - } -} - -void LLInventoryAction::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle root) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0 && !root.isDead() && !root.get()->isDead()) - { - bool has_worn = notification["payload"]["has_worn"].asBoolean(); - LLFolderView* folder_root = root.get(); - //Need to remove item from DND before item is removed from root folder view - //because once removed from root folder view the item is no longer a selected item - removeItemFromDND(folder_root); - - // removeSelectedItems will change selection, collect worn items beforehand - uuid_vec_t worn; - uuid_vec_t item_deletion_list; - uuid_vec_t cat_deletion_list; - if (has_worn) - { - //Get selected items - LLFolderView::selected_items_t selectedItems = folder_root->getSelectedItems(); - - //If user is in DND and deletes item, make sure the notification is not displayed by removing the notification - //from DND history and .xml file. Once this is done, upon exit of DND mode the item deleted will not show a notification. - for (LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) - { - LLFolderViewModelItemInventory* viewModel = dynamic_cast((*it)->getViewModelItem()); - - LLUUID obj_id = viewModel->getUUID(); - LLViewerInventoryCategory* cat = gInventory.getCategory(obj_id); - bool cat_has_worn = false; - if (cat) - { - LLInventoryModel::cat_array_t categories; - LLInventoryModel::item_array_t items; - - gInventory.collectDescendents(obj_id, categories, items, false); - - for (LLInventoryModel::item_array_t::value_type& item : items) - { - if (get_is_item_worn(item)) - { - worn.push_back(item->getUUID()); - cat_has_worn = true; - } - } - if (cat_has_worn) - { - cat_deletion_list.push_back(obj_id); - } - } - LLViewerInventoryItem* item = gInventory.getItem(obj_id); - if (item && get_is_item_worn(item)) - { - worn.push_back(obj_id); - item_deletion_list.push_back(obj_id); - } - } - } - - // removeSelectedItems will check if items are worn before deletion, - // don't 'unwear' yet to prevent race conditions from unwearing - // and removing simultaneously - folder_root->removeSelectedItems(); - - // unwear then delete the rest - if (!worn.empty()) - { - // should fire once after every item gets detached - LLAppearanceMgr::instance().removeItemsFromAvatar(worn, - [item_deletion_list, cat_deletion_list]() - { - for (const LLUUID& id : item_deletion_list) - { - remove_inventory_item(id, NULL); - } - for (const LLUUID& id : cat_deletion_list) - { - remove_inventory_category(id, NULL); - } - }); - } - - // Update the marketplace listings that have been affected by the operation - updateMarketplaceFolders(); - } -} - -void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) -{ - // Make a list of all marketplace folders containing the elements in the selected list - // as well as the elements themselves. - // Once those elements are updated (cut, delete in particular but potentially any action), their - // containing folder will need to be updated as well as their initially containing folder. For - // instance, moving a stock folder from a listed folder to another will require an update of the - // target listing *and* the original listing. So we need to keep track of both. - // Note: do not however put the marketplace listings root itself in this list or the whole marketplace data will be rebuilt. - sMarketplaceFolders.clear(); - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); - if (marketplacelistings_id.isNull()) - { - return; - } - - std::set selected_items = root->getSelectionList(); - std::set::iterator set_iter = selected_items.begin(); - LLFolderViewModelItemInventory * viewModel = NULL; - for (; set_iter != selected_items.end(); ++set_iter) - { - viewModel = dynamic_cast((*set_iter)->getViewModelItem()); - if (!viewModel || !viewModel->getInventoryObject()) continue; - if (gInventory.isObjectDescendentOf(viewModel->getInventoryObject()->getParentUUID(), marketplacelistings_id)) - { - const LLUUID &parent_id = viewModel->getInventoryObject()->getParentUUID(); - if (parent_id != marketplacelistings_id) - { - sMarketplaceFolders.push_back(parent_id); - } - const LLUUID &curr_id = viewModel->getInventoryObject()->getUUID(); - if (curr_id != marketplacelistings_id) - { - sMarketplaceFolders.push_back(curr_id); - } - } - } - // Suppress dupes in the list so we won't update listings twice - sMarketplaceFolders.sort(); - sMarketplaceFolders.unique(); -} - -void LLInventoryAction::updateMarketplaceFolders() -{ - while (!sMarketplaceFolders.empty()) - { - update_marketplace_category(sMarketplaceFolders.back()); - sMarketplaceFolders.pop_back(); - } -} - - +/** + * @file llinventoryfunctions.cpp + * @brief Implementation of the inventory view and associated stuff. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include // for std::pair<> + +#include "llinventoryfunctions.h" + +// library includes +#include "llagent.h" +#include "llagentwearables.h" +#include "llcallingcard.h" +#include "llfloaterreg.h" +#include "llinventorydefines.h" +#include "llsdserialize.h" +#include "llfiltereditor.h" +#include "llspinctrl.h" +#include "llui.h" +#include "message.h" + +// newview includes +#include "llappearancemgr.h" +#include "llappviewer.h" +#include "llavataractions.h" +#include "llavatarnamecache.h" +#include "llclipboard.h" +#include "lldirpicker.h" +#include "lldonotdisturbnotificationstorage.h" +#include "llfloatermarketplacelistings.h" +#include "llfloatersidepanelcontainer.h" +#include "llfocusmgr.h" +#include "llfolderview.h" +#include "llgesturemgr.h" +#include "llgiveinventory.h" +#include "lliconctrl.h" +#include "llimview.h" +#include "llinventorybridge.h" +#include "llinventorymodel.h" +#include "llinventorypanel.h" +#include "lllineeditor.h" +#include "llmarketplacenotifications.h" +#include "llmarketplacefunctions.h" +#include "llmenugl.h" +#include "llnotificationsutil.h" +#include "llpanelmaininventory.h" +#include "llpreviewanim.h" +#include "llpreviewgesture.h" +#include "llpreviewnotecard.h" +#include "llpreviewscript.h" +#include "llpreviewsound.h" +#include "llpreviewtexture.h" +#include "llresmgr.h" +#include "llscrollbar.h" +#include "llscrollcontainer.h" +#include "llselectmgr.h" +#include "llsidepanelinventory.h" +#include "lltabcontainer.h" +#include "lltooldraganddrop.h" +#include "lltrans.h" +#include "lluictrlfactory.h" +#include "llviewermenu.h" +#include "llviewermessage.h" +#include "llviewerfoldertype.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewerwindow.h" +#include "llvoavatarself.h" +#include "llwearablelist.h" + +bool LLInventoryState::sWearNewClothing = false; +LLUUID LLInventoryState::sWearNewClothingTransactionID; +std::list LLInventoryAction::sMarketplaceFolders; +bool LLInventoryAction::sDeleteConfirmationDisplayed = false; + +// Helper function : callback to update a folder after inventory action happened in the background +void update_folder_cb(const LLUUID& dest_folder) +{ + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + gInventory.updateCategory(dest_cat); + gInventory.notifyObservers(); +} + +// Helper function : Count only the copyable items, i.e. skip the stock items (which are no copy) +S32 count_copyable_items(LLInventoryModel::item_array_t& items) +{ + S32 count = 0; + for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + LLViewerInventoryItem* item = *it; + if (item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + count++; + } + } + return count; +} + +// Helper function : Count only the non-copyable items, i.e. the stock items, skip the others +S32 count_stock_items(LLInventoryModel::item_array_t& items) +{ + S32 count = 0; + for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + LLViewerInventoryItem* item = *it; + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + count++; + } + } + return count; +} + +// Helper function : Count the number of stock folders +S32 count_stock_folders(LLInventoryModel::cat_array_t& categories) +{ + S32 count = 0; + for (LLInventoryModel::cat_array_t::const_iterator it = categories.begin(); it != categories.end(); ++it) + { + LLInventoryCategory* cat = *it; + if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + count++; + } + } + return count; +} + +// Helper funtion : Count the number of items (not folders) in the descending hierarchy +S32 count_descendants_items(const LLUUID& cat_id) +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); + + S32 count = item_array->size(); + + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* category = *iter; + count += count_descendants_items(category->getUUID()); + } + + return count; +} + +// Helper function : Returns true if the hierarchy contains nocopy items +bool contains_nocopy_items(const LLUUID& id) +{ + LLInventoryCategory* cat = gInventory.getCategory(id); + + if (cat) + { + // Get the content + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(id,cat_array,item_array); + + // Check all the items: returns true upon encountering a nocopy item + for (LLInventoryModel::item_array_t::iterator iter = item_array->begin(); iter != item_array->end(); iter++) + { + LLInventoryItem* item = *iter; + LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return true; + } + } + + // Check all the sub folders recursively + for (LLInventoryModel::cat_array_t::iterator iter = cat_array->begin(); iter != cat_array->end(); iter++) + { + LLViewerInventoryCategory* cat = *iter; + if (contains_nocopy_items(cat->getUUID())) + { + return true; + } + } + } + else + { + LLInventoryItem* item = gInventory.getItem(id); + LLViewerInventoryItem * inv_item = (LLViewerInventoryItem *) item; + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return true; + } + } + + // Exit without meeting a nocopy item + return false; +} + +// Generates a string containing the path to the item specified by id. +void append_path(const LLUUID& id, std::string& path) +{ + std::string temp; + const LLInventoryObject* obj = gInventory.getObject(id); + LLUUID parent_id; + if(obj) parent_id = obj->getParentUUID(); + std::string forward_slash("/"); + while(obj) + { + obj = gInventory.getCategory(parent_id); + if(obj) + { + temp.assign(forward_slash + obj->getName() + temp); + parent_id = obj->getParentUUID(); + } + } + path.append(temp); +} + +// Generates a string containing the path name of the object. +std::string make_path(const LLInventoryObject* object) +{ + std::string path; + append_path(object->getUUID(), path); + return path + "/" + object->getName(); +} + +// Generates a string containing the path name of the object specified by id. +std::string make_inventory_path(const LLUUID& id) +{ + if (LLInventoryObject* object = gInventory.getObject(id)) + return make_path(object); + return ""; +} + +// Generates a string containing the path name and id of the object. +std::string make_info(const LLInventoryObject* object) +{ + return "'" + make_path(object) + "' (" + object->getUUID().asString() + ")"; +} + +// Generates a string containing the path name and id of the object specified by id. +std::string make_inventory_info(const LLUUID& id) +{ + if (LLInventoryObject* object = gInventory.getObject(id)) + return make_info(object); + return " (" + id.asString() + ")"; +} + +void update_marketplace_folder_hierarchy(const LLUUID cat_id) +{ + // When changing the marketplace status of a folder, the only thing that needs to happen is + // for all observers of the folder to, possibly, change the display label of the folder + // so that's the only thing we change on the update mask. + gInventory.addChangedMask(LLInventoryObserver::LABEL, cat_id); + + // Update all descendent folders down + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); + + 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++) + { + LLInventoryCategory* category = *iter; + update_marketplace_folder_hierarchy(category->getUUID()); + } + return; +} + +void update_marketplace_category(const LLUUID& cur_uuid, bool perform_consistency_enforcement, bool skip_clear_listing) +{ + // When changing the marketplace status of an item, we usually have to change the status of all + // folders in the same listing. This is because the display of each folder is affected by the + // overall status of the whole listing. + // Consequently, the only way to correctly update an item anywhere in the marketplace is to + // update the whole listing from its listing root. + // This is not as bad as it seems as we only update folders, not items, and the folder nesting depth + // is limited to 4. + // We also take care of degenerated cases so we don't update all folders in the inventory by mistake. + + if (cur_uuid.isNull() + || gInventory.getCategory(cur_uuid) == NULL + || gInventory.getCategory(cur_uuid)->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + return; + } + + // Grab marketplace listing data for this item + S32 depth = depth_nesting_in_marketplace(cur_uuid); + if (depth > 0) + { + // Retrieve the listing uuid this object is in + LLUUID listing_uuid = nested_parent_id(cur_uuid, depth); + LLViewerInventoryCategory* listing_cat = gInventory.getCategory(listing_uuid); + bool listing_cat_loaded = listing_cat != NULL && listing_cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; + + // Verify marketplace data consistency for this listing + if (perform_consistency_enforcement + && listing_cat_loaded + && LLMarketplaceData::instance().isListed(listing_uuid)) + { + LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); + S32 version_depth = depth_nesting_in_marketplace(version_folder_uuid); + if (version_folder_uuid.notNull() && (!gInventory.isObjectDescendentOf(version_folder_uuid, listing_uuid) || (version_depth != 2))) + { + LL_INFOS("SLM") << "Unlist and clear version folder as the version folder is not at the right place anymore!!" << LL_ENDL; + LLMarketplaceData::instance().setVersionFolder(listing_uuid, LLUUID::null,1); + } + else if (version_folder_uuid.notNull() + && gInventory.isCategoryComplete(version_folder_uuid) + && LLMarketplaceData::instance().getActivationState(version_folder_uuid) + && (count_descendants_items(version_folder_uuid) == 0) + && !LLMarketplaceData::instance().isUpdating(version_folder_uuid,version_depth)) + { + LL_INFOS("SLM") << "Unlist as the version folder is empty of any item!!" << LL_ENDL; + LLNotificationsUtil::add("AlertMerchantVersionFolderEmpty"); + LLMarketplaceData::instance().activateListing(listing_uuid, false,1); + } + } + + // Check if the count on hand needs to be updated on SLM + if (perform_consistency_enforcement + && listing_cat_loaded + && (compute_stock_count(listing_uuid) != LLMarketplaceData::instance().getCountOnHand(listing_uuid))) + { + LLMarketplaceData::instance().updateCountOnHand(listing_uuid,1); + } + // Update all descendents starting from the listing root + update_marketplace_folder_hierarchy(listing_uuid); + } + else if (depth == 0) + { + // If this is the marketplace listings root itself, update all descendents + if (gInventory.getCategory(cur_uuid)) + { + update_marketplace_folder_hierarchy(cur_uuid); + } + } + else + { + // If the folder is outside the marketplace listings root, clear its SLM data if needs be + if (perform_consistency_enforcement && !skip_clear_listing && LLMarketplaceData::instance().isListed(cur_uuid)) + { + LL_INFOS("SLM") << "Disassociate as the listing folder is not under the marketplace folder anymore!!" << LL_ENDL; + LLMarketplaceData::instance().clearListing(cur_uuid); + } + // Update all descendents if this is a category + if (gInventory.getCategory(cur_uuid)) + { + update_marketplace_folder_hierarchy(cur_uuid); + } + } + + return; +} + +// Iterate through the marketplace and flag for label change all categories that countain a stock folder (i.e. stock folders and embedding folders up the hierarchy) +void update_all_marketplace_count(const LLUUID& cat_id) +{ + // Get all descendent folders down + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); + + 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++) + { + LLInventoryCategory* category = *iter; + if (category->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + // Listing containing stock folders needs to be updated but not others + // Note: we take advantage of the fact that stock folder *do not* contain sub folders to avoid a recursive call here + update_marketplace_category(category->getUUID()); + } + else + { + // Explore the contained folders recursively + update_all_marketplace_count(category->getUUID()); + } + } +} + +void update_all_marketplace_count() +{ + // Get the marketplace root and launch the recursive exploration + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (!marketplace_listings_uuid.isNull()) + { + update_all_marketplace_count(marketplace_listings_uuid); + } + return; +} + +void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name) +{ + LLViewerInventoryCategory* cat; + + if (!model || + !get_is_category_renameable(model, cat_id) || + (cat = model->getCategory(cat_id)) == NULL || + cat->getName() == new_name) + { + return; + } + + LLSD updates; + updates["name"] = new_name; + update_inventory_category(cat_id, updates, NULL); +} + +void copy_inventory_category(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& parent_id, + const LLUUID& root_copy_id, + bool move_no_copy_items) +{ + // Create the initial folder + inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items](const LLUUID& new_id) + { + copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); + }; + gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); +} + +void copy_inventory_category(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& parent_id, + const LLUUID& root_copy_id, + bool move_no_copy_items, + inventory_func_type callback) +{ + // Create the initial folder + inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items, callback](const LLUUID &new_id) + { + copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); + if (callback) + { + callback(new_id); + } + }; + gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); +} + +void copy_cb(const LLUUID& dest_folder, const LLUUID& root_id) +{ + // Decrement the count in root_id since that one item won't be copied over + LLMarketplaceData::instance().decrementValidationWaiting(root_id); + update_folder_cb(dest_folder); +}; + +void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items) +{ + model->notifyObservers(); + + // We need to exclude the initial root of the copy to avoid recursively copying the copy, etc... + LLUUID root_id = (root_copy_id.isNull() ? new_cat_uuid : root_copy_id); + + // Get the content of the folder + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(), cat_array, item_array); + + // If root_copy_id is null, tell the marketplace model we'll be waiting for new items to be copied over for this folder + if (root_copy_id.isNull()) + { + LLMarketplaceData::instance().setValidationWaiting(root_id, count_descendants_items(cat->getUUID())); + } + + LLPointer cb; + if (root_copy_id.isNull()) + { + cb = new LLBoostFuncInventoryCallback(boost::bind(copy_cb, new_cat_uuid, root_id)); + } + else + { + cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_uuid)); + } + + // Copy all the items + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + + if (item->getIsLinkType()) + { + link_inventory_object(new_cat_uuid, item->getLinkedUUID(), cb); + } + else if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + // If the item is nocopy, we do nothing or, optionally, move it + if (move_no_copy_items) + { + // Reparent the item + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *)item; + gInventory.changeItemParent(viewer_inv_item, new_cat_uuid, true); + } + if (root_copy_id.isNull()) + { + // Decrement the count in root_id since that one item won't be copied over + LLMarketplaceData::instance().decrementValidationWaiting(root_id); + } + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + new_cat_uuid, + std::string(), + cb); + } + } + + // Copy all the folders + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* category = *iter; + if (category->getUUID() != root_id) + { + copy_inventory_category(model, category, new_cat_uuid, root_id, move_no_copy_items); + } + } +} + +class LLInventoryCollectAllItems : public LLInventoryCollectFunctor +{ +public: + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + return true; + } +}; + +bool get_is_parent_to_worn_item(const LLUUID& id) +{ + const LLViewerInventoryCategory* cat = gInventory.getCategory(id); + if (!cat) + { + return false; + } + + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLInventoryCollectAllItems collect_all; + gInventory.collectDescendentsIf(LLAppearanceMgr::instance().getCOF(), cats, items, LLInventoryModel::EXCLUDE_TRASH, collect_all); + + for (LLInventoryModel::item_array_t::const_iterator it = items.begin(); it != items.end(); ++it) + { + const LLViewerInventoryItem * const item = *it; + + llassert(item->getIsLinkType()); + + LLUUID linked_id = item->getLinkedUUID(); + const LLViewerInventoryItem * const linked_item = gInventory.getItem(linked_id); + + if (linked_item) + { + LLUUID parent_id = linked_item->getParentUUID(); + + while (!parent_id.isNull()) + { + LLInventoryCategory * parent_cat = gInventory.getCategory(parent_id); + + if (cat == parent_cat) + { + return true; + } + + parent_id = parent_cat->getParentUUID(); + } + } + } + + return false; +} + +bool get_is_item_worn(const LLUUID& id, const LLViewerInventoryItem* item) +{ + if (!item) + return false; + + if (item->getIsLinkType() && !gInventory.getItem(item->getLinkedUUID())) + { + return false; + } + + // Consider the item as worn if it has links in COF. + if (LLAppearanceMgr::instance().isLinkedInCOF(id)) + { + return true; + } + + switch(item->getType()) + { + case LLAssetType::AT_OBJECT: + { + if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) + return true; + break; + } + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + if(gAgentWearables.isWearingItem(item->getLinkedUUID())) + return true; + break; + case LLAssetType::AT_GESTURE: + if (LLGestureMgr::instance().isGestureActive(item->getLinkedUUID())) + return true; + break; + default: + break; + } + return false; +} + +bool get_is_item_worn(const LLUUID& id) +{ + const LLViewerInventoryItem* item = gInventory.getItem(id); + return get_is_item_worn(id, item); +} + +bool get_is_item_worn(const LLViewerInventoryItem* item) +{ + if (!item) + { + return false; + } + return get_is_item_worn(item->getUUID(), item); +} + +bool get_can_item_be_worn(const LLUUID& id) +{ + const LLViewerInventoryItem* item = gInventory.getItem(id); + if (!item) + return false; + + if (LLAppearanceMgr::instance().isLinkedInCOF(item->getLinkedUUID())) + { + // an item having links in COF (i.e. a worn item) + return false; + } + + if (gInventory.isObjectDescendentOf(id, LLAppearanceMgr::instance().getCOF())) + { + // a non-link object in COF (should not normally happen) + return false; + } + + const LLUUID trash_id = gInventory.findCategoryUUIDForType( + LLFolderType::FT_TRASH); + + // item can't be worn if base obj in trash, see EXT-7015 + if (gInventory.isObjectDescendentOf(item->getLinkedUUID(), + trash_id)) + { + return false; + } + + switch(item->getType()) + { + case LLAssetType::AT_OBJECT: + { + if (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item->getLinkedUUID())) + { + // Already being worn + return false; + } + else + { + // Not being worn yet. + return true; + } + break; + } + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + if(gAgentWearables.isWearingItem(item->getLinkedUUID())) + { + // Already being worn + return false; + } + else + { + // Not being worn yet. + return true; + } + break; + default: + break; + } + return false; +} + +bool get_is_item_removable(const LLInventoryModel* model, const LLUUID& id, bool check_worn) +{ + if (!model) + { + return false; + } + + // Can't delete an item that's in the library. + if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) + { + return false; + } + + // Disable delete from COF folder; have users explicitly choose "detach/take off", + // unless the item is not worn but in the COF (i.e. is bugged). + const LLViewerInventoryItem* obj = model->getItem(id); + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(obj)) + { + if (get_is_item_worn(id, obj)) + { + return false; + } + } + + if (obj && obj->getIsLinkType()) + { + return true; + } + if (check_worn && get_is_item_worn(id, obj)) + { + return false; + } + return true; +} + +bool get_is_item_editable(const LLUUID& inv_item_id) +{ + if (const LLInventoryItem* inv_item = gInventory.getLinkedItem(inv_item_id)) + { + switch (inv_item->getType()) + { + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + return gAgentWearables.isWearableModifiable(inv_item_id); + case LLAssetType::AT_OBJECT: + return true; + default: + return false;; + } + } + return gAgentAvatarp->getWornAttachment(inv_item_id) != nullptr; +} + +void handle_item_edit(const LLUUID& inv_item_id) +{ + if (get_is_item_editable(inv_item_id)) + { + if (const LLInventoryItem* inv_item = gInventory.getLinkedItem(inv_item_id)) + { + switch (inv_item->getType()) + { + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + LLAgentWearables::editWearable(inv_item_id); + break; + case LLAssetType::AT_OBJECT: + handle_attachment_edit(inv_item_id); + break; + default: + break; + } + } + else + { + handle_attachment_edit(inv_item_id); + } + } +} + +bool get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) +{ + // NOTE: This function doesn't check the folder's children. + // See LLFolderBridge::isItemRemovable for a function that does + // consider the children. + + if (!model) + { + return false; + } + + if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) + { + return false; + } + + if (!isAgentAvatarValid()) return false; + + const LLInventoryCategory* category = model->getCategory(id); + if (!category) + { + return false; + } + + const LLFolderType::EType folder_type = category->getPreferredType(); + + if (LLFolderType::lookupIsProtectedType(folder_type)) + { + return false; + } + + // Can't delete the outfit that is currently being worn. + if (folder_type == LLFolderType::FT_OUTFIT) + { + const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); + if (base_outfit_link && (category == base_outfit_link->getLinkedCategory())) + { + return false; + } + } + + return true; +} + +bool get_is_category_and_children_removable(LLInventoryModel* model, const LLUUID& folder_id, bool check_worn) +{ + if (!get_is_category_removable(model, folder_id)) + { + return false; + } + + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (mp_id.notNull() && gInventory.isObjectDescendentOf(folder_id, mp_id)) + { + return false; + } + + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + model->collectDescendents( + folder_id, + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + + if (check_worn) + { + for (LLInventoryModel::item_array_t::value_type& item : item_array) + { + // Disable delete/cut from COF folder; have users explicitly choose "detach/take off", + // unless the item is not worn but in the COF (i.e. is bugged). + if (item) + { + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(item)) + { + if (get_is_item_worn(item)) + { + return false; + } + } + + if (!item->getIsLinkType() && get_is_item_worn(item)) + { + return false; + } + } + } + } + + const LLViewerInventoryItem* base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); + LLViewerInventoryCategory* outfit_linked_category = base_outfit_link ? base_outfit_link->getLinkedCategory() : nullptr; + for (LLInventoryModel::cat_array_t::value_type& cat : cat_array) + { + const LLFolderType::EType folder_type = cat->getPreferredType(); + if (LLFolderType::lookupIsProtectedType(folder_type)) + { + return false; + } + + // Can't delete the outfit that is currently being worn. + if (folder_type == LLFolderType::FT_OUTFIT) + { + if (cat == outfit_linked_category) + { + return false; + } + } + } + + return true; +} + +bool get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) +{ + if (!model) + { + return false; + } + + LLViewerInventoryCategory* cat = model->getCategory(id); + + if (cat && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) && + cat->getOwnerID() == gAgent.getID()) + { + return true; + } + return false; +} + +void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id) +{ + LLSD params; + params["id"] = item_uuid; + params["object"] = object_id; + + LLFloaterReg::showInstance("item_properties", params); +} + +void show_item_profile(const LLUUID& item_uuid) +{ + LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); + LLFloaterReg::showInstance("item_properties", LLSD().with("id", linked_uuid)); +} + +void show_item_original(const LLUUID& item_uuid) +{ + static LLUICachedControl find_original_new_floater("FindOriginalOpenWindow", false); + + //show in a new single-folder window + if(find_original_new_floater) + { + const LLUUID& linked_item_uuid = gInventory.getLinkedItemID(item_uuid); + const LLInventoryObject *obj = gInventory.getObject(linked_item_uuid); + if (obj && obj->getParentUUID().notNull()) + { + LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), linked_item_uuid); + } + } + //show in main Inventory + else + { + LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + if (!floater_inventory) + { + LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; + return; + } + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory) + { + if(main_inventory->isSingleFolderMode()) + { + main_inventory->toggleViewMode(); + } + main_inventory->resetAllItemsFilters(); + } + reset_inventory_filter(); + + if (!LLFloaterReg::getTypedInstance("inventory")->isInVisibleChain()) + { + LLFloaterReg::toggleInstanceOrBringToFront("inventory"); + } + + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); + if (gInventory.isObjectDescendentOf(gInventory.getLinkedItemID(item_uuid), inbox_id)) + { + if (sidepanel_inventory->getInboxPanel()) + { + sidepanel_inventory->openInbox(); + sidepanel_inventory->getInboxPanel()->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_YES); + } + } + else + { + sidepanel_inventory->selectAllItemsPanel(); + if (sidepanel_inventory->getActivePanel()) + { + sidepanel_inventory->getActivePanel()->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_YES); + } + } + } + } +} + + +void reset_inventory_filter() +{ + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory) + { + main_inventory->onFilterEdit(""); + } + } +} + +void open_marketplace_listings() +{ + LLFloaterReg::showInstance("marketplace_listings"); +} + +///---------------------------------------------------------------------------- +// Marketplace functions +// +// Handles Copy and Move to or within the Marketplace listings folder. +// Handles creation of stock folders, nesting of listings and version folders, +// permission checking and listings validation. +///---------------------------------------------------------------------------- + +S32 depth_nesting_in_marketplace(LLUUID cur_uuid) +{ + // Get the marketplace listings root, exit with -1 (i.e. not under the marketplace listings root) if none + // Todo: findCategoryUUIDForType is somewhat expensive with large + // flat root folders yet we use depth_nesting_in_marketplace at + // every turn, find a way to correctly cache this id. + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (marketplace_listings_uuid.isNull()) + { + return -1; + } + // If not a descendant of the marketplace listings root, then the nesting depth is -1 by definition + if (!gInventory.isObjectDescendentOf(cur_uuid, marketplace_listings_uuid)) + { + return -1; + } + + // Iterate through the parents till we hit the marketplace listings root + // Note that the marketplace listings root itself will return 0 + S32 depth = 0; + LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); + while (cur_uuid != marketplace_listings_uuid) + { + depth++; + cur_uuid = cur_object->getParentUUID(); + cur_object = gInventory.getCategory(cur_uuid); + } + return depth; +} + +// Returns the UUID of the marketplace listing this object is in +LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth) +{ + if (depth < 1) + { + // For objects outside the marketplace listings root (or root itself), we return a NULL UUID + return LLUUID::null; + } + else if (depth == 1) + { + // Just under the root, we return the passed UUID itself if it's a folder, NULL otherwise (not a listing) + LLViewerInventoryCategory* cat = gInventory.getCategory(cur_uuid); + return (cat ? cur_uuid : LLUUID::null); + } + + // depth > 1 + LLInventoryObject* cur_object = gInventory.getObject(cur_uuid); + while (depth > 1) + { + depth--; + cur_uuid = cur_object->getParentUUID(); + cur_object = gInventory.getCategory(cur_uuid); + } + return cur_uuid; +} + +S32 compute_stock_count(LLUUID cat_uuid, bool force_count /* false */) +{ + // Handle the case of the folder being a stock folder immediately + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); + if (!cat) + { + // Not a category so no stock count to speak of + return COMPUTE_STOCK_INFINITE; + } + if (cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + // If the folder is not completely fetched, we do not want to return any confusing value that could lead to unlisting + // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder has a count that cannot be evaluated at this time (folder not up to date) + return COMPUTE_STOCK_NOT_EVALUATED; + } + // Note: stock folders are *not* supposed to have nested subfolders so we stop recursion here but we count only items (subfolders will be ignored) + // Note: we *always* give a stock count for stock folders, it's useful even if the listing is unassociated + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); + return item_array->size(); + } + + // When force_count is true, we do not do any verification of the marketplace status and simply compute + // the stock amount based on the descendent hierarchy. This is used specifically when creating a listing. + if (!force_count) + { + // Grab marketplace data for this folder + S32 depth = depth_nesting_in_marketplace(cat_uuid); + LLUUID listing_uuid = nested_parent_id(cat_uuid, depth); + if (!LLMarketplaceData::instance().isListed(listing_uuid)) + { + // If not listed, the notion of stock is meaningless so it won't be computed for any level + return COMPUTE_STOCK_INFINITE; + } + + LLUUID version_folder_uuid = LLMarketplaceData::instance().getVersionFolder(listing_uuid); + // Handle the case of the first 2 levels : listing and version folders + if (depth == 1) + { + if (version_folder_uuid.notNull()) + { + // If there is a version folder, the stock value for the listing is the version folder stock + return compute_stock_count(version_folder_uuid, true); + } + else + { + // If there's no version folder associated, the notion of stock count has no meaning + return COMPUTE_STOCK_INFINITE; + } + } + else if (depth == 2) + { + if (version_folder_uuid.notNull() && (version_folder_uuid != cat_uuid)) + { + // If there is a version folder but we're not it, our stock count is meaningless + return COMPUTE_STOCK_INFINITE; + } + } + } + + // In all other cases, the stock count is the min of stock folders count found in the descendents + // "COMPUTE_STOCK_NOT_EVALUATED" denotes that a stock folder in the hierarchy has a count that cannot be evaluated at this time (folder not up to date) + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); + + // "COMPUTE_STOCK_INFINITE" denotes a folder that doesn't countain any stock folders in its descendents + S32 curr_count = COMPUTE_STOCK_INFINITE; + + // Note: marketplace listings have a maximum depth nesting of 4 + 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++) + { + LLInventoryCategory* category = *iter; + S32 count = compute_stock_count(category->getUUID(), true); + if ((curr_count == COMPUTE_STOCK_INFINITE) || ((count != COMPUTE_STOCK_INFINITE) && (count < curr_count))) + { + curr_count = count; + } + } + + return curr_count; +} + +// local helper +bool can_move_to_marketplace(LLInventoryItem* inv_item, std::string& tooltip_msg, bool resolve_links) +{ + // Collapse links directly to items/folders + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); + LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); + + // Linked items and folders cannot be put for sale + if (linked_category || linked_item) + { + tooltip_msg = LLTrans::getString("TooltipOutboxLinked"); + return false; + } + + // A category is always considered as passing... + if (linked_category != NULL) + { + return true; + } + + // Take the linked item if necessary + if (linked_item != NULL) + { + inv_item = linked_item; + } + + // Check that the agent has transfer permission on the item: this is required as a resident cannot + // put on sale items she cannot transfer. Proceed with move if we have permission. + bool allow_transfer = inv_item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + if (!allow_transfer) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNoTransfer"); + return false; + } + + // Check worn/not worn status: worn items cannot be put on the marketplace + bool worn = get_is_item_worn(inv_item->getUUID()); + if (worn) + { + tooltip_msg = LLTrans::getString("TooltipOutboxWorn"); + return false; + } + + // Check library status: library items cannot be put on the marketplace + if (!gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.getRootFolderID())) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + return false; + } + + // Check type: for the moment, calling cards cannot be put on the marketplace + bool calling_card = (LLAssetType::AT_CALLINGCARD == inv_item->getType()); + if (calling_card) + { + tooltip_msg = LLTrans::getString("TooltipOutboxCallingCard"); + return false; + } + + return true; +} + +// local helper +// Returns the max tree length (in folder nodes) down from the argument folder +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->size(); ++i) + { + LLInventoryCategory* category = cats->at(i); + max_child_levels = llmax(max_child_levels, get_folder_levels(category)); + } + + return 1 + max_child_levels; +} + +// local helper +// Returns the distance (in folder nodes) between the ancestor and its descendant. Returns -1 if not related. +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); + } + + LL_WARNS("SLM") << "get_folder_path_length() couldn't trace a path from the descendant to the ancestor" << LL_ENDL; + return -1; +} + +// local helper +// Returns true if all items within the argument folder are fit for sale, false otherwise +bool has_correct_permissions_for_sale(LLInventoryCategory* cat, std::string& error_msg) +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + + LLInventoryModel::item_array_t item_array_copy = *item_array; + + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + if (!can_move_to_marketplace(item, error_msg, false)) + { + return false; + } + } + + 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++) + { + LLInventoryCategory* category = *iter; + if (!has_correct_permissions_for_sale(category, error_msg)) + { + return false; + } + } + return true; +} + +// Returns true if inv_item can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size, bool from_paste) +{ + // Check stock folder type matches item type in marketplace listings or merchant outbox (even if of no use there for the moment) + LLViewerInventoryCategory* view_folder = dynamic_cast(dest_folder); + bool move_in_stock = (view_folder && (view_folder->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)); + bool accept = (view_folder && view_folder->acceptItem(inv_item)); + if (!accept) + { + tooltip_msg = LLTrans::getString("TooltipOutboxMixedStock"); + } + + // Check that the item has the right type and permissions to be sold on the marketplace + if (accept) + { + accept = can_move_to_marketplace(inv_item, tooltip_msg, true); + } + + // Check that the total amount of items won't violate the max limit on the marketplace + if (accept) + { + // If the dest folder is a stock folder, we do not count the incoming items toward the total (stock items are seen as one) + int existing_item_count = (move_in_stock ? 0 : bundle_size); + + // If the dest folder is a stock folder, we do assume that the incoming items are also stock items (they should anyway) + int existing_stock_count = (move_in_stock ? bundle_size : 0); + + int existing_folder_count = 0; + + // Get the version folder: that's where the counts start from + const LLViewerInventoryCategory * version_folder = ((root_folder && (root_folder != dest_folder)) ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); + + if (version_folder) + { + if (!from_paste && gInventory.isObjectDescendentOf(inv_item->getUUID(), version_folder->getUUID())) + { + // Clear those counts or they will be counted twice because we're already inside the version category + existing_item_count = 0; + } + + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, false); + + existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + existing_stock_count += count_stock_items(existing_items); + existing_folder_count += existing_categories.size(); + + // If the incoming item is a nocopy (stock) item, we need to consider that it will create a stock folder + if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && !move_in_stock) + { + // Note : we do not assume that all incoming items are nocopy of different kinds... + existing_folder_count += 1; + } + } + + if (existing_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); + accept = false; + } + else if (existing_stock_count > gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); + accept = false; + } + else if (existing_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); + accept = false; + } + } + + return accept; +} + +// Returns true if inv_cat can be dropped in dest_folder, a folder nested in marketplace listings (or merchant inventory) under the root_folder root +// If returns is false, tooltip_msg contains an error message to display to the user (localized and all). +// bundle_size is the amount of sibling items that are getting moved to the marketplace at the same time. +bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size, bool check_items, bool from_paste) +{ + bool accept = true; + + // Compute the nested folders level we'll add into with that incoming folder + int incoming_folder_depth = get_folder_levels(inv_cat); + // Compute the nested folders level we're inserting ourselves in + // Note: add 1 when inserting under a listing folder as we need to take the root listing folder in the count + int insertion_point_folder_depth = (root_folder ? get_folder_path_length(root_folder->getUUID(), dest_folder->getUUID()) + 1 : 1); + + // Get the version folder: that's where the folders and items counts start from + const LLViewerInventoryCategory * version_folder = (insertion_point_folder_depth >= 2 ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); + + // Compare the whole with the nested folders depth limit + // Note: substract 2 as we leave root and version folder out of the count threshold + if ((incoming_folder_depth + insertion_point_folder_depth - 2) > (S32)(gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"))) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderDepth"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxFolderLevels", args); + accept = false; + } + + if (accept) + { + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, false); + + int dragged_folder_count = descendent_categories.size() + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... + int dragged_item_count = count_copyable_items(descendent_items) + count_stock_folders(descendent_categories); + int dragged_stock_count = count_stock_items(descendent_items); + int existing_item_count = 0; + int existing_stock_count = 0; + int existing_folder_count = 0; + + if (version_folder) + { + if (!from_paste && gInventory.isObjectDescendentOf(inv_cat->getUUID(), version_folder->getUUID())) + { + // Clear those counts or they will be counted twice because we're already inside the version category + dragged_folder_count = 0; + dragged_item_count = 0; + dragged_stock_count = 0; + } + + // Tally the total number of categories and items inside the root folder + LLInventoryModel::cat_array_t existing_categories; + LLInventoryModel::item_array_t existing_items; + gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, false); + + existing_folder_count += existing_categories.size(); + existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); + existing_stock_count += count_stock_items(existing_items); + } + + const int total_folder_count = existing_folder_count + dragged_folder_count; + const int total_item_count = existing_item_count + dragged_item_count; + const int total_stock_count = existing_stock_count + dragged_stock_count; + + if (total_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxFolderCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyFolders", args); + accept = false; + } + else if (total_item_count > gSavedSettings.getU32("InventoryOutboxMaxItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyObjects", args); + accept = false; + } + else if (total_stock_count > gSavedSettings.getU32("InventoryOutboxMaxStockItemCount")) + { + LLStringUtil::format_map_t args; + U32 amount = gSavedSettings.getU32("InventoryOutboxMaxStockItemCount"); + args["[AMOUNT]"] = llformat("%d",amount); + tooltip_msg = LLTrans::getString("TooltipOutboxTooManyStockItems", args); + accept = false; + } + + // Now check that each item in the folder can be moved in the marketplace + if (accept && check_items) + { + for (S32 i=0; i < descendent_items.size(); ++i) + { + LLInventoryItem* item = descendent_items[i]; + if (!can_move_to_marketplace(item, tooltip_msg, false)) + { + accept = false; + break; + } + } + } + } + + return accept; +} + +// Can happen asynhroneously!!! +bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy) +{ + // Get the marketplace listings depth of the destination folder, exit with error if not under marketplace + S32 depth = depth_nesting_in_marketplace(dest_folder); + if (depth < 0) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Merchant"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // We will collapse links into items/folders + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); + + if (linked_category != NULL) + { + // Move the linked folder directly + return move_folder_to_marketplacelistings(linked_category, dest_folder, copy); + } + else + { + // Grab the linked item if any + LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); + viewer_inv_item = (linked_item != NULL ? linked_item : viewer_inv_item); + + // If we want to copy but the item is no copy, fail silently (this is a common case that doesn't warrant notification) + if (copy && !viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + return false; + } + + // Check that the agent has transfer permission on the item: this is required as a resident cannot + // put on sale items she cannot transfer. Proceed with move if we have permission. + std::string error_msg; + if (can_move_to_marketplace(inv_item, error_msg, true)) + { + // When moving an isolated item, we might need to create the folder structure to support it + + LLUUID item_id = inv_item->getUUID(); + std::function callback_create_stock = [copy, item_id](const LLUUID& new_cat_id) + { + if (new_cat_id.isNull()) + { + LL_WARNS() << "Failed to create category" << LL_ENDL; + LLSD subs; + subs["[ERROR_CODE]"] = + LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + + // Verify we can have this item in that destination category + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); + LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); + if (!dest_cat || !viewer_inv_item) + { + LL_WARNS() << "Move to marketplace: item or folder do not exist" << LL_ENDL; + + LLSD subs; + subs["[ERROR_CODE]"] = + LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + if (!dest_cat->acceptItem(viewer_inv_item)) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + } + + if (copy) + { + // Copy the item + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_id)); + copy_inventory_item( + gAgent.getID(), + viewer_inv_item->getPermissions().getOwner(), + viewer_inv_item->getUUID(), + new_cat_id, + std::string(), + cb); + } + else + { + // Reparent the item + gInventory.changeItemParent(viewer_inv_item, new_cat_id, true); + } + }; + + std::function callback_dest_create = [item_id, callback_create_stock](const LLUUID& new_cat_id) + { + if (new_cat_id.isNull()) + { + LL_WARNS() << "Failed to create category" << LL_ENDL; + LLSD subs; + subs["[ERROR_CODE]"] = + LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); + LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && + (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // We need to create a stock folder to move a no copy item + gInventory.createNewCategory(new_cat_id, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName(), callback_create_stock); + } + else + { + callback_create_stock(new_cat_id); + } + }; + + if (depth == 0) + { + // We need a listing folder + gInventory.createNewCategory(dest_folder, + LLFolderType::FT_NONE, + viewer_inv_item->getName(), + [callback_dest_create](const LLUUID &new_cat_id) + { + if (new_cat_id.isNull()) + { + LL_WARNS() << "Failed to create listing folder for marketpace" << LL_ENDL; + return; + } + LLViewerInventoryCategory *dest_cat = gInventory.getCategory(new_cat_id); + if (!dest_cat) + { + LL_WARNS() << "Failed to find freshly created listing folder" << LL_ENDL; + return; + } + // version folder + gInventory.createNewCategory(new_cat_id, + LLFolderType::FT_NONE, + dest_cat->getName(), + callback_dest_create); + }); + } + else if (depth == 1) + { + // We need a version folder + gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create); + } + else + { + callback_dest_create(dest_folder); + } + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + } + + open_marketplace_listings(); + return true; +} + +bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy, bool move_no_copy_items) +{ + // Check that we have adequate permission on all items being moved. Proceed if we do. + std::string error_msg; + if (has_correct_permissions_for_sale(inv_cat, error_msg)) + { + // Get the destination folder + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); + + // Check it's not a stock folder + if (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + // Get the parent folder of the moved item : we may have to update it + LLUUID src_folder = inv_cat->getParentUUID(); + + LLViewerInventoryCategory * viewer_inv_cat = (LLViewerInventoryCategory *) inv_cat; + if (copy) + { + // Copy the folder + copy_inventory_category(&gInventory, viewer_inv_cat, dest_folder, LLUUID::null, move_no_copy_items); + } + else + { + LL_INFOS("SLM") << "Move category " << make_info(viewer_inv_cat) << " to '" << make_inventory_path(dest_folder) << "'" << LL_ENDL; + // Reparent the folder + gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false); + // Check the destination folder recursively for no copy items and promote the including folders if any + LLMarketplaceValidator::getInstance()->validateMarketplaceListings(dest_folder); + } + + // Update the modified folders + update_marketplace_category(src_folder); + update_marketplace_category(dest_folder); + gInventory.notifyObservers(); + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + error_msg; + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return false; + } + + open_marketplace_listings(); + return true; +} + +bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCategory* cat2) +{ + return cat1->getName().compare(cat2->getName()) < 0; +} + +// Make all relevant business logic checks on the marketplace listings starting with the folder as argument. +// This function does no deletion of listings but a mere audit and raises issues to the user (through the +// optional callback cb). +// The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders. +// @pending_callbacks - how many callbacks we are waiting for, must be inited before use +// @result - true if things validate, false if issues are raised, must be inited before use +typedef boost::function validation_result_callback_t; +void validate_marketplacelistings( + LLInventoryCategory* cat, + validation_result_callback_t cb_result, + LLMarketplaceValidator::validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth, + bool notify_observers, + S32 &pending_callbacks, + bool &result) +{ + // Get the type and the depth of the folder + LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (cat); + const LLFolderType::EType folder_type = cat->getPreferredType(); + if (depth < 0) + { + // If the depth argument was not provided, evaluate the depth directly + depth = depth_nesting_in_marketplace(cat->getUUID()); + } + if (depth < 0) + { + // If the folder is not under the marketplace listings root, we run validation as if it was a listing folder and prevent any hierarchy fix + // This allows the function to be used to pre-validate a folder anywhere in the inventory + depth = 1; + fix_hierarchy = false; + } + + // Set the indentation for print output (typically, audit button in marketplace folder floater) + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + + // Check out that version folders are marketplace ready + if (depth == 2) + { + std::string message; + // Note: if we fix the hierarchy, we want to check the items individually, hence the last argument here + if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy)) + { + result = false; + if (cb_msg) + { + message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + } + } + + // Check out that stock folders are at the right level + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth <= 2)) + { + if (fix_hierarchy) + { + if (cb_msg) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); + cb_msg(message,depth,LLError::LEVEL_WARN); + } + + // Nest the stock folder one level deeper in a normal folder and restart from there + pending_callbacks++; + LLUUID parent_uuid = cat->getParentUUID(); + LLUUID cat_uuid = cat->getUUID(); + gInventory.createNewCategory(parent_uuid, + LLFolderType::FT_NONE, + cat->getName(), + [cat_uuid, cb_result, cb_msg, fix_hierarchy, depth](const LLUUID &new_cat_id) + { + if (new_cat_id.isNull()) + { + cb_result(0, false); + return; + } + LLInventoryCategory * move_cat = gInventory.getCategory(cat_uuid); + LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *)(move_cat); + LLInventoryCategory * new_cat = gInventory.getCategory(new_cat_id); + gInventory.changeCategoryParent(viewer_cat, new_cat_id, false); + S32 pending = 0; + bool result = true; + validate_marketplacelistings(new_cat, cb_result, cb_msg, fix_hierarchy, depth + 1, true, pending, result); + cb_result(pending, result); + } + ); + result = false; + return; + } + else + { + result = false; + if (cb_msg) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + } + } + + // Item sorting and validation : sorting and moving the various stock items is complicated as the set of constraints is high + // We need to: + // * separate non stock items, stock items per types in different folders + // * have stock items nested at depth 2 at least + // * never ever move the non-stock items + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + + // We use a composite (type,permission) key on that map to store UUIDs of items of same (type,permissions) + std::map > items_vector; + + // Parse the items and create vectors of item UUIDs sorting copyable items and stock items of various types + bool has_bad_items = false; + 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; + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; + + // Test but skip items that shouldn't be there to start with, raise an error message for those + std::string error_msg; + if (!can_move_to_marketplace(item, error_msg, false)) + { + has_bad_items = true; + if (cb_msg && fix_hierarchy) + { + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + continue; + } + // Update the appropriate vector item for that type + LLInventoryType::EType type = LLInventoryType::IT_COUNT; // Default value for non stock items + U32 perms = 0; + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + // Get the item type for stock items + type = viewer_inv_item->getInventoryType(); + perms = viewer_inv_item->getPermissions().getMaskNextOwner(); + } + U32 key = (((U32)(type) & 0xFF) << 24) | (perms & 0xFFFFFF); + items_vector[key].push_back(viewer_inv_item->getUUID()); + } + + // How many types of items? Which type is it if only one? + S32 count = items_vector.size(); + U32 default_key = (U32)(LLInventoryType::IT_COUNT) << 24; // This is the key for any normal copyable item + U32 unique_key = (count == 1 ? items_vector.begin()->first : default_key); // The key in the case of one item type only + + // If we have no items in there (only folders or empty), analyze a bit further + if ((count == 0) && !has_bad_items) + { + if (cat_array->size() == 0) + { + // So we have no item and no folder. That's at least a warning. + if (depth == 2) + { + // If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist) + if (cb_msg) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); + cb_msg(message,depth,LLError::LEVEL_WARN); + } + } + else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) + { + // If this is a legit but empty stock folder, warn only (listing must stay searchable when out of stock) + if (cb_msg) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); + cb_msg(message,depth,LLError::LEVEL_WARN); + } + } + else if (cb_msg) + { + // We warn if there's nothing in a regular folder (may be it's an under construction listing) + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); + cb_msg(message,depth,LLError::LEVEL_WARN); + } + } + else + { + // Done with that folder : Print out the folder name unless we already found an error here + if (cb_msg && result && (depth >= 1)) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb_msg(message,depth,LLError::LEVEL_INFO); + } + } + } + // If we have a single type of items of the right type in the right place, we're done + else if ((count == 1) && !has_bad_items && (((unique_key == default_key) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) + { + // Done with that folder : Print out the folder name unless we already found an error here + if (cb_msg && result && (depth >= 1)) + { + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb_msg(message,depth,LLError::LEVEL_INFO); + } + } + else + { + if (fix_hierarchy && !has_bad_items) + { + // Alert the user when an existing stock folder has to be split + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && ((count >= 2) || (cat_array->size() > 0))) + { + LLNotificationsUtil::add("AlertMerchantStockFolderSplit"); + } + // If we have more than 1 type of items or we are at the listing level or we have stock/no stock type mismatch, wrap the items in subfolders + if ((count > 1) || (depth == 1) || + ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key == default_key)) || + ((folder_type != LLFolderType::FT_MARKETPLACE_STOCK) && (unique_key != default_key))) + { + // Create one folder per vector at the right depth and of the right type + std::map >::iterator items_vector_it = items_vector.begin(); + while (items_vector_it != items_vector.end()) + { + // Create a new folder + const LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); + const LLUUID origin_uuid = viewer_cat->getUUID(); + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); + std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName()); + LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); + if (cb_msg) + { + std::string message = ""; + if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK) + { + message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Stock"); + } + else + { + message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); + } + cb_msg(message,depth,LLError::LEVEL_WARN); + } + + pending_callbacks++; + std::vector uuid_vector = items_vector_it->second; // needs to be a copy for lambda + gInventory.createNewCategory( + parent_uuid, + new_folder_type, + folder_name, + [uuid_vector, cb_result, cb_msg, depth, parent_uuid, origin_uuid, notify_observers](const LLUUID &new_category_id) + { + // Move each item to the new folder + std::vector::const_reverse_iterator iter = uuid_vector.rbegin(); + while (iter != uuid_vector.rend()) + { + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(*iter); + if (cb_msg) + { + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); + cb_msg(message, depth, LLError::LEVEL_WARN); + } + gInventory.changeItemParent(viewer_inv_item, new_category_id, true); + iter++; + } + + if (origin_uuid != parent_uuid) + { + // We might have moved last item from a folder, check if it needs to be removed + LLViewerInventoryCategory* cat = gInventory.getCategory(origin_uuid); + if (cat->getDescendentCount() == 0) + { + // Remove previous folder if it ends up empty + if (cb_msg) + { + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); + cb_msg(message, depth, LLError::LEVEL_WARN); + } + gInventory.removeCategory(cat->getUUID()); + if (notify_observers) + { + gInventory.notifyObservers(); + } + } + } + + // Next type + update_marketplace_category(parent_uuid); + update_marketplace_category(new_category_id); + if (notify_observers) + { + gInventory.notifyObservers(); + } + cb_result(0, true); + } + ); + items_vector_it++; + } + } + // Stock folder should have no sub folder so reparent those up + if (folder_type == LLFolderType::FT_MARKETPLACE_STOCK) + { + LLUUID parent_uuid = cat->getParentUUID(); + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (*iter); + gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); + validate_marketplacelistings(viewer_cat, cb_result, cb_msg, fix_hierarchy, depth, false, pending_callbacks, result); + } + } + } + else if (cb_msg) + { + // We are not fixing the hierarchy but reporting problems, report everything we can find + // Print the folder name + if (result && (depth >= 1)) + { + if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (count >= 2)) + { + // Report if a stock folder contains a mix of items + result = false; + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0)) + { + // Report if a stock folder contains subfolders + result = false; + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock"); + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + else + { + // Simply print the folder name + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); + cb_msg(message,depth,LLError::LEVEL_INFO); + } + } + // Scan each item and report if there's a problem + 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; + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) item; + std::string error_msg; + if (!can_move_to_marketplace(item, error_msg, false)) + { + // Report items that shouldn't be there to start with + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // Report stock items that are misplaced + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + else if (depth == 1) + { + // Report items not wrapped in version folder + result = false; + std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); + cb_msg(message,depth,LLError::LEVEL_ERROR); + } + } + } + + // Clean up + if (viewer_cat->getDescendentCount() == 0) + { + // Remove the current folder if it ends up empty + if (cb_msg) + { + std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); + cb_msg(message,depth,LLError::LEVEL_WARN); + } + gInventory.removeCategory(cat->getUUID()); + if (notify_observers) + { + gInventory.notifyObservers(); + } + result &=!has_bad_items; + return; + } + } + + // Recursion : Perform the same validation on each nested folder + gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array); + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + // Sort the folders in alphabetical order first + std::sort(cat_array_copy.begin(), cat_array_copy.end(), sort_alpha); + + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLInventoryCategory* category = *iter; + validate_marketplacelistings(category, cb_result, cb_msg, fix_hierarchy, depth + 1, false, pending_callbacks, result); + } + + update_marketplace_category(cat->getUUID(), true, true); + if (notify_observers) + { + gInventory.notifyObservers(); + } + result &= !has_bad_items; +} + +void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) +{ + LLInventoryItem* inv_item = gInventory.getItem(item_id); + if (inv_item) + { + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(inv_item->getParentUUID(), -1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + LLPointer new_item = new LLViewerInventoryItem(inv_item); + new_item->setParent(new_parent_id); + new_item->updateParentOnServer(false); + gInventory.updateItem(new_item); + gInventory.notifyObservers(); + } +} + +void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLInventoryItem* inv_item = gInventory.getItem(*it); + if (inv_item) + { + change_item_parent(*it, new_cat_uuid); + } + else + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (inv_cat && !LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + gInventory.changeCategoryParent((LLViewerInventoryCategory*)inv_cat, new_cat_uuid, false); + } + } + } + + LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + if (!floater_inventory) + { + LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; + return; + } + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + if (sidepanel_inventory) + { + if (sidepanel_inventory->getActivePanel()) + { + sidepanel_inventory->getActivePanel()->setSelection(new_cat_uuid, TAKE_FOCUS_YES); + LLFolderViewItem* fv_folder = sidepanel_inventory->getActivePanel()->getItemByID(new_cat_uuid); + if (fv_folder) + { + fv_folder->setOpen(true); + } + } + } +} + +bool is_only_cats_selected(const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (!inv_cat) + { + return false; + } + } + return true; +} + +bool is_only_items_selected(const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLViewerInventoryItem* inv_item = gInventory.getItem(*it); + if (!inv_item) + { + return false; + } + } + return true; +} + + +void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::string& folder_name) +{ + LLInventoryObject* first_item = gInventory.getObject(*selected_uuids.begin()); + if (!first_item) + { + return; + } + + inventory_func_type func = boost::bind(&move_items_to_folder, _1, selected_uuids); + gInventory.createNewCategory(first_item->getParentUUID(), LLFolderType::FT_NONE, folder_name, func); +} + +std::string get_category_path(LLUUID cat_id) +{ + LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + std::string localized_cat_name; + if (!LLTrans::findString(localized_cat_name, "InvFolder " + cat->getName())) + { + localized_cat_name = cat->getName(); + } + + if (cat->getParentUUID().notNull()) + { + return get_category_path(cat->getParentUUID()) + " > " + localized_cat_name; + } + else + { + return localized_cat_name; + } +} +// Returns true if the item can be moved to Current Outfit or any outfit folder. +bool can_move_to_outfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit) +{ + LLInventoryType::EType inv_type = inv_item->getInventoryType(); + if ((inv_type != LLInventoryType::IT_WEARABLE) && + (inv_type != LLInventoryType::IT_GESTURE) && + (inv_type != LLInventoryType::IT_ATTACHMENT) && + (inv_type != LLInventoryType::IT_OBJECT) && + (inv_type != LLInventoryType::IT_SNAPSHOT) && + (inv_type != LLInventoryType::IT_TEXTURE)) + { + return false; + } + + U32 flags = inv_item->getFlags(); + if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS) + { + return false; + } + + if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT)) + { + return !move_is_into_current_outfit; + } + + if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) + { + return false; + } + + return true; +} + +// Returns true if item is a landmark or a link to a landmark +// and can be moved to Favorites or Landmarks folder. +bool can_move_to_landmarks(LLInventoryItem* inv_item) +{ + // Need to get the linked item to know its type because LLInventoryItem::getType() + // returns actual type AT_LINK for links, not the asset type of a linked item. + if (LLAssetType::AT_LINK == inv_item->getType()) + { + LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); + if (linked_item) + { + return LLAssetType::AT_LANDMARK == linked_item->getType(); + } + } + + return LLAssetType::AT_LANDMARK == inv_item->getType(); +} + +// Returns true if folder's content can be moved to Current Outfit or any outfit folder. +bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit) +{ + LLInventoryModel::cat_array_t *cats; + LLInventoryModel::item_array_t *items; + model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items); + + if (items->size() > wear_limit) + { + return false; + } + + if (items->size() == 0) + { + // Nothing to move(create) + return false; + } + + if (cats->size() > 0) + { + // We do not allow subfolders in outfits of "My Outfits" yet + return false; + } + + LLInventoryModel::item_array_t::iterator iter = items->begin(); + LLInventoryModel::item_array_t::iterator end = items->end(); + + while (iter != end) + { + LLViewerInventoryItem *item = *iter; + if (!can_move_to_outfit(item, false)) + { + return false; + } + iter++; + } + + return true; +} + +std::string get_localized_folder_name(LLUUID cat_uuid) +{ + std::string localized_root_name; + const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); + if (cat) + { + LLFolderType::EType preferred_type = cat->getPreferredType(); + + // Translation of Accessories folder in Library inventory folder + bool accessories = false; + if(cat->getName() == "Accessories") + { + const LLUUID& parent_folder_id = cat->getParentUUID(); + accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); + } + + //"Accessories" inventory category has folder type FT_NONE. So, this folder + //can not be detected as protected with LLFolderType::lookupIsProtectedType + localized_root_name.assign(cat->getName()); + if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) + { + LLTrans::findString(localized_root_name, std::string("InvFolder ") + cat->getName(), LLSD()); + } + } + + return localized_root_name; +} + +void new_folder_window(const LLUUID& folder_id) +{ + LLPanelMainInventory::newFolderWindow(folder_id); +} + +void ungroup_folder_items(const LLUUID& folder_id) +{ + LLInventoryCategory* inv_cat = gInventory.getCategory(folder_id); + if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + return; + } + const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); + LLInventoryModel::cat_array_t cats = *cat_array; + LLInventoryModel::item_array_t items = *item_array; + + for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) + { + LLViewerInventoryCategory* cat = *cat_iter; + if (cat) + { + gInventory.changeCategoryParent(cat, new_cat_uuid, false); + } + } + for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) + { + LLViewerInventoryItem* item = *item_iter; + if(item) + { + gInventory.changeItemParent(item, new_cat_uuid, false); + } + } + gInventory.removeCategory(inv_cat->getUUID()); + gInventory.notifyObservers(); +} + +std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id) +{ + if (model) + { + const LLInventoryItem *item = model->getItem(item_id); + if(item) + { + std::string desc = item->getDescription(); + LLStringUtil::toUpper(desc); + return desc; + } + } + return LLStringUtil::null; +} + +std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id) +{ + if (model) + { + const LLInventoryItem *item = model->getItem(item_id); + if(item) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) + { + std::string username = av_name.getUserName(); + LLStringUtil::toUpper(username); + return username; + } + } + } + return LLStringUtil::null; +} + +std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id) +{ + if (model) + { + const LLViewerInventoryItem *item = model->getItem(item_id); + if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery())) + { + std::string uuid = item->getAssetUUID().asString(); + LLStringUtil::toUpper(uuid); + return uuid; + } + } + return LLStringUtil::null; +} + +bool can_share_item(const LLUUID& item_id) +{ + bool can_share = false; + + if (gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID())) + { + const LLViewerInventoryItem *item = gInventory.getItem(item_id); + if (item) + { + if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) + { + can_share = LLGiveInventory::isInventoryGiveAcceptable(item); + } + } + else + { + can_share = (gInventory.getCategory(item_id) != NULL); + } + + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if ((item_id == trash_id) || gInventory.isObjectDescendentOf(item_id, trash_id)) + { + can_share = false; + } + } + + return can_share; +} +///---------------------------------------------------------------------------- +/// LLMarketplaceValidator implementations +///---------------------------------------------------------------------------- + + +LLMarketplaceValidator::LLMarketplaceValidator() + : mPendingCallbacks(0) + , mValidationInProgress(false) +{ +} + +LLMarketplaceValidator::~LLMarketplaceValidator() +{ +} + +void LLMarketplaceValidator::validateMarketplaceListings( + const LLUUID &category_id, + LLMarketplaceValidator::validation_done_callback_t cb_done, + LLMarketplaceValidator::validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth) +{ + + mValidationQueue.emplace(category_id, cb_done, cb_msg, fix_hierarchy, depth); + if (!mValidationInProgress) + { + start(); + } +} + +void LLMarketplaceValidator::start() +{ + if (mValidationQueue.empty()) + { + mValidationInProgress = false; + return; + } + mValidationInProgress = true; + + const ValidationRequest &first = mValidationQueue.front(); + LLViewerInventoryCategory* cat = gInventory.getCategory(first.mCategoryId); + if (!cat) + { + LL_WARNS() << "Tried to validate a folder that doesn't exist" << LL_ENDL; + if (first.mCbDone) + { + first.mCbDone(false); + } + mValidationQueue.pop(); + start(); + return; + } + + validation_result_callback_t result_callback = [](S32 pending, bool result) + { + LLMarketplaceValidator* validator = LLMarketplaceValidator::getInstance(); + validator->mPendingCallbacks--; // we just got a callback + validator->mPendingCallbacks += pending; + validator->mPendingResult &= result; + if (validator->mPendingCallbacks <= 0) + { + llassert(validator->mPendingCallbacks == 0); // shouldn't be below 0 + const ValidationRequest &first = validator->mValidationQueue.front(); + if (first.mCbDone) + { + first.mCbDone(validator->mPendingResult); + } + validator->mValidationQueue.pop(); // done; + validator->start(); + } + }; + + mPendingResult = true; + mPendingCallbacks = 1; // do '1' in case something decides to callback immediately + + S32 pending_calbacks = 0; + bool result = true; + validate_marketplacelistings( + cat, + result_callback, + first.mCbMsg, + first.mFixHierarchy, + first.mDepth, + true, + pending_calbacks, + result); + + result_callback(pending_calbacks, result); +} + +LLMarketplaceValidator::ValidationRequest::ValidationRequest( + LLUUID category_id, + validation_done_callback_t cb_done, + validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth) +: mCategoryId(category_id) +, mCbDone(cb_done) +, mCbMsg(cb_msg) +, mFixHierarchy(fix_hierarchy) +, mDepth(depth) +{} + +///---------------------------------------------------------------------------- +/// LLInventoryCollectFunctor implementations +///---------------------------------------------------------------------------- + +// static +bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryItem* item) +{ + if (!item) + return false; + + switch(item->getType()) + { + case LLAssetType::AT_OBJECT: + case LLAssetType::AT_BODYPART: + case LLAssetType::AT_CLOTHING: + if (!get_is_item_worn(item->getUUID())) + return true; + break; + default: + return true; + break; + } + return false; +} + +bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if(mType == LLAssetType::AT_CATEGORY) + { + if(cat) return true; + } + if(item) + { + if(item->getType() == mType) return true; + } + return false; +} + +bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if(mType == LLAssetType::AT_CATEGORY) + { + if(cat) return false; + } + if(item) + { + if(item->getType() == mType) return false; + else return true; + } + return true; +} + +bool LLIsOfAssetType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if(mType == LLAssetType::AT_CATEGORY) + { + if(cat) return true; + } + if(item) + { + if(item->getActualType() == mType) return true; + } + return false; +} + +bool LLAssetIDAndTypeMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (!item) return false; + return (item->getActualType() == mType && item->getAssetUUID() == mAssetID); +} + +bool LLIsValidItemLink::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem) return false; + return (vitem->getActualType() == LLAssetType::AT_LINK && !vitem->getIsBrokenLink()); +} + +bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if(mType == LLAssetType::AT_CATEGORY) + { + if(cat) + { + return true; + } + } + if(item) + { + if(item->getType() == mType) + { + LLPermissions perm = item->getPermissions(); + if ((perm.getMaskBase() & mPerm) == mPerm) + { + return true; + } + } + } + return false; +} + +bool LLBuddyCollector::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if(item) + { + if((LLAssetType::AT_CALLINGCARD == item->getType()) + && (!item->getCreatorUUID().isNull()) + && (item->getCreatorUUID() != gAgent.getID())) + { + return true; + } + } + return false; +} + + +bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if(item) + { + if((LLAssetType::AT_CALLINGCARD == item->getType()) + && (item->getCreatorUUID().notNull()) + && (item->getCreatorUUID() != gAgent.getID())) + { + mSeen.insert(item->getCreatorUUID()); + return true; + } + } + return false; +} + + +bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if(item) + { + if((LLAssetType::AT_CALLINGCARD == item->getType()) + && (item->getCreatorUUID() == mBuddyID)) + { + return true; + } + } + return false; +} + + +bool LLNameCategoryCollector::operator()( + LLInventoryCategory* cat, LLInventoryItem* item) +{ + if(cat) + { + if (!LLStringUtil::compareInsensitive(mName, cat->getName())) + { + return true; + } + } + return false; +} + +bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + // Valid COF items are: + // - links to wearables (body parts or clothing) + // - links to attachments + // - links to gestures + // - links to ensemble folders + LLViewerInventoryItem *linked_item = ((LLViewerInventoryItem*)item)->getLinkedItem(); + if (linked_item) + { + LLAssetType::EType type = linked_item->getType(); + return (type == LLAssetType::AT_CLOTHING || + type == LLAssetType::AT_BODYPART || + type == LLAssetType::AT_GESTURE || + type == LLAssetType::AT_OBJECT); + } + else + { + LLViewerInventoryCategory *linked_category = ((LLViewerInventoryItem*)item)->getLinkedCategory(); + // BAP remove AT_NONE support after ensembles are fully working? + return (linked_category && + ((linked_category->getPreferredType() == LLFolderType::FT_NONE) || + (LLFolderType::lookupIsEnsembleType(linked_category->getPreferredType())))); + } +} + +bool LLFindBrokenLinks::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + // only for broken links getType will be a link + // otherwise it's supposed to have the type of an item + // it is linked too + if (item && LLAssetType::lookupIsLinkType(item->getType())) + { + return true; + } + return false; +} + +bool LLFindWearables::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if(item) + { + if((item->getType() == LLAssetType::AT_CLOTHING) + || (item->getType() == LLAssetType::AT_BODYPART)) + { + return true; + } + } + return false; +} + +LLFindWearablesEx::LLFindWearablesEx(bool is_worn, bool include_body_parts) +: mIsWorn(is_worn) +, mIncludeBodyParts(include_body_parts) +{} + +bool LLFindWearablesEx::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem) return false; + + // Skip non-wearables. + if (!vitem->isWearableType() && vitem->getType() != LLAssetType::AT_OBJECT && vitem->getType() != LLAssetType::AT_GESTURE) + { + return false; + } + + // Skip body parts if requested. + if (!mIncludeBodyParts && vitem->getType() == LLAssetType::AT_BODYPART) + { + return false; + } + + // Skip broken links. + if (vitem->getIsBrokenLink()) + { + return false; + } + + return (bool) get_is_item_worn(item->getUUID()) == mIsWorn; +} + +bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (!item) return false; + if (item->getType() != LLAssetType::AT_CLOTHING && + item->getType() != LLAssetType::AT_BODYPART) + { + return false; + } + + LLViewerInventoryItem *vitem = dynamic_cast(item); + if (!vitem || vitem->getWearableType() != mWearableType) return false; + + return true; +} + +void LLFindWearablesOfType::setType(LLWearableType::EType type) +{ + mWearableType = type; +} + +bool LLIsTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return item && (item->getType() == LLAssetType::AT_TEXTURE); +} + +bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (item) + { + return !get_is_item_removable(&gInventory, item->getUUID(), true); + } + if (cat) + { + return !get_is_category_removable(&gInventory, cat->getUUID()); + } + + LL_WARNS() << "Not a category and not an item?" << LL_ENDL; + return false; +} + +///---------------------------------------------------------------------------- +/// LLAssetIDMatches +///---------------------------------------------------------------------------- +bool LLAssetIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return (item && item->getAssetUUID() == mAssetID); +} + +///---------------------------------------------------------------------------- +/// LLLinkedItemIDMatches +///---------------------------------------------------------------------------- +bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return (item && + (item->getIsLinkType()) && + (item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID. +} + +void LLSaveFolderState::setApply(bool apply) +{ + mApply = apply; + // before generating new list of open folders, clear the old one + if(!apply) + { + clearOpenFolders(); + } +} + +void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) +{ + LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getViewModelItem(); + if(!bridge) return; + + if(mApply) + { + // we're applying the open state + LLUUID id(bridge->getUUID()); + if(mOpenFolders.find(id) != mOpenFolders.end()) + { + if (!folder->isOpen()) + { + folder->setOpen(true); + } + } + else + { + // keep selected filter in its current state, this is less jarring to user + if (!folder->isSelected() && folder->isOpen()) + { + folder->setOpen(false); + } + } + } + else + { + // we're recording state at this point + if(folder->isOpen()) + { + mOpenFolders.insert(bridge->getUUID()); + } + } +} + +void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) +{ + if (item->passedFilter()) + { + item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); + } +} + +void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) +{ + if (folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) + { + folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); + } + // if this folder didn't pass the filter, and none of its descendants did + else if (!folder->getViewModelItem()->passedFilter() && !folder->getViewModelItem()->descendantsPassedFilter()) + { + folder->setOpenArrangeRecursively(false, LLFolderViewFolder::RECURSE_NO); + } +} + +void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) +{ + if (item->passedFilter() && !mItemSelected) + { + item->getRoot()->setSelection(item, false, false); + if (item->getParentFolder()) + { + item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); + } + mItemSelected = true; + } +} + +void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder) +{ + // Skip if folder or item already found, if not filtered or if no parent (root folder is not selectable) + if (!mFolderSelected && !mItemSelected && folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) + { + folder->getRoot()->setSelection(folder, false, false); + folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); + mFolderSelected = true; + } +} + +void LLOpenFoldersWithSelection::doItem(LLFolderViewItem *item) +{ + if (item->getParentFolder() && item->isSelected()) + { + item->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); + } +} + +void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) +{ + if (folder->getParentFolder() && folder->isSelected()) + { + folder->getParentFolder()->setOpenArrangeRecursively(true, LLFolderViewFolder::RECURSE_UP); + } +} + +// Callback for doToSelected if DAMA required... +void LLInventoryAction::callback_doToSelected(const LLSD& notification, const LLSD& response, class LLInventoryModel* model, class LLFolderView* root, const std::string& action) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES + { + doToSelected(model, root, action, false); + } +} + +void LLInventoryAction::callback_copySelected(const LLSD& notification, const LLSD& response, class LLInventoryModel* model, class LLFolderView* root, const std::string& action) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) // YES, Move no copy item(s) + { + doToSelected(model, root, "copy_or_move_to_marketplace_listings", false); + } + else if (option == 1) // NO, Don't move no copy item(s) (leave them behind) + { + doToSelected(model, root, "copy_to_marketplace_listings", false); + } +} + +// Succeeds iff all selected items are bridges to objects, in which +// case returns their corresponding uuids. +bool get_selection_object_uuids(LLFolderView *root, uuid_vec_t& ids) +{ + uuid_vec_t results; + S32 non_object = 0; + LLFolderView::selected_items_t selectedItems = root->getSelectedItems(); + for(LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + LLObjectBridge *view_model = dynamic_cast((*it)->getViewModelItem()); + + if(view_model && view_model->getUUID().notNull()) + { + results.push_back(view_model->getUUID()); + } + else + { + non_object++; + } + } + if (non_object == 0) + { + ids = results; + return true; + } + return false; +} + + +void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action, bool user_confirm) +{ + std::set selected_items = root->getSelectionList(); + if (selected_items.empty() + && action != "wear" + && action != "wear_add" + && !isRemoveAction(action)) + { + // Was item removed while user was checking menu? + // "wear" and removal exlusions are due to use of + // getInventorySelectedUUIDs() below + LL_WARNS("Inventory") << "Menu tried to operate on empty selection" << LL_ENDL; + + if (("copy" == action) || ("cut" == action)) + { + LLClipboard::instance().reset(); + } + + return; + } + + // Prompt the user and check for authorization for some marketplace active listing edits + if (user_confirm && (("delete" == action) || ("cut" == action) || ("rename" == action) || ("properties" == action) || ("task_properties" == action) || ("open" == action))) + { + std::set::iterator set_iter = selected_items.begin(); + LLFolderViewModelItemInventory * viewModel = NULL; + for (; set_iter != selected_items.end(); ++set_iter) + { + viewModel = dynamic_cast((*set_iter)->getViewModelItem()); + if (viewModel && (depth_nesting_in_marketplace(viewModel->getUUID()) >= 0)) + { + break; + } + } + if (set_iter != selected_items.end()) + { + if ("open" == action) + { + if (get_can_item_be_worn(viewModel->getUUID())) + { + // Wearing an object from any listing, active or not, is verbotten + LLNotificationsUtil::add("AlertMerchantListingCannotWear"); + return; + } + // Note: we do not prompt for change when opening items (e.g. textures or note cards) on the marketplace... + } + else if (LLMarketplaceData::instance().isInActiveFolder(viewModel->getUUID()) || + LLMarketplaceData::instance().isListedAndActive(viewModel->getUUID())) + { + // If item is in active listing, further confirmation is required + if ((("cut" == action) || ("delete" == action)) && (LLMarketplaceData::instance().isListed(viewModel->getUUID()) || LLMarketplaceData::instance().isVersionFolder(viewModel->getUUID()))) + { + // Cut or delete of the active version folder or listing folder itself will unlist the listing so ask that question specifically + LLNotificationsUtil::add("ConfirmMerchantUnlist", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); + return; + } + // Any other case will simply modify but not unlist a listing + LLNotificationsUtil::add("ConfirmMerchantActiveChange", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); + return; + } + // Cutting or deleting a whole listing needs confirmation as SLM will be archived and inaccessible to the user + else if (LLMarketplaceData::instance().isListed(viewModel->getUUID()) && (("cut" == action) || ("delete" == action))) + { + LLNotificationsUtil::add("ConfirmListingCutOrDelete", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_doToSelected, _1, _2, model, root, action)); + return; + } + } + } + // Copying to the marketplace needs confirmation if nocopy items are involved + if (user_confirm && ("copy_to_marketplace_listings" == action)) + { + std::set::iterator set_iter = selected_items.begin(); + LLFolderViewModelItemInventory * viewModel = dynamic_cast((*set_iter)->getViewModelItem()); + if (contains_nocopy_items(viewModel->getUUID())) + { + LLNotificationsUtil::add("ConfirmCopyToMarketplace", LLSD(), LLSD(), boost::bind(&LLInventoryAction::callback_copySelected, _1, _2, model, root, action)); + return; + } + } + + // Keep track of the marketplace folders that will need update of their status/name after the operation is performed + buildMarketplaceFolders(root); + + if ("rename" == action) + { + root->startRenamingSelectedItem(); + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); + return; + } + + if ("delete" == action) + { + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + bool marketplacelistings_item = false; + bool has_worn = false; + bool needs_replacement = false; + LLAllDescendentsPassedFilter f; + for (std::set::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it) + { + if (LLFolderViewFolder* folder = dynamic_cast(*it)) + { + folder->applyFunctorRecursively(f); + } + LLFolderViewModelItemInventory * viewModel = dynamic_cast((*it)->getViewModelItem()); + LLUUID obj_id = viewModel->getUUID(); + if (viewModel && gInventory.isObjectDescendentOf(obj_id, marketplacelistings_id)) + { + marketplacelistings_item = true; + break; + } + + LLViewerInventoryCategory* cat = gInventory.getCategory(obj_id); + if (cat) + { + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + + gInventory.collectDescendents(obj_id, categories, items, false); + + for (LLInventoryModel::item_array_t::value_type& item : items) + { + if (get_is_item_worn(item)) + { + has_worn = true; + LLWearableType::EType type = item->getWearableType(); + if (type == LLWearableType::WT_SHAPE + || type == LLWearableType::WT_SKIN + || type == LLWearableType::WT_HAIR + || type == LLWearableType::WT_EYES) + { + needs_replacement = true; + break; + } + } + } + if (needs_replacement) + { + break; + } + } + LLViewerInventoryItem* item = gInventory.getItem(obj_id); + if (item && get_is_item_worn(item)) + { + has_worn = true; + LLWearableType::EType type = item->getWearableType(); + if (type == LLWearableType::WT_SHAPE + || type == LLWearableType::WT_SKIN + || type == LLWearableType::WT_HAIR + || type == LLWearableType::WT_EYES) + { + needs_replacement = true; + break; + } + } + } + // Fall through to the generic confirmation if the user choose to ignore the specialized one + if (needs_replacement) + { + LLNotificationsUtil::add("CantDeleteRequiredClothing"); + } + else if (has_worn) + { + LLSD payload; + payload["has_worn"] = true; + LLNotificationsUtil::add("DeleteWornItems", LLSD(), payload, boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); + } + else if ( (!f.allDescendentsPassedFilter()) && !marketplacelistings_item && (!LLNotifications::instance().getIgnored("DeleteFilteredItems")) ) + { + LLNotificationsUtil::add("DeleteFilteredItems", LLSD(), LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); + } + else + { + if (!sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session + { + LLNotifications::instance().setIgnored("DeleteItems", false); + sDeleteConfirmationDisplayed = true; + } + + LLSD args; + args["QUESTION"] = LLTrans::getString(root->getSelectedCount() > 1 ? "DeleteItems" : "DeleteItem"); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); + } + // Note: marketplace listings will be updated in the callback if delete confirmed + return; + } + if (("copy" == action) || ("cut" == action)) + { + // Clear the clipboard before we start adding things on it + LLClipboard::instance().reset(); + } + if ("replace_links" == action) + { + LLSD params; + if (root->getSelectedCount() == 1) + { + LLFolderViewItem* folder_item = root->getSelectedItems().front(); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); + + if (bridge) + { + LLInventoryObject* obj = bridge->getInventoryObject(); + if (obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getActualType() != LLAssetType::AT_LINK_FOLDER) + { + params = LLSD(obj->getUUID()); + } + } + } + LLFloaterReg::showInstance("linkreplace", params); + return; + } + + static const std::string change_folder_string = "change_folder_type_"; + if (action.length() > change_folder_string.length() && + (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) + { + LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); + LLFolderViewModelItemInventory* inventory_item = static_cast(root->getViewModelItem()); + LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID()); + if (!cat) return; + cat->changeType(new_folder_type); + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); + return; + } + + + LLMultiPreview* multi_previewp = NULL; + LLMultiItemProperties* multi_itempropertiesp = nullptr; + + if (("task_open" == action || "open" == action) && selected_items.size() > 1) + { + bool open_multi_preview = true; + + if ("open" == action) + { + for (std::set::iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = *set_iter; + if (folder_item) + { + LLInvFVBridge* bridge = dynamic_cast(folder_item->getViewModelItem()); + if (!bridge || !bridge->isMultiPreviewAllowed()) + { + open_multi_preview = false; + break; + } + } + } + } + + if (open_multi_preview) + { + multi_previewp = new LLMultiPreview(); + gFloaterView->addChild(multi_previewp); + + LLFloater::setFloaterHost(multi_previewp); + } + + } + else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) + { + multi_itempropertiesp = new LLMultiItemProperties("item_properties"); + gFloaterView->addChild(multi_itempropertiesp); + LLFloater::setFloaterHost(multi_itempropertiesp); + } + + std::set selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); + + // copy list of applicable items into a vector for bulk handling + uuid_vec_t ids; + if (action == "wear" || action == "wear_add") + { + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + std::copy_if(selected_uuid_set.begin(), + selected_uuid_set.end(), + std::back_inserter(ids), + [trash_id, mp_id](LLUUID id) + { + if (get_is_item_worn(id) + || LLAppearanceMgr::instance().getIsInCOF(id) + || gInventory.isObjectDescendentOf(id, trash_id)) + { + return false; + } + if (mp_id.notNull() && gInventory.isObjectDescendentOf(id, mp_id)) + { + return false; + } + LLInventoryObject* obj = (LLInventoryObject*)gInventory.getObject(id); + if (!obj) + { + return false; + } + if (obj->getIsLinkType() && gInventory.isObjectDescendentOf(obj->getLinkedUUID(), trash_id)) + { + return false; + } + if (obj->getIsLinkType() && LLAssetType::lookupIsLinkType(obj->getType())) + { + // missing + return false; + } + return true; + } + ); + } + else if (isRemoveAction(action)) + { + std::copy_if(selected_uuid_set.begin(), + selected_uuid_set.end(), + std::back_inserter(ids), + [](LLUUID id) + { + return get_is_item_worn(id); + } + ); + } + else + { + for (std::set::iterator it = selected_items.begin(), end_it = selected_items.end(); + it != end_it; + ++it) + { + ids.push_back(static_cast((*it)->getViewModelItem())->getUUID()); + } + } + + // Check for actions that get handled in bulk + if (action == "wear") + { + wear_multiple(ids, true); + } + else if (action == "wear_add") + { + wear_multiple(ids, false); + } + else if (isRemoveAction(action)) + { + LLAppearanceMgr::instance().removeItemsFromAvatar(ids); + } + else if ("save_selected_as" == action) + { + (new LLDirPickerThread(boost::bind(&LLInventoryAction::saveMultipleTextures, _1, selected_items, model), std::string()))->getFile(); + } + else if ("new_folder_from_selected" == action) + { + + LLInventoryObject* first_item = gInventory.getObject(*ids.begin()); + if (!first_item) + { + return; + } + const LLUUID& parent_uuid = first_item->getParentUUID(); + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + LLInventoryObject *item = gInventory.getObject(*it); + if (!item || item->getParentUUID() != parent_uuid) + { + LLNotificationsUtil::add("SameFolderRequired"); + return; + } + } + + LLSD args; + args["DESC"] = LLTrans::getString("New Folder"); + + LLNotificationsUtil::add("CreateSubfolder", args, LLSD(), + [ids](const LLSD& notification, const LLSD& response) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notification, response); + if (opt == 0) + { + std::string settings_name = response["message"].asString(); + + LLInventoryObject::correctInventoryName(settings_name); + if (settings_name.empty()) + { + settings_name = LLTrans::getString("New Folder"); + } + move_items_to_new_subfolder(ids, settings_name); + } + }); + } + else if ("ungroup_folder_items" == action) + { + if (ids.size() == 1) + { + ungroup_folder_items(*ids.begin()); + } + } + else if ("thumbnail" == action) + { + if (selected_items.size() > 0) + { + LLSD data; + std::set::iterator set_iter; + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = *set_iter; + if (!folder_item) continue; + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); + if (!bridge) continue; + data.append(bridge->getUUID()); + } + LLFloaterReg::showInstance("change_item_thumbnail", data); + } + } + else + { + std::set::iterator set_iter; + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = *set_iter; + if(!folder_item) continue; + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); + if(!bridge) continue; + bridge->performAction(model, action); + } + if(root->isSingleFolderMode() && selected_items.empty()) + { + LLInvFVBridge* bridge = (LLInvFVBridge*)root->getViewModelItem(); + if(bridge) + { + bridge->performAction(model, action); + } + } + } + + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); + + LLFloater::setFloaterHost(NULL); + if (multi_previewp) + { + multi_previewp->openFloater(LLSD()); + } + else if (multi_itempropertiesp) + { + multi_itempropertiesp->openFloater(LLSD()); + } +} + +void LLInventoryAction::saveMultipleTextures(const std::vector& filenames, std::set selected_items, LLInventoryModel* model) +{ + gSavedSettings.setString("TextureSaveLocation", filenames[0]); + + LLMultiPreview* multi_previewp = new LLMultiPreview(); + gFloaterView->addChild(multi_previewp); + + LLFloater::setFloaterHost(multi_previewp); + + std::map tex_names_map; + std::set::iterator set_iter; + + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = *set_iter; + if(!folder_item) continue; + LLTextureBridge* bridge = (LLTextureBridge*)folder_item->getViewModelItem(); + if(!bridge) continue; + + std::string tex_name = bridge->getName(); + if(!tex_names_map.insert(std::pair(tex_name, 0)).second) + { + tex_names_map[tex_name]++; + bridge->setFileName(tex_name + llformat("_%.3d", tex_names_map[tex_name])); + } + bridge->performAction(model, "save_selected_as"); + } + + LLFloater::setFloaterHost(NULL); + if (multi_previewp) + { + multi_previewp->openFloater(LLSD()); + } +} + +void LLInventoryAction::removeItemFromDND(LLFolderView* root) +{ + if(gAgent.isDoNotDisturb()) + { + //Get selected items + LLFolderView::selected_items_t selectedItems = root->getSelectedItems(); + LLFolderViewModelItemInventory * viewModel = NULL; + + //If user is in DND and deletes item, make sure the notification is not displayed by removing the notification + //from DND history and .xml file. Once this is done, upon exit of DND mode the item deleted will not show a notification. + for(LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + viewModel = dynamic_cast((*it)->getViewModelItem()); + + if(viewModel && viewModel->getUUID().notNull()) + { + //Will remove the item offer notification + LLDoNotDisturbNotificationStorage::instance().removeNotification(LLDoNotDisturbNotificationStorage::offerName, viewModel->getUUID()); + } + } + } +} + +void LLInventoryAction::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle root) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0 && !root.isDead() && !root.get()->isDead()) + { + bool has_worn = notification["payload"]["has_worn"].asBoolean(); + LLFolderView* folder_root = root.get(); + //Need to remove item from DND before item is removed from root folder view + //because once removed from root folder view the item is no longer a selected item + removeItemFromDND(folder_root); + + // removeSelectedItems will change selection, collect worn items beforehand + uuid_vec_t worn; + uuid_vec_t item_deletion_list; + uuid_vec_t cat_deletion_list; + if (has_worn) + { + //Get selected items + LLFolderView::selected_items_t selectedItems = folder_root->getSelectedItems(); + + //If user is in DND and deletes item, make sure the notification is not displayed by removing the notification + //from DND history and .xml file. Once this is done, upon exit of DND mode the item deleted will not show a notification. + for (LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) + { + LLFolderViewModelItemInventory* viewModel = dynamic_cast((*it)->getViewModelItem()); + + LLUUID obj_id = viewModel->getUUID(); + LLViewerInventoryCategory* cat = gInventory.getCategory(obj_id); + bool cat_has_worn = false; + if (cat) + { + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + + gInventory.collectDescendents(obj_id, categories, items, false); + + for (LLInventoryModel::item_array_t::value_type& item : items) + { + if (get_is_item_worn(item)) + { + worn.push_back(item->getUUID()); + cat_has_worn = true; + } + } + if (cat_has_worn) + { + cat_deletion_list.push_back(obj_id); + } + } + LLViewerInventoryItem* item = gInventory.getItem(obj_id); + if (item && get_is_item_worn(item)) + { + worn.push_back(obj_id); + item_deletion_list.push_back(obj_id); + } + } + } + + // removeSelectedItems will check if items are worn before deletion, + // don't 'unwear' yet to prevent race conditions from unwearing + // and removing simultaneously + folder_root->removeSelectedItems(); + + // unwear then delete the rest + if (!worn.empty()) + { + // should fire once after every item gets detached + LLAppearanceMgr::instance().removeItemsFromAvatar(worn, + [item_deletion_list, cat_deletion_list]() + { + for (const LLUUID& id : item_deletion_list) + { + remove_inventory_item(id, NULL); + } + for (const LLUUID& id : cat_deletion_list) + { + remove_inventory_category(id, NULL); + } + }); + } + + // Update the marketplace listings that have been affected by the operation + updateMarketplaceFolders(); + } +} + +void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) +{ + // Make a list of all marketplace folders containing the elements in the selected list + // as well as the elements themselves. + // Once those elements are updated (cut, delete in particular but potentially any action), their + // containing folder will need to be updated as well as their initially containing folder. For + // instance, moving a stock folder from a listed folder to another will require an update of the + // target listing *and* the original listing. So we need to keep track of both. + // Note: do not however put the marketplace listings root itself in this list or the whole marketplace data will be rebuilt. + sMarketplaceFolders.clear(); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (marketplacelistings_id.isNull()) + { + return; + } + + std::set selected_items = root->getSelectionList(); + std::set::iterator set_iter = selected_items.begin(); + LLFolderViewModelItemInventory * viewModel = NULL; + for (; set_iter != selected_items.end(); ++set_iter) + { + viewModel = dynamic_cast((*set_iter)->getViewModelItem()); + if (!viewModel || !viewModel->getInventoryObject()) continue; + if (gInventory.isObjectDescendentOf(viewModel->getInventoryObject()->getParentUUID(), marketplacelistings_id)) + { + const LLUUID &parent_id = viewModel->getInventoryObject()->getParentUUID(); + if (parent_id != marketplacelistings_id) + { + sMarketplaceFolders.push_back(parent_id); + } + const LLUUID &curr_id = viewModel->getInventoryObject()->getUUID(); + if (curr_id != marketplacelistings_id) + { + sMarketplaceFolders.push_back(curr_id); + } + } + } + // Suppress dupes in the list so we won't update listings twice + sMarketplaceFolders.sort(); + sMarketplaceFolders.unique(); +} + +void LLInventoryAction::updateMarketplaceFolders() +{ + while (!sMarketplaceFolders.empty()) + { + update_marketplace_category(sMarketplaceFolders.back()); + sMarketplaceFolders.pop_back(); + } +} + + -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/newview/llinventoryfunctions.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 5533c0328e..6e6448c0cd 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -156,7 +156,7 @@ S32 count_descendants_items(const LLUUID& cat_id) LLInventoryModel::item_array_t* item_array; gInventory.getDirectDescendentsOf(cat_id,cat_array,item_array); - S32 count = item_array->size(); + S32 count = static_cast(item_array->size()); 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++) @@ -1104,7 +1104,7 @@ S32 compute_stock_count(LLUUID cat_uuid, bool force_count /* false */) LLInventoryModel::cat_array_t* cat_array; LLInventoryModel::item_array_t* item_array; gInventory.getDirectDescendentsOf(cat_uuid,cat_array,item_array); - return item_array->size(); + return static_cast(item_array->size()); } // When force_count is true, we do not do any verification of the marketplace status and simply compute @@ -1358,7 +1358,7 @@ bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInve existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); existing_stock_count += count_stock_items(existing_items); - existing_folder_count += existing_categories.size(); + existing_folder_count += static_cast(existing_categories.size()); // If the incoming item is a nocopy (stock) item, we need to consider that it will create a stock folder if (!inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && !move_in_stock) @@ -1430,7 +1430,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn LLInventoryModel::item_array_t descendent_items; gInventory.collectDescendents(inv_cat->getUUID(), descendent_categories, descendent_items, false); - int dragged_folder_count = descendent_categories.size() + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... + int dragged_folder_count = static_cast(descendent_categories.size()) + bundle_size; // Note: We assume that we're moving a bunch of folders in. That might be wrong... int dragged_item_count = count_copyable_items(descendent_items) + count_stock_folders(descendent_categories); int dragged_stock_count = count_stock_items(descendent_items); int existing_item_count = 0; @@ -1452,7 +1452,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn LLInventoryModel::item_array_t existing_items; gInventory.collectDescendents(version_folder->getUUID(), existing_categories, existing_items, false); - existing_folder_count += existing_categories.size(); + existing_folder_count += static_cast(existing_categories.size()); existing_item_count += count_copyable_items(existing_items) + count_stock_folders(existing_categories); existing_stock_count += count_stock_items(existing_items); } @@ -1882,7 +1882,7 @@ void validate_marketplacelistings( } // How many types of items? Which type is it if only one? - S32 count = items_vector.size(); + auto count = items_vector.size(); U32 default_key = (U32)(LLInventoryType::IT_COUNT) << 24; // This is the key for any normal copyable item U32 unique_key = (count == 1 ? items_vector.begin()->first : default_key); // The key in the case of one item type only -- cgit v1.2.3 From c0fad3028fd55c2067ce6a0ae4382cffe1014284 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 10 Jun 2024 16:42:43 +0200 Subject: Re-enable compiler warnings C4018, C4100, C4231 and C4506 --- indra/newview/llinventoryfunctions.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'indra/newview/llinventoryfunctions.cpp') diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 6e6448c0cd..7f550bb511 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1333,12 +1333,12 @@ bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInve if (accept) { // If the dest folder is a stock folder, we do not count the incoming items toward the total (stock items are seen as one) - int existing_item_count = (move_in_stock ? 0 : bundle_size); + unsigned int existing_item_count = (move_in_stock ? 0 : bundle_size); // If the dest folder is a stock folder, we do assume that the incoming items are also stock items (they should anyway) - int existing_stock_count = (move_in_stock ? bundle_size : 0); + unsigned int existing_stock_count = (move_in_stock ? bundle_size : 0); - int existing_folder_count = 0; + unsigned int existing_folder_count = 0; // Get the version folder: that's where the counts start from const LLViewerInventoryCategory * version_folder = ((root_folder && (root_folder != dest_folder)) ? gInventory.getFirstDescendantOf(root_folder->getUUID(), dest_folder->getUUID()) : NULL); @@ -1457,9 +1457,9 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn existing_stock_count += count_stock_items(existing_items); } - const int total_folder_count = existing_folder_count + dragged_folder_count; - const int total_item_count = existing_item_count + dragged_item_count; - const int total_stock_count = existing_stock_count + dragged_stock_count; + const unsigned int total_folder_count = existing_folder_count + dragged_folder_count; + const unsigned int total_item_count = existing_item_count + dragged_item_count; + const unsigned int total_stock_count = existing_stock_count + dragged_stock_count; if (total_folder_count > gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) { -- cgit v1.2.3