790 lines
30 KiB
C++
790 lines
30 KiB
C++
/**
|
|
*
|
|
* Copyright (c) 2009-2011, Kitty Barnett
|
|
*
|
|
* The source code in this file is provided to you under the terms of the
|
|
* GNU General Public License, version 2.0, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
* PARTICULAR PURPOSE. Terms of the GPL can be found in doc/GPL-license.txt
|
|
* in this distribution, or online at http://www.gnu.org/licenses/gpl-2.0.txt
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
#include "llagent.h"
|
|
#include "llappearancemgr.h"
|
|
#include "llinventoryobserver.h"
|
|
#include "llstartup.h"
|
|
#include "llviewerfoldertype.h"
|
|
#include "llviewerobject.h"
|
|
#include "llvoavatarself.h"
|
|
|
|
#include "rlvdefines.h"
|
|
#include "rlvcommon.h"
|
|
#include "rlvlocks.h"
|
|
#include "rlvinventory.h"
|
|
#include "rlvhandler.h"
|
|
|
|
#include "boost/algorithm/string.hpp"
|
|
|
|
// ============================================================================
|
|
// Static variable initialization
|
|
//
|
|
|
|
const std::string RlvInventory::cstrSharedRoot = RLV_ROOT_FOLDER;
|
|
|
|
// ============================================================================
|
|
// Helper classes
|
|
//
|
|
|
|
// TODO-RLVa: [RLVa-1.2.1] This class really shouldn't be calling "fetchSharedLinks" directly so find a better way
|
|
class RlvSharedInventoryFetcher : public LLInventoryFetchDescendentsObserver
|
|
{
|
|
public:
|
|
RlvSharedInventoryFetcher(const uuid_vec_t& idFolders): LLInventoryFetchDescendentsObserver(idFolders) {}
|
|
virtual ~RlvSharedInventoryFetcher() {}
|
|
|
|
virtual void done()
|
|
{
|
|
RLV_INFOS << "Shared folders fetch completed" << LL_ENDL;
|
|
RlvInventory::instance().m_fFetchComplete = true;
|
|
RlvInventory::instance().fetchSharedLinks();
|
|
|
|
gInventory.removeObserver(this);
|
|
delete this;
|
|
}
|
|
};
|
|
|
|
// ============================================================================
|
|
// RlvInventory member functions
|
|
//
|
|
|
|
// Checked: 2011-03-28 (RLVa-1.3.0g) | Modified: RLVa-1.3.0g
|
|
RlvInventory::RlvInventory()
|
|
: m_fFetchStarted(false), m_fFetchComplete(false)
|
|
{
|
|
}
|
|
|
|
// Checked: 2011-03-28 (RLVa-1.3.0g) | Added: RLVa-1.3.0g
|
|
RlvInventory::~RlvInventory()
|
|
{
|
|
if (gInventory.containsObserver(this))
|
|
gInventory.removeObserver(this);
|
|
}
|
|
|
|
// Checked: 2011-03-28 (RLVa-1.3.0g) | Added: RLVa-1.3.0g
|
|
void RlvInventory::changed(U32 mask)
|
|
{
|
|
const LLInventoryModel::changed_items_t& idsChanged = gInventory.getChangedIDs();
|
|
if (std::find(idsChanged.begin(), idsChanged.end(), m_idRlvRoot) != idsChanged.end())
|
|
{
|
|
gInventory.removeObserver(this);
|
|
|
|
LLUUID idRlvRootPrev = m_idRlvRoot;
|
|
m_idRlvRoot.setNull();
|
|
|
|
if (idRlvRootPrev != getSharedRootID())
|
|
m_OnSharedRootIDChanged();
|
|
}
|
|
}
|
|
|
|
// Checked: 2010-02-28 (RLVa-1.2.0a) | Modified: RLVa-1.0.0h
|
|
void RlvInventory::fetchSharedInventory()
|
|
{
|
|
// Sanity check - don't fetch if we're already fetching, or if we don't have a shared root
|
|
const LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
|
|
if ( (m_fFetchStarted) || (!pRlvRoot) )
|
|
return;
|
|
|
|
// Grab all the folders under the shared root
|
|
LLInventoryModel::cat_array_t folders; LLInventoryModel::item_array_t items;
|
|
gInventory.collectDescendents(pRlvRoot->getUUID(), folders, items, FALSE);
|
|
|
|
// Add them to the "to fetch" list
|
|
uuid_vec_t idFolders;
|
|
idFolders.push_back(pRlvRoot->getUUID());
|
|
for (S32 idxFolder = 0, cntFolder = folders.count(); idxFolder < cntFolder; idxFolder++)
|
|
idFolders.push_back(folders.get(idxFolder)->getUUID());
|
|
|
|
// Now fetch them all in one go
|
|
RlvSharedInventoryFetcher* pFetcher = new RlvSharedInventoryFetcher(idFolders);
|
|
|
|
RLV_INFOS << "Starting fetch of " << idFolders.size() << " shared folders" << RLV_ENDL;
|
|
pFetcher->startFetch();
|
|
m_fFetchStarted = true;
|
|
|
|
if (pFetcher->isFinished())
|
|
pFetcher->done();
|
|
else
|
|
gInventory.addObserver(pFetcher);
|
|
}
|
|
|
|
// Checked: 2010-02-28 (RLVa-1.2.0a) | Modified: RLVa-1.0.0h
|
|
void RlvInventory::fetchSharedLinks()
|
|
{
|
|
// TOFIX-RLVa: [RLVa-1.2.1] Finish adding support for AT_LINK_FOLDER
|
|
const LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
|
|
if (!pRlvRoot)
|
|
return;
|
|
|
|
// Grab all the inventory links under the shared root
|
|
LLInventoryModel::cat_array_t folders; LLInventoryModel::item_array_t items; RlvIsLinkType f;
|
|
gInventory.collectDescendentsIf(pRlvRoot->getUUID(), folders, items, FALSE, f, FALSE);
|
|
|
|
// Add them to the "to fetch" list based on link type
|
|
uuid_vec_t idFolders, idItems;
|
|
for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
|
|
{
|
|
const LLViewerInventoryItem* pItem = items.get(idxItem);
|
|
switch (pItem->getActualType())
|
|
{
|
|
case LLAssetType::AT_LINK:
|
|
idItems.push_back(pItem->getLinkedUUID());
|
|
break;
|
|
case LLAssetType::AT_LINK_FOLDER:
|
|
idFolders.push_back(pItem->getLinkedUUID());
|
|
break;
|
|
default:
|
|
break;;
|
|
}
|
|
}
|
|
|
|
RLV_INFOS << "Starting link target fetch of " << idItems.size() << " items and " << idFolders.size() << " folders" << RLV_ENDL;
|
|
|
|
// Fetch all the link item targets
|
|
LLInventoryFetchItemsObserver itemFetcher(idItems);
|
|
itemFetcher.startFetch();
|
|
|
|
// Fetch all the link folder targets
|
|
// TODO!
|
|
}
|
|
|
|
// Checked: 2010-02-28 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a
|
|
void RlvInventory::fetchWornItems()
|
|
{
|
|
uuid_vec_t idItems;
|
|
|
|
// Fetch all currently worn clothing layers and body parts
|
|
for (int type = 0; type < LLWearableType::WT_COUNT; type++)
|
|
{
|
|
// RELEASE-RLVa: [SL-2.0.0] Needs rewriting once 'LLAgentWearables::MAX_WEARABLES_PER_TYPE > 1'
|
|
const LLUUID& idItem = gAgentWearables.getWearableItemID((LLWearableType::EType)type, 0);
|
|
if (idItem.notNull())
|
|
idItems.push_back(idItem);
|
|
}
|
|
|
|
// Fetch all currently worn attachments
|
|
if (isAgentAvatarValid())
|
|
{
|
|
for (LLVOAvatar::attachment_map_t::const_iterator itAttachPt = gAgentAvatarp->mAttachmentPoints.begin();
|
|
itAttachPt != gAgentAvatarp->mAttachmentPoints.end(); ++itAttachPt)
|
|
{
|
|
const LLViewerJointAttachment* pAttachPt = itAttachPt->second;
|
|
if (pAttachPt)
|
|
{
|
|
for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator itAttachObj = pAttachPt->mAttachedObjects.begin();
|
|
itAttachObj != pAttachPt->mAttachedObjects.end(); ++itAttachObj)
|
|
{
|
|
const LLViewerObject* pAttachObj = (*itAttachObj);
|
|
if ( (pAttachObj) && (pAttachObj->getAttachmentItemID().notNull()) )
|
|
idItems.push_back(pAttachObj->getAttachmentItemID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LLInventoryFetchItemsObserver itemFetcher(idItems);
|
|
itemFetcher.startFetch();
|
|
}
|
|
|
|
// Checked: 2010-04-07 (RLVa-1.2.0a) | Modified: RLVa-1.0.0h
|
|
bool RlvInventory::findSharedFolders(const std::string& strCriteria, LLInventoryModel::cat_array_t& folders) const
|
|
{
|
|
// Sanity check - can't do anything without a shared root
|
|
const LLViewerInventoryCategory* pRlvRoot = RlvInventory::instance().getSharedRoot();
|
|
if (!pRlvRoot)
|
|
return false;
|
|
|
|
folders.clear();
|
|
LLInventoryModel::item_array_t items;
|
|
RlvCriteriaCategoryCollector f(strCriteria);
|
|
gInventory.collectDescendentsIf(pRlvRoot->getUUID(), folders, items, FALSE, f);
|
|
|
|
return (folders.count() != 0);
|
|
}
|
|
|
|
// Checked: 2010-08-30 (RLVa-1.2.1c) | Modified: RLVa-1.2.1c
|
|
bool RlvInventory::getPath(const uuid_vec_t& idItems, LLInventoryModel::cat_array_t& folders) const
|
|
{
|
|
// Sanity check - can't do anything without a shared root
|
|
const LLViewerInventoryCategory* pRlvRoot = RlvInventory::instance().getSharedRoot();
|
|
if (!pRlvRoot)
|
|
return false;
|
|
|
|
folders.clear();
|
|
for (uuid_vec_t::const_iterator itItem = idItems.begin(); itItem != idItems.end(); ++itItem)
|
|
{
|
|
const LLInventoryItem* pItem = gInventory.getItem(*itItem);
|
|
if ( (pItem) && (gInventory.isObjectDescendentOf(pItem->getUUID(), pRlvRoot->getUUID())) )
|
|
{
|
|
// If the containing folder is a folded folder we need its parent
|
|
LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
|
|
if (RlvInventory::instance().isFoldedFolder(pFolder, true))
|
|
pFolder = gInventory.getCategory(pFolder->getParentUUID());
|
|
folders.push_back(pFolder);
|
|
}
|
|
}
|
|
|
|
return (folders.count() != 0);
|
|
}
|
|
|
|
// Checked: 2011-03-28 (RLVa-1.3.0g) | Modified: RLVa-1.3.0g
|
|
const LLUUID& RlvInventory::getSharedRootID() const
|
|
{
|
|
if ( (m_idRlvRoot.isNull()) && (gInventory.isInventoryUsable()) )
|
|
{
|
|
LLInventoryModel::cat_array_t* pFolders; LLInventoryModel::item_array_t* pItems;
|
|
gInventory.getDirectDescendentsOf(gInventory.getRootFolderID(), pFolders, pItems);
|
|
if (pFolders)
|
|
{
|
|
// NOTE: we might have multiple #RLV folders (pick the first one with sub-folders; otherwise the last one with no sub-folders)
|
|
const LLViewerInventoryCategory* pFolder;
|
|
for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++)
|
|
{
|
|
if ( ((pFolder = pFolders->get(idxFolder)) != NULL) && (cstrSharedRoot == pFolder->getName()) )
|
|
{
|
|
m_idRlvRoot = pFolder->getUUID();
|
|
if (getDirectDescendentsFolderCount(pFolder) > 0)
|
|
break;
|
|
}
|
|
}
|
|
if ( (m_idRlvRoot.notNull()) && (!gInventory.containsObserver((RlvInventory*)this)) )
|
|
gInventory.addObserver((RlvInventory*)this);
|
|
}
|
|
}
|
|
return m_idRlvRoot;
|
|
}
|
|
|
|
// Checked: 2010-02-28 (RLVa-1.2.0a) | Modified: RLVa-1.0.1a
|
|
LLViewerInventoryCategory* RlvInventory::getSharedFolder(const LLUUID& idParent, const std::string& strFolderName) const
|
|
{
|
|
LLInventoryModel::cat_array_t* pFolders; LLInventoryModel::item_array_t* pItems;
|
|
gInventory.getDirectDescendentsOf(idParent, pFolders, pItems);
|
|
if ( (!pFolders) || (strFolderName.empty()) )
|
|
return NULL;
|
|
|
|
// If we can't find an exact match then we'll settle for a "contains" match
|
|
LLViewerInventoryCategory* pPartial = NULL;
|
|
|
|
//LLStringUtil::toLower(strFolderName); <- everything was already converted to lower case before
|
|
|
|
std::string strName;
|
|
for (S32 idxFolder = 0, cntFolder = pFolders->count(); idxFolder < cntFolder; idxFolder++)
|
|
{
|
|
LLViewerInventoryCategory* pFolder = pFolders->get(idxFolder);
|
|
|
|
strName = pFolder->getName();
|
|
if (strName.empty())
|
|
continue;
|
|
LLStringUtil::toLower(strName);
|
|
|
|
if (strFolderName == strName)
|
|
return pFolder; // Found an exact match, no need to keep on going
|
|
else if ( (!pPartial) && (RLV_FOLDER_PREFIX_HIDDEN != strName[0]) && (std::string::npos != strName.find(strFolderName)) )
|
|
pPartial = pFolder; // Found a partial (non-hidden) match, but we might still find an exact one (first partial match wins)
|
|
}
|
|
|
|
return pPartial;
|
|
}
|
|
|
|
// Checked: 2010-02-28 (RLVa-1.2.0a) | Modified: RLVa-0.2.0e
|
|
LLViewerInventoryCategory* RlvInventory::getSharedFolder(const std::string& strPath) const
|
|
{
|
|
// Sanity check - no shared root => no shared folder
|
|
LLViewerInventoryCategory* pRlvRoot = getSharedRoot(), *pFolder = pRlvRoot;
|
|
if (!pRlvRoot)
|
|
return NULL;
|
|
|
|
// Walk the path (starting at the root)
|
|
boost_tokenizer tokens(strPath, boost::char_separator<char>("/", "", boost::drop_empty_tokens));
|
|
for (boost_tokenizer::const_iterator itToken = tokens.begin(); itToken != tokens.end(); ++itToken)
|
|
{
|
|
pFolder = getSharedFolder(pFolder->getUUID(), *itToken);
|
|
if (!pFolder)
|
|
return NULL; // No such folder
|
|
}
|
|
|
|
return pFolder; // If strPath was empty or just a bunch of //// then: pFolder == pRlvRoot
|
|
}
|
|
|
|
// Checked: 2010-03-02 (RLVa-1.1.3a) | Modified: RLVa-0.2.0g
|
|
std::string RlvInventory::getSharedPath(const LLViewerInventoryCategory* pFolder) const
|
|
{
|
|
// Sanity check - no shared root or no folder => no path
|
|
const LLViewerInventoryCategory* pRlvRoot = getSharedRoot();
|
|
if ( (!pRlvRoot) || (!pFolder) || (pRlvRoot->getUUID() == pFolder->getUUID()) )
|
|
return std::string();
|
|
|
|
const LLUUID& idRLV = pRlvRoot->getUUID();
|
|
const LLUUID& idRoot = gInventory.getRootFolderID();
|
|
std::string strPath;
|
|
|
|
// Walk up the tree until we reach the top
|
|
RLV_ASSERT(gInventory.isObjectDescendentOf(pFolder->getUUID(), pRlvRoot->getUUID()));
|
|
while (pFolder)
|
|
{
|
|
strPath = "/" + pFolder->getName() + strPath;
|
|
|
|
const LLUUID& idParent = pFolder->getParentUUID();
|
|
if (idRLV == idParent) // Reached the shared root, we're done
|
|
break;
|
|
else if (idRoot == idParent) // We reached the agent's inventory root (indicative of a logic error elsewhere)
|
|
return std::string();
|
|
|
|
pFolder = gInventory.getCategory(idParent);
|
|
}
|
|
|
|
return strPath.erase(0, 1);
|
|
}
|
|
|
|
// Checked: 2011-10-06 (RLVa-1.4.2a) | Added: RLVa-1.4.2a
|
|
S32 RlvInventory::getDirectDescendentsFolderCount(const LLInventoryCategory* pFolder)
|
|
{
|
|
LLInventoryModel::cat_array_t* pFolders = NULL; LLInventoryModel::item_array_t* pItems = NULL;
|
|
if (pFolder)
|
|
gInventory.getDirectDescendentsOf(pFolder->getUUID(), pFolders, pItems);
|
|
return (pFolders) ? pFolders->size() : 0;
|
|
}
|
|
|
|
// Checked: 2009-05-26 (RLVa-0.2.0d) | Modified: RLVa-0.2.0d
|
|
S32 RlvInventory::getDirectDescendentsItemCount(const LLInventoryCategory* pFolder, LLAssetType::EType filterType)
|
|
{
|
|
S32 cntType = 0;
|
|
if (pFolder)
|
|
{
|
|
LLInventoryModel::cat_array_t* pFolders; LLInventoryModel::item_array_t* pItems;
|
|
gInventory.getDirectDescendentsOf(pFolder->getUUID(), pFolders, pItems);
|
|
|
|
if (pItems)
|
|
{
|
|
for (S32 idxItem = 0, cntItem = pItems->count(); idxItem < cntItem; idxItem++)
|
|
if (pItems->get(idxItem)->getType() == filterType)
|
|
cntType++;
|
|
}
|
|
}
|
|
return cntType;
|
|
}
|
|
|
|
// ============================================================================
|
|
// RlvRenameOnWearObserver member functions
|
|
//
|
|
|
|
// Checked: 2010-03-14 (RLVa-1.2.0a) | Added: RLVa-1.2.0a
|
|
void RlvRenameOnWearObserver::done()
|
|
{
|
|
gInventory.removeObserver(this);
|
|
|
|
// We shouldn't be messing with inventory items during LLInventoryModel::notifyObservers()
|
|
doOnIdleOneTime(boost::bind(&RlvRenameOnWearObserver::doneIdle, this));
|
|
}
|
|
|
|
// Checked: 2010-03-14 (RLVa-1.1.3a) | Added: RLVa-1.2.0a
|
|
void RlvRenameOnWearObserver::doneIdle()
|
|
{
|
|
const LLViewerInventoryCategory* pRlvRoot = NULL;
|
|
if ( (RlvSettings::getEnableSharedWear()) || (!RlvSettings::getSharedInvAutoRename()) || (LLStartUp::getStartupState() < STATE_STARTED) ||
|
|
(!isAgentAvatarValid()) || ((pRlvRoot = RlvInventory::instance().getSharedRoot()) == NULL) )
|
|
{
|
|
delete this;
|
|
return;
|
|
}
|
|
|
|
const LLViewerJointAttachment* pAttachPt = NULL; S32 idxAttachPt = 0;
|
|
RLV_ASSERT(mComplete.size() > 0); // Catch instances where we forgot to call startFetch()
|
|
for (uuid_vec_t::const_iterator itItem = mComplete.begin(); itItem != mComplete.end(); ++itItem)
|
|
{
|
|
const LLUUID& idAttachItem = *itItem;
|
|
|
|
// If the item resides under #RLV we'll rename it directly; otherwise settle for "renaming" all of its links residing under #RLV
|
|
LLInventoryModel::item_array_t items;
|
|
if (gInventory.isObjectDescendentOf(idAttachItem, pRlvRoot->getUUID()))
|
|
items.push_back(gInventory.getItem(idAttachItem));
|
|
else
|
|
items = gInventory.collectLinkedItems(idAttachItem, pRlvRoot->getUUID());
|
|
if (items.empty())
|
|
continue;
|
|
|
|
if ( ((pAttachPt = gAgentAvatarp->getWornAttachmentPoint(idAttachItem)) == NULL) ||
|
|
((idxAttachPt = RlvAttachPtLookup::getAttachPointIndex(pAttachPt)) == 0) )
|
|
{
|
|
RLV_ASSERT(false);
|
|
continue;
|
|
}
|
|
|
|
for (S32 idxItem = 0, cntItem = items.count(); idxItem < cntItem; idxItem++)
|
|
{
|
|
LLViewerInventoryItem* pItem = items.get(idxItem);
|
|
if (!pItem)
|
|
continue;
|
|
|
|
S32 idxAttachPtItem = RlvAttachPtLookup::getAttachPointIndex(pItem);
|
|
if ( (idxAttachPt == idxAttachPtItem) || (idxAttachPtItem) )
|
|
continue;
|
|
|
|
std::string strAttachPt = pAttachPt->getName();
|
|
LLStringUtil::toLower(strAttachPt);
|
|
|
|
// If we can modify the item then we rename it directly, otherwise we create a new folder and move it
|
|
if (pItem->getPermissions().allowModifyBy(gAgent.getID()))
|
|
{
|
|
std::string strName = pItem->getName();
|
|
LLStringUtil::truncate(strName, DB_INV_ITEM_NAME_STR_LEN - strAttachPt.length() - 3);
|
|
|
|
strName += " (" + strAttachPt + ")";
|
|
|
|
pItem->rename(strName);
|
|
pItem->updateServer(FALSE);
|
|
gInventory.addChangedMask(LLInventoryObserver::LABEL, pItem->getUUID());
|
|
}
|
|
else
|
|
{
|
|
// Don't do anything if the item is a direct descendant of the shared root, or a folded folder
|
|
LLViewerInventoryCategory* pFolder = gInventory.getCategory(pItem->getParentUUID());
|
|
if ( (pFolder) && (pFolder->getUUID() != pRlvRoot->getUUID()) && (!RlvInventory::isFoldedFolder(pFolder, false)) )
|
|
{
|
|
std::string strFolderName = ".(" + strAttachPt + ")";
|
|
|
|
// Rename the item's parent folder if it's called "New Folder", isn't directly under #RLV and contains exactly 1 object
|
|
if ( (LLViewerFolderType::lookupNewCategoryName(LLFolderType::FT_NONE) == pFolder->getName()) &&
|
|
(pFolder->getParentUUID() != pRlvRoot->getUUID()) &&
|
|
(1 == RlvInventory::getDirectDescendentsItemCount(pFolder, LLAssetType::AT_OBJECT)) )
|
|
{
|
|
pFolder->rename(strFolderName);
|
|
pFolder->updateServer(FALSE);
|
|
gInventory.addChangedMask(LLInventoryObserver::LABEL, pFolder->getUUID());
|
|
}
|
|
else
|
|
{
|
|
// "No modify" item with a non-renameable parent: create a new folder named and move the item into it
|
|
LLUUID idFolder = gInventory.createNewCategory(pFolder->getUUID(), LLFolderType::FT_NONE, strFolderName,
|
|
&RlvRenameOnWearObserver::onCategoryCreate, new LLUUID(pItem->getUUID()));
|
|
if (idFolder.notNull())
|
|
{
|
|
// Not using the new 'CreateInventoryCategory' cap so manually invoke the callback
|
|
RlvRenameOnWearObserver::onCategoryCreate(LLSD().with("folder_id", idFolder), new LLUUID(pItem->getUUID()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
gInventory.notifyObservers();
|
|
|
|
delete this;
|
|
}
|
|
|
|
// Checked: 2012-03-22 (RLVa-1.4.6) | Added: RLVa-1.4.6
|
|
void RlvRenameOnWearObserver::onCategoryCreate(const LLSD& sdData, void* pParam)
|
|
{
|
|
LLUUID idFolder = sdData["folder_id"].asUUID();
|
|
LLUUID* pidItem = (LLUUID*)pParam;
|
|
|
|
if ( (idFolder.notNull()) && (pidItem) && (pidItem->notNull()) )
|
|
move_inventory_item(gAgent.getID(), gAgent.getSessionID(), *pidItem, idFolder, std::string(), NULL);
|
|
|
|
delete pidItem;
|
|
}
|
|
|
|
// ============================================================================
|
|
// "Give to #RLV" helper classes
|
|
//
|
|
|
|
// Checked: 2010-04-18 (RLVa-1.2.0e) | Added: RLVa-1.2.0e
|
|
void RlvGiveToRLVTaskOffer::changed(U32 mask)
|
|
{
|
|
if (mask & LLInventoryObserver::ADD)
|
|
{
|
|
LLMessageSystem* pMsg = gMessageSystem;
|
|
if ( (pMsg->getMessageName()) && (0 == strcmp(pMsg->getMessageName(), "BulkUpdateInventory")) )
|
|
{
|
|
LLUUID idTransaction;
|
|
pMsg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, idTransaction);
|
|
if(m_idTransaction == idTransaction)
|
|
{
|
|
LLUUID idInvObject;
|
|
for(S32 idxBlock = 0, cntBlock = pMsg->getNumberOfBlocksFast(_PREHASH_FolderData); idxBlock < cntBlock; idxBlock++)
|
|
{
|
|
pMsg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, idInvObject, idxBlock);
|
|
if ( (idInvObject.notNull()) && (std::find(m_Folders.begin(), m_Folders.end(), idInvObject) == m_Folders.end()) )
|
|
m_Folders.push_back(idInvObject);
|
|
}
|
|
|
|
done();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Checked: 2010-04-18 (RLVa-1.2.0e) | Added: RLVa-1.2.0e
|
|
void RlvGiveToRLVTaskOffer::done()
|
|
{
|
|
gInventory.removeObserver(this);
|
|
|
|
// We shouldn't be messing with inventory items during LLInventoryModel::notifyObservers()
|
|
doOnIdleOneTime(boost::bind(&RlvGiveToRLVTaskOffer::doneIdle, this));
|
|
}
|
|
|
|
// Checked: 2010-04-18 (RLVa-1.2.0e) | Added: RLVa-1.2.0e
|
|
void RlvGiveToRLVTaskOffer::doneIdle()
|
|
{
|
|
const LLViewerInventoryCategory* pRlvRoot = RlvInventory::instance().getSharedRoot();
|
|
if (pRlvRoot)
|
|
{
|
|
for (folder_ref_t::const_iterator itFolder = m_Folders.begin(); itFolder != m_Folders.end(); ++itFolder)
|
|
{
|
|
const LLViewerInventoryCategory* pFolder = gInventory.getCategory(*itFolder);
|
|
if (!pFolder)
|
|
continue;
|
|
|
|
std::string strFolder = pFolder->getName();
|
|
if ( (pRlvRoot->getUUID() == pFolder->getParentUUID() ) && (strFolder.find(RLV_PUTINV_PREFIX) == 0))
|
|
{
|
|
LLPointer<LLViewerInventoryCategory> pNewFolder = new LLViewerInventoryCategory(pFolder);
|
|
pNewFolder->rename(strFolder.erase(0, strFolder.find(RLV_FOLDER_PREFIX_PUTINV)));
|
|
pNewFolder->updateServer(FALSE);
|
|
gInventory.updateCategory(pNewFolder);
|
|
|
|
RlvBehaviourNotifyHandler::sendNotification("accepted_in_rlv inv_offer " + pNewFolder->getName());
|
|
|
|
gInventory.notifyObservers();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
delete this;
|
|
}
|
|
|
|
// Checked: 2010-04-18 (RLVa-1.2.0e) | Modified: RLVa-1.2.0e
|
|
void RlvGiveToRLVAgentOffer::done()
|
|
{
|
|
gInventory.removeObserver(this);
|
|
|
|
// We shouldn't be messing with inventory items during LLInventoryModel::notifyObservers()
|
|
doOnIdleOneTime(boost::bind(&RlvGiveToRLVAgentOffer::doneIdle, this));
|
|
}
|
|
|
|
// Checked: 2010-04-18 (RLVa-1.1.3a) | Modified: RLVa-1.2.0e
|
|
void RlvGiveToRLVAgentOffer::doneIdle()
|
|
{
|
|
const LLViewerInventoryCategory* pRlvRoot = RlvInventory::instance().getSharedRoot();
|
|
const LLViewerInventoryCategory* pFolder = (mComplete.size()) ? gInventory.getCategory(mComplete[0]) : NULL;
|
|
if ( (pRlvRoot) && (pFolder) )
|
|
{
|
|
std::string strName = pFolder->getName();
|
|
if (strName.find(RLV_PUTINV_PREFIX) == 0)
|
|
{
|
|
LLInventoryModel::update_list_t update;
|
|
LLInventoryModel::LLCategoryUpdate updOldParent(pFolder->getParentUUID(), -1);
|
|
update.push_back(updOldParent);
|
|
LLInventoryModel::LLCategoryUpdate updNewParent(pRlvRoot->getUUID(), 1);
|
|
update.push_back(updNewParent);
|
|
gInventory.accountForUpdate(update);
|
|
|
|
LLPointer<LLViewerInventoryCategory> pNewFolder = new LLViewerInventoryCategory(pFolder);
|
|
pNewFolder->setParent(pRlvRoot->getUUID());
|
|
pNewFolder->updateParentOnServer(FALSE);
|
|
pNewFolder->rename(strName.erase(0, strName.find(RLV_FOLDER_PREFIX_PUTINV)));
|
|
pNewFolder->updateServer(FALSE);
|
|
gInventory.updateCategory(pNewFolder);
|
|
|
|
gInventory.notifyObservers();
|
|
}
|
|
}
|
|
delete this;
|
|
}
|
|
|
|
// ============================================================================
|
|
// RlvWearableItemCollector
|
|
//
|
|
|
|
// Checked: 2010-09-25 (RLVa-1.2.1c) | Added: RLVa-1.2.1c
|
|
RlvWearableItemCollector::RlvWearableItemCollector(const LLInventoryCategory* pFolder, RlvForceWear::EWearAction eAction, RlvForceWear::EWearFlags eFlags)
|
|
: m_idFolder(pFolder->getUUID()), m_eWearAction(eAction), m_eWearFlags(eFlags),
|
|
m_strWearAddPrefix(RlvSettings::getWearAddPrefix()), m_strWearReplacePrefix(RlvSettings::getWearReplacePrefix())
|
|
{
|
|
m_Wearable.push_back(m_idFolder);
|
|
|
|
// Wear prefixes can't/shouldn't start with '.'
|
|
if ( (m_strWearAddPrefix.length() > 1) && (RLV_FOLDER_PREFIX_HIDDEN == m_strWearAddPrefix[0]) )
|
|
m_strWearAddPrefix.clear();
|
|
if ( (m_strWearReplacePrefix.length() > 1) && (RLV_FOLDER_PREFIX_HIDDEN == m_strWearReplacePrefix[0]) )
|
|
m_strWearReplacePrefix.clear();
|
|
|
|
// If there's a prefix on the "root" folder then it will override what we were passed in the constructor
|
|
m_eWearAction = getWearActionNormal(pFolder);
|
|
m_WearActionMap.insert(std::pair<LLUUID, RlvForceWear::EWearAction>(m_idFolder, m_eWearAction));
|
|
}
|
|
|
|
// Checked: 2010-09-25 (RLVa-1.2.1c) | Added: RLVa-1.2.1c
|
|
RlvForceWear::EWearAction RlvWearableItemCollector::getWearActionNormal(const LLInventoryCategory* pFolder)
|
|
{
|
|
RLV_ASSERT_DBG(!RlvInventory::isFoldedFolder(pFolder, false));
|
|
if ( (RlvForceWear::ACTION_WEAR_REPLACE == m_eWearAction) && (!m_strWearAddPrefix.empty()) &&
|
|
(boost::algorithm::starts_with(pFolder->getName(), m_strWearAddPrefix)))
|
|
{
|
|
return RlvForceWear::ACTION_WEAR_ADD;
|
|
}
|
|
else if ( (RlvForceWear::ACTION_WEAR_ADD == m_eWearAction) && (!m_strWearReplacePrefix.empty()) &&
|
|
(boost::algorithm::starts_with(pFolder->getName(), m_strWearReplacePrefix)) )
|
|
{
|
|
return RlvForceWear::ACTION_WEAR_REPLACE;
|
|
}
|
|
return (pFolder->getUUID() != m_idFolder) ? getWearAction(pFolder->getParentUUID()) : m_eWearAction;
|
|
}
|
|
|
|
// Checked: 2010-04-07 (RLVa-1.2.0d) | Added: RLVa-0.2.0e
|
|
const LLUUID& RlvWearableItemCollector::getFoldedParent(const LLUUID& idFolder) const
|
|
{
|
|
std::map<LLUUID, LLUUID>::const_iterator itFolder = m_FoldingMap.end(), itCur = m_FoldingMap.find(idFolder);
|
|
while (itCur != m_FoldingMap.end())
|
|
{
|
|
itFolder = itCur;
|
|
itCur = m_FoldingMap.find(itFolder->second);
|
|
}
|
|
return (m_FoldingMap.end() == itFolder) ? idFolder : itFolder->second;
|
|
}
|
|
|
|
// Checked: 2010-09-25 (RLVa-1.1.3a) | Added: RLVa-1.2.1c
|
|
RlvForceWear::EWearAction RlvWearableItemCollector::getWearAction(const LLUUID& idFolder) const
|
|
{
|
|
LLUUID idCurFolder(idFolder); std::map<LLUUID, RlvForceWear::EWearAction>::const_iterator itCurFolder;
|
|
while ((itCurFolder = m_WearActionMap.find(idCurFolder)) == m_WearActionMap.end())
|
|
{
|
|
const LLViewerInventoryCategory* pFolder = gInventory.getCategory(idCurFolder);
|
|
if ((!pFolder) || (gInventory.getRootFolderID() == pFolder->getParentUUID()))
|
|
break;
|
|
idCurFolder = pFolder->getParentUUID();
|
|
}
|
|
return (itCurFolder != m_WearActionMap.end()) ? itCurFolder->second : m_eWearAction;
|
|
}
|
|
|
|
// Checked: 2010-09-30 (RLVa-1.2.1d) | Modified: RLVa-1.2.1d
|
|
bool RlvWearableItemCollector::onCollectFolder(const LLInventoryCategory* pFolder)
|
|
{
|
|
// We treat folder links differently since they won't exist in the wearable folder list yet and their ancestry isn't relevant
|
|
bool fLinkedFolder = isLinkedFolder(pFolder->getUUID());
|
|
if ( (!fLinkedFolder) && (m_Wearable.end() == std::find(m_Wearable.begin(), m_Wearable.end(), pFolder->getParentUUID())) )
|
|
return false; // Not a linked folder or the child of a wearable folder
|
|
|
|
const std::string& strFolder = pFolder->getName();
|
|
if (strFolder.empty()) // Shouldn't happen but does... naughty Lindens
|
|
return false;
|
|
|
|
bool fAttach = RlvForceWear::isWearAction(m_eWearAction);
|
|
bool fMatchAll = (!fLinkedFolder) && (m_eWearFlags & RlvForceWear::FLAG_MATCHALL);
|
|
|
|
if ( (!fLinkedFolder) && (RlvInventory::isFoldedFolder(pFolder, false)) ) // Check for folder that should get folded under its parent
|
|
{
|
|
if ( (!fAttach) || (1 == RlvInventory::getDirectDescendentsItemCount(pFolder, LLAssetType::AT_OBJECT)) )
|
|
{ // When attaching there should only be 1 attachment in it
|
|
m_Folded.push_front(pFolder->getUUID());
|
|
m_FoldingMap.insert(std::pair<LLUUID, LLUUID>(pFolder->getUUID(), pFolder->getParentUUID()));
|
|
}
|
|
}
|
|
else if ( (RLV_FOLDER_PREFIX_HIDDEN != strFolder[0]) && // Collect from any non-hidden child folder for *all
|
|
( (fMatchAll) || (fLinkedFolder) ) && // ... and collect from linked folders
|
|
(!isLinkedFolder(pFolder->getParentUUID())) ) // ... but never from non-folded linked folder descendents
|
|
{
|
|
#ifdef RLV_EXPERIMENTAL_COMPOSITEFOLDERS
|
|
if ( (!RlvSettings::getEnableComposites()) || // ... if we're not checking composite folders
|
|
(!gRlvHandler.isCompositeFolder(pFolder)) || // ... or if it's not a composite folder
|
|
((m_fAttach) && (gRlvHandler.canWearComposite(pFolder))) || // ... or if we're attaching and can attach it OR
|
|
(!m_fAttach) && (gRlvHandler.canTakeOffComposite(pFolder)) ) // ... or if we're detaching and can detach it
|
|
#endif // RLV_EXPERIMENTAL_COMPOSITEFOLDERS
|
|
{
|
|
m_Wearable.push_front(pFolder->getUUID());
|
|
m_WearActionMap.insert(std::pair<LLUUID, RlvForceWear::EWearAction>(pFolder->getUUID(), getWearActionNormal(pFolder)));
|
|
}
|
|
return (!fLinkedFolder) && (pFolder->getParentUUID() == m_idFolder); // Convenience for @getinvworn
|
|
}
|
|
#ifdef RLV_EXPERIMENTAL_COMPOSITEFOLDERS
|
|
else if ( (RlvSettings::getEnableComposites()) &&
|
|
(RLV_FOLDER_PREFIX_HIDDEN == strFolder[0]) && // Hidden folder that's a...
|
|
(gRlvHandler.isCompositeFolder(pFolder)) && // ... composite folder which we...
|
|
( ((m_fAttach) && (gRlvHandler.canWearComposite(pFolder))) || // ... are attaching and can attach OR
|
|
(!m_fAttach) && (gRlvHandler.canTakeOffComposite(pFolder)) ) ) // ... are detaching and can detach
|
|
{
|
|
m_Wearable.push_front(pFolder->getUUID());
|
|
m_FoldingMap.insert(std::pair<LLUUID, LLUUID>(pFolder->getUUID(), idParent));
|
|
}
|
|
#endif // RLV_EXPERIMENTAL_COMPOSITEFOLDERS
|
|
|
|
return false;
|
|
}
|
|
|
|
// Checked: 2010-09-30 (RLVa-1.2.1d) | Modified: RLVa-1.2.1d
|
|
bool RlvWearableItemCollector::onCollectItem(const LLInventoryItem* pItem)
|
|
{
|
|
bool fAttach = RlvForceWear::isWearAction(m_eWearAction);
|
|
|
|
if ( (!fAttach) && (!RlvForceWear::isStrippable(pItem)) ) // Don't process "nostrip" items on detach
|
|
return false;
|
|
|
|
const LLUUID& idParent = pItem->getParentUUID(); bool fRet = false;
|
|
switch (pItem->getType())
|
|
{
|
|
case LLAssetType::AT_BODYPART:
|
|
if (!fAttach)
|
|
break; // Don't process body parts on detach
|
|
case LLAssetType::AT_CLOTHING:
|
|
fRet = ( (m_Wearable.end() != std::find(m_Wearable.begin(), m_Wearable.end(), idParent)) ||
|
|
( (fAttach) && (m_Folded.end() != std::find(m_Folded.begin(), m_Folded.end(), idParent)) &&
|
|
(RlvForceWear::isStrippable(pItem)) ) );
|
|
break;
|
|
case LLAssetType::AT_OBJECT:
|
|
fRet = ( (m_Wearable.end() != std::find(m_Wearable.begin(), m_Wearable.end(), idParent)) ||
|
|
(m_Folded.end() != std::find(m_Folded.begin(), m_Folded.end(), idParent)) ) &&
|
|
( (!fAttach) || (RlvAttachPtLookup::hasAttachPointName(pItem)) || (RlvSettings::getEnableSharedWear()) );
|
|
break;
|
|
#ifdef RLV_EXTENSION_FORCEWEAR_GESTURES
|
|
case LLAssetType::AT_GESTURE:
|
|
fRet = (m_Wearable.end() != std::find(m_Wearable.begin(), m_Wearable.end(), idParent));
|
|
break;
|
|
#endif // RLV_EXTENSION_FORCEWEAR_GESTURES
|
|
#ifdef RLV_EXTENSION_FORCEWEAR_FOLDERLINKS
|
|
case LLAssetType::AT_CATEGORY:
|
|
if (LLAssetType::AT_LINK_FOLDER == pItem->getActualType())
|
|
{
|
|
const LLUUID& idLinkedFolder = pItem->getLinkedUUID();
|
|
LLViewerInventoryCategory* pLinkedFolder = gInventory.getCategory(idLinkedFolder);
|
|
// Link can't point to an outfit folder, or start a second level of indirection, or have the base folder as an ancestor
|
|
if ( (pLinkedFolder) && (LLFolderType::FT_OUTFIT != pLinkedFolder->getPreferredType()) &&
|
|
(gInventory.isObjectDescendentOf(pItem->getUUID(), m_idFolder)) &&
|
|
(!gInventory.isObjectDescendentOf(idLinkedFolder, m_idFolder)) )
|
|
{
|
|
// Fold the contents of the linked folder under the folder the link is a child of
|
|
m_FoldingMap.insert(std::pair<LLUUID, LLUUID>(idLinkedFolder, pItem->getParentUUID()));
|
|
m_Linked.push_front(idLinkedFolder);
|
|
}
|
|
}
|
|
break;
|
|
#endif // RLV_EXTENSION_FORCEWEAR_FOLDERLINKS
|
|
default:
|
|
break;
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
// Checked: 2010-03-20 (RLVa-1.2.0a) | Modified: RLVa-0.2.0d
|
|
bool RlvWearableItemCollector::operator()(LLInventoryCategory* pFolder, LLInventoryItem* pItem)
|
|
{
|
|
// NOTE: this is used for more than was originally intended so only modify if you're sure it won't break something obscure
|
|
return (pFolder) ? onCollectFolder(pFolder) : ( (pItem) ? onCollectItem(pItem) : false );
|
|
}
|
|
|
|
// ============================================================================
|