diff options
Diffstat (limited to 'indra/newview/llinventoryfunctions.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/newview/llinventoryfunctions.cpp | 1226 |
1 files changed, 1024 insertions, 202 deletions
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 8f4136c01f..2546db546b 100644..100755 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1,32 +1,26 @@ /** - * @file llfloaterinventory.cpp + * @file llinventoryfunctions.cpp * @brief Implementation of the inventory view and associated stuff. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,6 +35,7 @@ #include "llagentwearables.h" #include "llcallingcard.h" #include "llfloaterreg.h" +#include "llinventorydefines.h" #include "llsdserialize.h" #include "llfiltereditor.h" #include "llspinctrl.h" @@ -50,20 +45,24 @@ // newview includes #include "llappearancemgr.h" #include "llappviewer.h" -#include "llfirstuse.h" -#include "llfloaterchat.h" -#include "llfloatercustomize.h" +#include "llavataractions.h" +#include "llclipboard.h" +#include "lldonotdisturbnotificationstorage.h" +#include "llfloaterinventory.h" +#include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "llfolderview.h" #include "llgesturemgr.h" #include "lliconctrl.h" #include "llimview.h" #include "llinventorybridge.h" -#include "llinventoryclipboard.h" #include "llinventorymodel.h" #include "llinventorypanel.h" #include "lllineeditor.h" +#include "llmarketplacenotifications.h" #include "llmenugl.h" +#include "llnotificationsutil.h" +#include "llpanelmaininventory.h" #include "llpreviewanim.h" #include "llpreviewgesture.h" #include "llpreviewnotecard.h" @@ -74,20 +73,888 @@ #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 "llviewerinventory.h" #include "llviewermessage.h" +#include "llviewerfoldertype.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llwearablelist.h" +#include <boost/foreach.hpp> + BOOL LLInventoryState::sWearNewClothing = FALSE; LLUUID LLInventoryState::sWearNewClothingTransactionID; +// Generates a string containing the path to the item specified by +// item_id. +void append_path(const LLUUID& id, std::string& path) +{ + std::string temp; + const LLInventoryObject* obj = gInventory.getObject(id); + LLUUID parent_id; + if(obj) parent_id = obj->getParentUUID(); + std::string forward_slash("/"); + while(obj) + { + obj = gInventory.getCategory(parent_id); + if(obj) + { + temp.assign(forward_slash + obj->getName() + temp); + parent_id = obj->getParentUUID(); + } + } + path.append(temp); +} + +void 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) +{ + // Create the initial folder + LLUUID new_cat_uuid = gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName()); + 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); + + // 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; + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + new_cat_uuid, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } + + // 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); + } + } +} + +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 = gInventory.getItem(id); + if (!item) + return FALSE; + + // Consider the item as worn if it has links in COF. + if (LLAppearanceMgr::instance().isLinkInCOF(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_can_item_be_worn(const LLUUID& id) +{ + const LLViewerInventoryItem* item = gInventory.getItem(id); + if (!item) + return FALSE; + + if (LLAppearanceMgr::isLinkInCOF(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) +{ + if (!model) + { + return FALSE; + } + + // Can't delete an item that's in the library. + if (!model->isObjectDescendentOf(id, gInventory.getRootFolderID())) + { + return FALSE; + } + + // Disable delete from COF folder; have users explicitly choose "detach/take off", + // unless the item is not worn but in the COF (i.e. is bugged). + if (LLAppearanceMgr::instance().getIsProtectedCOFItem(id)) + { + if (get_is_item_worn(id)) + { + return FALSE; + } + } + + const LLInventoryObject *obj = model->getItem(id); + if (obj && obj->getIsLinkType()) + { + return TRUE; + } + if (get_is_item_worn(id)) + { + return FALSE; + } + return TRUE; +} + +BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) +{ + // 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_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) +{ + LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", item_uuid).with("object", object_id)); +} + +void show_item_profile(const LLUUID& item_uuid) +{ + LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); + LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", linked_uuid)); +} + +void show_item_original(const LLUUID& item_uuid) +{ + LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + if (!floater_inventory) + { + LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; + return; + } + + //sidetray inventory panel + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); + + bool do_reset_inventory_filter = !floater_inventory->isInVisibleChain(); + + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (!active_panel) + { + //this may happen when there is no floatera and other panel is active in inventory tab + + if (sidepanel_inventory) + { + sidepanel_inventory->showInventoryPanel(); + } + } + + active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (!active_panel) + { + return; + } + active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO); + + if(do_reset_inventory_filter) + { + reset_inventory_filter(); + } +} + + +void reset_inventory_filter() +{ + //inventory floater + bool floater_inventory_visible = false; + + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloaterInventory* floater_inventory = dynamic_cast<LLFloaterInventory*>(*iter); + if (floater_inventory) + { + LLPanelMainInventory* main_inventory = floater_inventory->getMainInventoryPanel(); + + main_inventory->onFilterEdit(""); + + if(floater_inventory->getVisible()) + { + floater_inventory_visible = true; + } + } + } + + if(!floater_inventory_visible) + { + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory) + { + main_inventory->onFilterEdit(""); + } + } + } +} + +void open_outbox() +{ + LLFloaterReg::showInstance("outbox"); +} + +LLUUID create_folder_in_outbox_for_item(LLInventoryItem* item, const LLUUID& destFolderId, S32 operation_id) +{ + llassert(item); + llassert(destFolderId.notNull()); + + LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName()); + gInventory.notifyObservers(); + + LLNotificationsUtil::add("OutboxFolderCreated"); + + return created_folder_id; +} + +void move_to_outbox_cb_action(const LLSD& payload) +{ + LLViewerInventoryItem * viitem = gInventory.getItem(payload["item_id"].asUUID()); + LLUUID dest_folder_id = payload["dest_folder_id"].asUUID(); + + if (viitem) + { + // when moving item directly into outbox create folder with that name + if (dest_folder_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) + { + S32 operation_id = payload["operation_id"].asInteger(); + dest_folder_id = create_folder_in_outbox_for_item(viitem, dest_folder_id, operation_id); + } + + LLUUID parent = viitem->getParentUUID(); + + gInventory.changeItemParent( + viitem, + dest_folder_id, + false); + + LLUUID top_level_folder = payload["top_level_folder"].asUUID(); + + if (top_level_folder != LLUUID::null) + { + LLViewerInventoryCategory* category; + + while (parent.notNull()) + { + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(parent,cat_array,item_array); + + LLUUID next_parent; + + category = gInventory.getCategory(parent); + + if (!category) break; + + next_parent = category->getParentUUID(); + + if (cat_array->empty() && item_array->empty()) + { + gInventory.removeCategory(parent); + } + + if (parent == top_level_folder) + { + break; + } + + parent = next_parent; + } + } + + open_outbox(); + } +} + +void copy_item_to_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, const LLUUID& top_level_folder, S32 operation_id) +{ + // Collapse links directly to items/folders + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; + LLViewerInventoryCategory * linked_category = viewer_inv_item->getLinkedCategory(); + if (linked_category != NULL) + { + copy_folder_to_outbox(linked_category, dest_folder, top_level_folder, operation_id); + } + else + { + LLViewerInventoryItem * linked_item = viewer_inv_item->getLinkedItem(); + if (linked_item != NULL) + { + inv_item = (LLInventoryItem *) linked_item; + } + + // Check for copy permissions + if (inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) + { + // when moving item directly into outbox create folder with that name + if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) + { + dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); + } + + copy_inventory_item(gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + dest_folder, + inv_item->getName(), + LLPointer<LLInventoryCallback>(NULL)); + + open_outbox(); + } + else + { + LLSD payload; + payload["item_id"] = inv_item->getUUID(); + payload["dest_folder_id"] = dest_folder; + payload["top_level_folder"] = top_level_folder; + payload["operation_id"] = operation_id; + + LLMarketplaceInventoryNotifications::addNoCopyNotification(payload, move_to_outbox_cb_action); + } + } +} + +void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32 operation_id) +{ + // when moving item directly into outbox create folder with that name + if (dest_folder == gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false)) + { + dest_folder = create_folder_in_outbox_for_item(inv_item, dest_folder, operation_id); + } + + LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item; + + gInventory.changeItemParent( + viewer_inv_item, + dest_folder, + false); +} + +void copy_folder_to_outbox(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, const LLUUID& top_level_folder, S32 operation_id) +{ + LLUUID new_folder_id = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, inv_cat->getName()); + gInventory.notifyObservers(); + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(),cat_array,item_array); + + // copy the vector because otherwise the iterator won't be happy if we delete from it + 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; + copy_item_to_outbox(item, new_folder_id, top_level_folder, operation_id); + } + + 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; + copy_folder_to_outbox(category, new_folder_id, top_level_folder, operation_id); + } + + open_outbox(); +} + +///---------------------------------------------------------------------------- +/// 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 LLIsValidItemLink::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(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 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<LLViewerInventoryItem*>(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<LLViewerInventoryItem*>(item); + if (!vitem || vitem->getWearableType() != mWearableType) return false; + + return true; +} + +void LLFindWearablesOfType::setType(LLWearableType::EType type) +{ + mWearableType = type; +} + +bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + if (item) + { + return !get_is_item_removable(&gInventory, item->getUUID()); + } + if (cat) + { + return !get_is_category_removable(&gInventory, cat->getUUID()); + } + + 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; @@ -100,21 +967,24 @@ void LLSaveFolderState::setApply(BOOL apply) void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) { - LLMemType mt(LLMemType::MTYPE_INVENTORY_DO_FOLDER); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getViewModelItem(); + if(!bridge) return; + if(mApply) { // we're applying the open state - LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener(); - if(!bridge) return; LLUUID id(bridge->getUUID()); if(mOpenFolders.find(id) != mOpenFolders.end()) { - folder->setOpen(TRUE); + if (!folder->isOpen()) + { + folder->setOpen(TRUE); + } } else { // keep selected filter in its current state, this is less jarring to user - if (!folder->isSelected()) + if (!folder->isSelected() && folder->isOpen()) { folder->setOpen(FALSE); } @@ -125,8 +995,6 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) // we're recording state at this point if(folder->isOpen()) { - LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener(); - if(!bridge) return; mOpenFolders.insert(bridge->getUUID()); } } @@ -134,7 +1002,7 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) { - if (item->getFiltered()) + if (item->passedFilter()) { item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } @@ -142,12 +1010,12 @@ void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && folder->getParentFolder()) + 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->getFiltered() && !folder->hasFilteredDescendants()) + else if (!folder->getViewModelItem()->passedFilter() && !folder->getViewModelItem()->descendantsPassedFilter()) { folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_NO); } @@ -155,29 +1023,25 @@ void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) { - if (item->getFiltered() && !mItemSelected) + if (item->passedFilter() && !mItemSelected) { item->getRoot()->setSelection(item, FALSE, FALSE); if (item->getParentFolder()) { item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } - item->getRoot()->scrollToShowSelection(); mItemSelected = TRUE; } } void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && !mItemSelected) + // 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); - if (folder->getParentFolder()) - { - folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); - } - folder->getRoot()->scrollToShowSelection(); - mItemSelected = TRUE; + folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); + mFolderSelected = TRUE; } } @@ -197,174 +1061,132 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) } } -const std::string& get_item_icon_name(LLAssetType::EType asset_type, - LLInventoryType::EType inventory_type, - U32 attachment_point, - BOOL item_is_multi ) +void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action) { - EInventoryIcon idx = OBJECT_ICON_NAME; - if ( item_is_multi ) + if ("rename" == action) { - idx = OBJECT_MULTI_ICON_NAME; + root->startRenamingSelectedItem(); + return; } - - switch(asset_type) + if ("delete" == action) { - case LLAssetType::AT_TEXTURE: - if(LLInventoryType::IT_SNAPSHOT == inventory_type) - { - idx = SNAPSHOT_ICON_NAME; - } - else - { - idx = TEXTURE_ICON_NAME; - } - break; + LLSD args; + args["QUESTION"] = LLTrans::getString(root->getSelectedCount() > 1 ? "DeleteItems" : "DeleteItem"); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root)); + return; + } + if (("copy" == action) || ("cut" == action)) + { + // Clear the clipboard before we start adding things on it + LLClipboard::instance().reset(); + } - case LLAssetType::AT_SOUND: - idx = SOUND_ICON_NAME; - break; - case LLAssetType::AT_CALLINGCARD: - if(attachment_point!= 0) - { - idx = CALLINGCARD_ONLINE_ICON_NAME; - } - else - { - idx = CALLINGCARD_OFFLINE_ICON_NAME; - } - break; - case LLAssetType::AT_LANDMARK: - if(attachment_point!= 0) - { - idx = LANDMARK_VISITED_ICON_NAME; - } - else - { - idx = LANDMARK_ICON_NAME; - } - break; - case LLAssetType::AT_SCRIPT: - case LLAssetType::AT_LSL_TEXT: - case LLAssetType::AT_LSL_BYTECODE: - idx = SCRIPT_ICON_NAME; - break; - case LLAssetType::AT_CLOTHING: - idx = CLOTHING_ICON_NAME; - case LLAssetType::AT_BODYPART : - if(LLAssetType::AT_BODYPART == asset_type) - { - idx = BODYPART_ICON_NAME; - } - switch(LLInventoryItem::II_FLAGS_WEARABLES_MASK & attachment_point) - { - case WT_SHAPE: - idx = BODYPART_SHAPE_ICON_NAME; - break; - case WT_SKIN: - idx = BODYPART_SKIN_ICON_NAME; - break; - case WT_HAIR: - idx = BODYPART_HAIR_ICON_NAME; - break; - case WT_EYES: - idx = BODYPART_EYES_ICON_NAME; - break; - case WT_SHIRT: - idx = CLOTHING_SHIRT_ICON_NAME; - break; - case WT_PANTS: - idx = CLOTHING_PANTS_ICON_NAME; - break; - case WT_SHOES: - idx = CLOTHING_SHOES_ICON_NAME; - break; - case WT_SOCKS: - idx = CLOTHING_SOCKS_ICON_NAME; - break; - case WT_JACKET: - idx = CLOTHING_JACKET_ICON_NAME; - break; - case WT_GLOVES: - idx = CLOTHING_GLOVES_ICON_NAME; - break; - case WT_UNDERSHIRT: - idx = CLOTHING_UNDERSHIRT_ICON_NAME; - break; - case WT_UNDERPANTS: - idx = CLOTHING_UNDERPANTS_ICON_NAME; - break; - case WT_SKIRT: - idx = CLOTHING_SKIRT_ICON_NAME; - break; - case WT_ALPHA: - idx = CLOTHING_ALPHA_ICON_NAME; - break; - case WT_TATTOO: - idx = CLOTHING_TATTOO_ICON_NAME; - break; - default: - // no-op, go with choice above - break; - } - break; - case LLAssetType::AT_NOTECARD: - idx = NOTECARD_ICON_NAME; - break; - case LLAssetType::AT_ANIMATION: - idx = ANIMATION_ICON_NAME; - break; - case LLAssetType::AT_GESTURE: - idx = GESTURE_ICON_NAME; - break; - case LLAssetType::AT_LINK: - idx = LINKITEM_ICON_NAME; - break; - case LLAssetType::AT_LINK_FOLDER: - idx = LINKFOLDER_ICON_NAME; - break; - default: - break; + 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<LLFolderViewModelItemInventory*>(root->getViewModelItem()); + LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID()); + if (!cat) return; + cat->changeType(new_folder_type); + return; + } + + + std::set<LLFolderViewItem*> selected_items = root->getSelectionList(); + + LLMultiPreview* multi_previewp = NULL; + LLMultiProperties* multi_propertiesp = NULL; + + if (("task_open" == action || "open" == action) && selected_items.size() > 1) + { + multi_previewp = new LLMultiPreview(); + gFloaterView->addChild(multi_previewp); + + LLFloater::setFloaterHost(multi_previewp); + + } + else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) + { + multi_propertiesp = new LLMultiProperties(); + gFloaterView->addChild(multi_propertiesp); + + LLFloater::setFloaterHost(multi_propertiesp); + } + + + std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); + uuid_vec_t ids; + std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids)); + // 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 (action == "take_off" || action == "detach") + { + LLAppearanceMgr::instance().removeItemsFromAvatar(ids); + } + else + { + std::set<LLFolderViewItem*>::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); + } + } + + LLFloater::setFloaterHost(NULL); + if (multi_previewp) + { + multi_previewp->openFloater(LLSD()); + } + else if (multi_propertiesp) + { + multi_propertiesp->openFloater(LLSD()); } - - return ICON_NAME[idx]; } -LLUIImagePtr get_item_icon(LLAssetType::EType asset_type, - LLInventoryType::EType inventory_type, - U32 attachment_point, - BOOL item_is_multi) +void LLInventoryAction::removeItemFromDND(LLFolderView* root) { - const std::string& icon_name = get_item_icon_name(asset_type, inventory_type, attachment_point, item_is_multi ); - return LLUI::getUIImage(icon_name); + 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<LLFolderViewModelItemInventory *>((*it)->getViewModelItem()); + + if(viewModel && viewModel->getUUID().notNull()) + { + //Will remove the item offer notification + LLDoNotDisturbNotificationStorage::instance().removeNotification(LLDoNotDisturbNotificationStorage::offerName, viewModel->getUUID()); + } + } + } } -BOOL get_is_item_worn(const LLUUID& id) +void LLInventoryAction::onItemsRemovalConfirmation( const LLSD& notification, const LLSD& response, LLFolderView* root ) { - const LLViewerInventoryItem* item = gInventory.getItem(id); - if (!item) - return FALSE; - - switch(item->getType()) + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) { - case LLAssetType::AT_OBJECT: - { - const LLVOAvatarSelf* my_avatar = gAgent.getAvatarObject(); - if(my_avatar && my_avatar->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 (LLGestureManager::instance().isGestureActive(item->getLinkedUUID())) - return TRUE; - break; - default: - break; + //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(root); + root->removeSelectedItems(); } - return FALSE; } |