PageRenderTime 154ms CodeModel.GetById 15ms app.highlight 124ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llinventorymodel.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2300 lines | 1797 code | 179 blank | 324 comment | 351 complexity | 9c674e438052e4ca7170b7741ab502a2 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llinventorymodel.cpp
   3 * @brief Implementation of the inventory model used to track agent inventory.
   4 *
   5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28#include "llinventorymodel.h"
  29
  30#include "llagent.h"
  31#include "llagentwearables.h"
  32#include "llappearancemgr.h"
  33#include "llinventorypanel.h"
  34#include "llinventorybridge.h"
  35#include "llinventoryfunctions.h"
  36#include "llinventoryobserver.h"
  37#include "llinventorypanel.h"
  38#include "llnotificationsutil.h"
  39#include "llwindow.h"
  40#include "llviewercontrol.h"
  41#include "llpreview.h" 
  42#include "llviewermessage.h"
  43#include "llviewerfoldertype.h"
  44#include "llviewerwindow.h"
  45#include "llappviewer.h"
  46#include "llviewerregion.h"
  47#include "llcallbacklist.h"
  48#include "llvoavatarself.h"
  49
  50//#define DIFF_INVENTORY_FILES
  51#ifdef DIFF_INVENTORY_FILES
  52#include "process.h"
  53#endif
  54
  55// Increment this if the inventory contents change in a non-backwards-compatible way.
  56// For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect.
  57const S32 LLInventoryModel::sCurrentInvCacheVersion = 2;
  58BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE;
  59
  60///----------------------------------------------------------------------------
  61/// Local function declarations, constants, enums, and typedefs
  62///----------------------------------------------------------------------------
  63
  64//BOOL decompress_file(const char* src_filename, const char* dst_filename);
  65const char CACHE_FORMAT_STRING[] = "%s.inv"; 
  66
  67struct InventoryIDPtrLess
  68{
  69	bool operator()(const LLViewerInventoryCategory* i1, const LLViewerInventoryCategory* i2) const
  70	{
  71		return (i1->getUUID() < i2->getUUID());
  72	}
  73};
  74
  75class LLCanCache : public LLInventoryCollectFunctor 
  76{
  77public:
  78	LLCanCache(LLInventoryModel* model) : mModel(model) {}
  79	virtual ~LLCanCache() {}
  80	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
  81protected:
  82	LLInventoryModel* mModel;
  83	std::set<LLUUID> mCachedCatIDs;
  84};
  85
  86bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
  87{
  88	bool rv = false;
  89	if(item)
  90	{
  91		if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end())
  92		{
  93			rv = true;
  94		}
  95	}
  96	else if(cat)
  97	{
  98		// HACK: downcast
  99		LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat;
 100		if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
 101		{
 102			S32 descendents_server = c->getDescendentCount();
 103			LLInventoryModel::cat_array_t* cats;
 104			LLInventoryModel::item_array_t* items;
 105			mModel->getDirectDescendentsOf(
 106				c->getUUID(),
 107				cats,
 108				items);
 109			S32 descendents_actual = 0;
 110			if(cats && items)
 111			{
 112				descendents_actual = cats->count() + items->count();
 113			}
 114			if(descendents_server == descendents_actual)
 115			{
 116				mCachedCatIDs.insert(c->getUUID());
 117				rv = true;
 118			}
 119		}
 120	}
 121	return rv;
 122}
 123
 124///----------------------------------------------------------------------------
 125/// Class LLInventoryModel
 126///----------------------------------------------------------------------------
 127
 128// global for the agent inventory.
 129LLInventoryModel gInventory;
 130
 131// Default constructor
 132LLInventoryModel::LLInventoryModel()
 133:	mModifyMask(LLInventoryObserver::ALL),
 134	mChangedItemIDs(),
 135	mCategoryMap(),
 136	mItemMap(),
 137	mCategoryLock(),
 138	mItemLock(),
 139	mLastItem(NULL),
 140	mParentChildCategoryTree(),
 141	mParentChildItemTree(),
 142	mObservers(),
 143	mRootFolderID(),
 144	mLibraryRootFolderID(),
 145	mLibraryOwnerID(),
 146	mIsNotifyObservers(FALSE),
 147	mIsAgentInvUsable(false)
 148{
 149}
 150
 151// Destroys the object
 152LLInventoryModel::~LLInventoryModel()
 153{
 154	cleanupInventory();
 155}
 156
 157void LLInventoryModel::cleanupInventory()
 158{
 159	empty();
 160	// Deleting one observer might erase others from the list, so always pop off the front
 161	while (!mObservers.empty())
 162	{
 163		observer_list_t::iterator iter = mObservers.begin();
 164		LLInventoryObserver* observer = *iter;
 165		mObservers.erase(iter);
 166		delete observer;
 167	}
 168	mObservers.clear();
 169}
 170
 171// This is a convenience function to check if one object has a parent
 172// chain up to the category specified by UUID.
 173BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
 174											const LLUUID& cat_id) const
 175{
 176	if (obj_id == cat_id) return TRUE;
 177
 178	const LLInventoryObject* obj = getObject(obj_id);
 179	while(obj)
 180	{
 181		const LLUUID& parent_id = obj->getParentUUID();
 182		if( parent_id.isNull() )
 183		{
 184			return FALSE;
 185		}
 186		if(parent_id == cat_id)
 187		{
 188			return TRUE;
 189		}
 190		// Since we're scanning up the parents, we only need to check
 191		// in the category list.
 192		obj = getCategory(parent_id);
 193	}
 194	return FALSE;
 195}
 196
 197const LLViewerInventoryCategory *LLInventoryModel::getFirstNondefaultParent(const LLUUID& obj_id) const
 198{
 199	const LLInventoryObject* obj = getObject(obj_id);
 200
 201	// Search up the parent chain until we get to root or an acceptable folder.
 202	// This assumes there are no cycles in the tree else we'll get a hang.
 203	LLUUID parent_id = obj->getParentUUID();
 204	while (!parent_id.isNull())
 205	{
 206		const LLViewerInventoryCategory *cat = getCategory(parent_id);
 207		if (!cat) break;
 208		const LLFolderType::EType folder_type = cat->getPreferredType();
 209		if (folder_type != LLFolderType::FT_NONE &&
 210			folder_type != LLFolderType::FT_ROOT_INVENTORY &&
 211			!LLFolderType::lookupIsEnsembleType(folder_type))
 212		{
 213			return cat;
 214		}
 215		parent_id = cat->getParentUUID();
 216	}
 217	return NULL;
 218}
 219
 220//
 221// Search up the parent chain until we get to the specified parent, then return the first child category under it
 222//
 223const LLViewerInventoryCategory* LLInventoryModel::getFirstDescendantOf(const LLUUID& master_parent_id, const LLUUID& obj_id) const
 224{
 225	if (master_parent_id == obj_id)
 226	{
 227		return NULL;
 228	}
 229
 230	const LLViewerInventoryCategory* current_cat = getCategory(obj_id);
 231
 232	if (current_cat == NULL)
 233	{
 234		current_cat = getCategory(getObject(obj_id)->getParentUUID());
 235	}
 236	
 237	while (current_cat != NULL)
 238	{
 239		const LLUUID& current_parent_id = current_cat->getParentUUID();
 240		
 241		if (current_parent_id == master_parent_id)
 242		{
 243			return current_cat;
 244		}
 245		
 246		current_cat = getCategory(current_parent_id);
 247	}
 248
 249	return NULL;
 250}
 251
 252// Get the object by id. Returns NULL if not found.
 253LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const
 254{
 255	LLViewerInventoryCategory* cat = getCategory(id);
 256	if (cat)
 257	{
 258		return cat;
 259	}
 260	LLViewerInventoryItem* item = getItem(id);
 261	if (item)
 262	{
 263		return item;
 264	}
 265	return NULL;
 266}
 267
 268// Get the item by id. Returns NULL if not found.
 269LLViewerInventoryItem* LLInventoryModel::getItem(const LLUUID& id) const
 270{
 271	LLViewerInventoryItem* item = NULL;
 272	if(mLastItem.notNull() && mLastItem->getUUID() == id)
 273	{
 274		item = mLastItem;
 275	}
 276	else
 277	{
 278		item_map_t::const_iterator iter = mItemMap.find(id);
 279		if (iter != mItemMap.end())
 280		{
 281			item = iter->second;
 282			mLastItem = item;
 283		}
 284	}
 285	return item;
 286}
 287
 288// Get the category by id. Returns NULL if not found
 289LLViewerInventoryCategory* LLInventoryModel::getCategory(const LLUUID& id) const
 290{
 291	LLViewerInventoryCategory* category = NULL;
 292	cat_map_t::const_iterator iter = mCategoryMap.find(id);
 293	if (iter != mCategoryMap.end())
 294	{
 295		category = iter->second;
 296	}
 297	return category;
 298}
 299
 300S32 LLInventoryModel::getItemCount() const
 301{
 302	return mItemMap.size();
 303}
 304
 305S32 LLInventoryModel::getCategoryCount() const
 306{
 307	return mCategoryMap.size();
 308}
 309
 310// Return the direct descendents of the id provided. The array
 311// provided points straight into the guts of this object, and
 312// should only be used for read operations, since modifications
 313// may invalidate the internal state of the inventory. Set passed
 314// in values to NULL if the call fails.
 315void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
 316											  cat_array_t*& categories,
 317											  item_array_t*& items) const
 318{
 319	categories = get_ptr_in_map(mParentChildCategoryTree, cat_id);
 320	items = get_ptr_in_map(mParentChildItemTree, cat_id);
 321}
 322
 323LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
 324{
 325	LLInventoryModel::cat_array_t* cat_array;
 326	LLInventoryModel::item_array_t* item_array;
 327	getDirectDescendentsOf(cat_id,cat_array,item_array);
 328	LLMD5 item_name_hash;
 329	if (!item_array)
 330	{
 331		item_name_hash.finalize();
 332		return item_name_hash;
 333	}
 334	for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin();
 335		 iter != item_array->end();
 336		 iter++)
 337	{
 338		const LLViewerInventoryItem *item = (*iter);
 339		if (!item)
 340			continue;
 341		item_name_hash.update(item->getName());
 342	}
 343	item_name_hash.finalize();
 344	return item_name_hash;
 345}
 346
 347// SJB: Added version to lock the arrays to catch potential logic bugs
 348void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id,
 349												  cat_array_t*& categories,
 350												  item_array_t*& items)
 351{
 352	getDirectDescendentsOf(cat_id, categories, items);
 353	if (categories)
 354	{
 355		mCategoryLock[cat_id] = true;
 356	}
 357	if (items)
 358	{
 359		mItemLock[cat_id] = true;
 360	}
 361}
 362
 363void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id)
 364{
 365	mCategoryLock[cat_id] = false;
 366	mItemLock[cat_id] = false;
 367}
 368
 369// findCategoryUUIDForType() returns the uuid of the category that
 370// specifies 'type' as what it defaults to containing. The category is
 371// not necessarily only for that type. *NOTE: This will create a new
 372// inventory category on the fly if one does not exist.
 373const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, 
 374													   bool create_folder, 
 375													   bool find_in_library)
 376{
 377	LLUUID rv = LLUUID::null;
 378	
 379	const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID();
 380	if(LLFolderType::FT_ROOT_INVENTORY == preferred_type)
 381	{
 382		rv = root_id;
 383	}
 384	else if (root_id.notNull())
 385	{
 386		cat_array_t* cats = NULL;
 387		cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
 388		if(cats)
 389		{
 390			S32 count = cats->count();
 391			for(S32 i = 0; i < count; ++i)
 392			{
 393				if(cats->get(i)->getPreferredType() == preferred_type)
 394				{
 395					rv = cats->get(i)->getUUID();
 396					break;
 397				}
 398			}
 399		}
 400	}
 401	
 402	if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library))
 403	{
 404		if(root_id.notNull())
 405		{
 406			return createNewCategory(root_id, preferred_type, LLStringUtil::null);
 407		}
 408	}
 409	return rv;
 410}
 411
 412class LLCreateInventoryCategoryResponder : public LLHTTPClient::Responder
 413{
 414public:
 415	LLCreateInventoryCategoryResponder(LLInventoryModel* model, 
 416									   void (*callback)(const LLSD&, void*),
 417									   void* user_data) :
 418										mModel(model),
 419										mCallback(callback), 
 420										mData(user_data) 
 421	{
 422	}
 423	
 424	virtual void error(U32 status, const std::string& reason)
 425	{
 426		LL_WARNS("InvAPI") << "CreateInventoryCategory failed.   status = " << status << ", reasion = \"" << reason << "\"" << LL_ENDL;
 427	}
 428	
 429	virtual void result(const LLSD& content)
 430	{
 431		//Server has created folder.
 432		
 433		LLUUID category_id = content["folder_id"].asUUID();
 434		
 435		
 436		// Add the category to the internal representation
 437		LLPointer<LLViewerInventoryCategory> cat =
 438		new LLViewerInventoryCategory( category_id, 
 439									  content["parent_id"].asUUID(),
 440									  (LLFolderType::EType)content["type"].asInteger(),
 441									  content["name"].asString(), 
 442									  gAgent.getID() );
 443		cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
 444		cat->setDescendentCount(0);
 445		LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1);
 446		mModel->accountForUpdate(update);
 447		mModel->updateCategory(cat);
 448		
 449		if (mCallback && mData)
 450		{
 451			mCallback(content, mData);
 452		}
 453		
 454	}
 455	
 456private:
 457	void (*mCallback)(const LLSD&, void*);
 458	void* mData;
 459	LLInventoryModel* mModel;
 460};
 461
 462// Convenience function to create a new category. You could call
 463// updateCategory() with a newly generated UUID category, but this
 464// version will take care of details like what the name should be
 465// based on preferred type. Returns the UUID of the new category.
 466LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
 467										   LLFolderType::EType preferred_type,
 468										   const std::string& pname,
 469										   void (*callback)(const LLSD&, void*),	//Default to NULL
 470										   void* user_data)							//Default to NULL
 471{
 472	
 473	LLUUID id;
 474	if(!isInventoryUsable())
 475	{
 476		llwarns << "Inventory is broken." << llendl;
 477		return id;
 478	}
 479
 480	if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup())
 481	{
 482		lldebugs << "Attempt to create undefined category." << llendl;
 483		return id;
 484	}
 485
 486	id.generate();
 487	std::string name = pname;
 488	if(!pname.empty())
 489	{
 490		name.assign(pname);
 491	}
 492	else
 493	{
 494		name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type));
 495	}
 496	
 497	if ( callback && user_data )  //callback required for acked message.
 498	{
 499		LLViewerRegion* viewer_region = gAgent.getRegion();
 500		std::string url;
 501		if ( viewer_region )
 502			url = viewer_region->getCapability("CreateInventoryCategory");
 503		
 504		if (!url.empty())
 505		{
 506			//Let's use the new capability.
 507			
 508			LLSD request, body;
 509			body["folder_id"] = id;
 510			body["parent_id"] = parent_id;
 511			body["type"] = (LLSD::Integer) preferred_type;
 512			body["name"] = name;
 513			
 514			request["message"] = "CreateInventoryCategory";
 515			request["payload"] = body;
 516			
 517	//		viewer_region->getCapAPI().post(request);
 518			LLHTTPClient::post(
 519							   url,
 520							   body,
 521							   new LLCreateInventoryCategoryResponder(this, callback, user_data) );
 522			return LLUUID::null;
 523		}
 524	}
 525
 526	// Add the category to the internal representation
 527	LLPointer<LLViewerInventoryCategory> cat =
 528		new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
 529	cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
 530	cat->setDescendentCount(0);
 531	LLCategoryUpdate update(cat->getParentUUID(), 1);
 532	accountForUpdate(update);
 533	updateCategory(cat);
 534
 535	// Create the category on the server. We do this to prevent people
 536	// from munging their protected folders.
 537	LLMessageSystem* msg = gMessageSystem;
 538	msg->newMessage("CreateInventoryFolder");
 539	msg->nextBlock("AgentData");
 540	msg->addUUID("AgentID", gAgent.getID());
 541	msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
 542	msg->nextBlock("FolderData");
 543	cat->packMessage(msg);
 544	gAgent.sendReliableMessage();
 545
 546	// return the folder id of the newly created folder
 547	return id;
 548}
 549
 550// Starting with the object specified, add it's descendents to the
 551// array provided, but do not add the inventory object specified by
 552// id. There is no guaranteed order. Neither array will be erased
 553// before adding objects to it. Do not store a copy of the pointers
 554// collected - use them, and collect them again later if you need to
 555// reference the same objects.
 556
 557class LLAlwaysCollect : public LLInventoryCollectFunctor
 558{
 559public:
 560	virtual ~LLAlwaysCollect() {}
 561	virtual bool operator()(LLInventoryCategory* cat,
 562							LLInventoryItem* item)
 563	{
 564		return TRUE;
 565	}
 566};
 567
 568void LLInventoryModel::collectDescendents(const LLUUID& id,
 569										  cat_array_t& cats,
 570										  item_array_t& items,
 571										  BOOL include_trash)
 572{
 573	LLAlwaysCollect always;
 574	collectDescendentsIf(id, cats, items, include_trash, always);
 575}
 576
 577void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
 578											cat_array_t& cats,
 579											item_array_t& items,
 580											BOOL include_trash,
 581											LLInventoryCollectFunctor& add,
 582											BOOL follow_folder_links)
 583{
 584	// Start with categories
 585	if(!include_trash)
 586	{
 587		const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
 588		if(trash_id.notNull() && (trash_id == id))
 589			return;
 590	}
 591	cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
 592	if(cat_array)
 593	{
 594		S32 count = cat_array->count();
 595		for(S32 i = 0; i < count; ++i)
 596		{
 597			LLViewerInventoryCategory* cat = cat_array->get(i);
 598			if(add(cat,NULL))
 599			{
 600				cats.put(cat);
 601			}
 602			collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add);
 603		}
 604	}
 605
 606	LLViewerInventoryItem* item = NULL;
 607	item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
 608
 609	// Follow folder links recursively.  Currently never goes more
 610	// than one level deep (for current outfit support)
 611	// Note: if making it fully recursive, need more checking against infinite loops.
 612	if (follow_folder_links && item_array)
 613	{
 614		S32 count = item_array->count();
 615		for(S32 i = 0; i < count; ++i)
 616		{
 617			item = item_array->get(i);
 618			if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER)
 619			{
 620				LLViewerInventoryCategory *linked_cat = item->getLinkedCategory();
 621				if (linked_cat && linked_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
 622					// BAP - was 
 623					// LLAssetType::lookupIsEnsembleCategoryType(linked_cat->getPreferredType()))
 624					// Change back once ensemble typing is in place.
 625				{
 626					if(add(linked_cat,NULL))
 627					{
 628						// BAP should this be added here?  May not
 629						// matter if it's only being used in current
 630						// outfit traversal.
 631						cats.put(LLPointer<LLViewerInventoryCategory>(linked_cat));
 632					}
 633					collectDescendentsIf(linked_cat->getUUID(), cats, items, include_trash, add, FALSE);
 634				}
 635			}
 636		}
 637	}
 638	
 639	// Move onto items
 640	if(item_array)
 641	{
 642		S32 count = item_array->count();
 643		for(S32 i = 0; i < count; ++i)
 644		{
 645			item = item_array->get(i);
 646			if(add(NULL, item))
 647			{
 648				items.put(item);
 649			}
 650		}
 651	}
 652}
 653
 654void LLInventoryModel::addChangedMaskForLinks(const LLUUID& object_id, U32 mask)
 655{
 656	const LLInventoryObject *obj = getObject(object_id);
 657	if (!obj || obj->getIsLinkType())
 658		return;
 659
 660	LLInventoryModel::cat_array_t cat_array;
 661	LLInventoryModel::item_array_t item_array;
 662	LLLinkedItemIDMatches is_linked_item_match(object_id);
 663	collectDescendentsIf(gInventory.getRootFolderID(),
 664						 cat_array,
 665						 item_array,
 666						 LLInventoryModel::INCLUDE_TRASH,
 667						 is_linked_item_match);
 668	if (cat_array.empty() && item_array.empty())
 669	{
 670		return;
 671	}
 672	for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin();
 673		 cat_iter != cat_array.end();
 674		 cat_iter++)
 675	{
 676		LLViewerInventoryCategory *linked_cat = (*cat_iter);
 677		addChangedMask(mask, linked_cat->getUUID());
 678	};
 679
 680	for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
 681		 iter != item_array.end();
 682		 iter++)
 683	{
 684		LLViewerInventoryItem *linked_item = (*iter);
 685		addChangedMask(mask, linked_item->getUUID());
 686	};
 687}
 688
 689const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const
 690{
 691	const LLInventoryItem *item = gInventory.getItem(object_id);
 692	if (!item)
 693	{
 694		return object_id;
 695	}
 696
 697	// Find the base item in case this a link (if it's not a link,
 698	// this will just be inv_item_id)
 699	return item->getLinkedUUID();
 700}
 701
 702LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const
 703{
 704	return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL;
 705}
 706
 707LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id,
 708																	const LLUUID& start_folder_id)
 709{
 710	item_array_t items;
 711	LLInventoryModel::cat_array_t cat_array;
 712	LLLinkedItemIDMatches is_linked_item_match(id);
 713	collectDescendentsIf((start_folder_id == LLUUID::null ? gInventory.getRootFolderID() : start_folder_id),
 714						 cat_array,
 715						 items,
 716						 LLInventoryModel::INCLUDE_TRASH,
 717						 is_linked_item_match);
 718	return items;
 719}
 720
 721bool LLInventoryModel::isInventoryUsable() const
 722{
 723	bool result = false;
 724	if(gInventory.getRootFolderID().notNull() && mIsAgentInvUsable)
 725	{
 726		result = true;
 727	}
 728	return result;	
 729}
 730
 731// Calling this method with an inventory item will either change an
 732// existing item with a matching item_id, or will add the item to the
 733// current inventory.
 734U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
 735{
 736	U32 mask = LLInventoryObserver::NONE;
 737	if(item->getUUID().isNull())
 738	{
 739		return mask;
 740	}
 741
 742	if(!isInventoryUsable())
 743	{
 744		llwarns << "Inventory is broken." << llendl;
 745		return mask;
 746	}
 747
 748	// We're hiding mesh types
 749#if 0
 750	if (item->getType() == LLAssetType::AT_MESH)
 751	{
 752		return mask;
 753	}
 754#endif
 755
 756	LLViewerInventoryItem* old_item = getItem(item->getUUID());
 757	LLPointer<LLViewerInventoryItem> new_item;
 758	if(old_item)
 759	{
 760		// We already have an old item, modify its values
 761		new_item = old_item;
 762		LLUUID old_parent_id = old_item->getParentUUID();
 763		LLUUID new_parent_id = item->getParentUUID();
 764			
 765		if(old_parent_id != new_parent_id)
 766		{
 767			// need to update the parent-child tree
 768			item_array_t* item_array;
 769			item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id);
 770			if(item_array)
 771			{
 772				item_array->removeObj(old_item);
 773			}
 774			item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id);
 775			if(item_array)
 776			{
 777				item_array->put(old_item);
 778			}
 779			mask |= LLInventoryObserver::STRUCTURE;
 780		}
 781		if(old_item->getName() != item->getName())
 782		{
 783			mask |= LLInventoryObserver::LABEL;
 784		}
 785		old_item->copyViewerItem(item);
 786		mask |= LLInventoryObserver::INTERNAL;
 787	}
 788	else
 789	{
 790		// Simply add this item
 791		new_item = new LLViewerInventoryItem(item);
 792		addItem(new_item);
 793
 794		if(item->getParentUUID().isNull())
 795		{
 796			const LLUUID category_id = findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(new_item->getType()));
 797			new_item->setParent(category_id);
 798			item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id);
 799			if( item_array )
 800			{
 801				// *FIX: bit of a hack to call update server from here...
 802				new_item->updateServer(TRUE);
 803				item_array->put(new_item);
 804			}
 805			else
 806			{
 807				llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl;
 808			}
 809		}
 810		else
 811		{
 812			// *NOTE: The general scheme is that if every byte of the
 813			// uuid is 0, except for the last one or two,the use the
 814			// last two bytes of the parent id, and match that up
 815			// against the type. For now, we're only worried about
 816			// lost & found.
 817			LLUUID parent_id = item->getParentUUID();
 818			if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID)
 819			{
 820				parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
 821				new_item->setParent(parent_id);
 822			}
 823			item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
 824			if(item_array)
 825			{
 826				item_array->put(new_item);
 827			}
 828			else
 829			{
 830				// Whoops! No such parent, make one.
 831				llinfos << "Lost item: " << new_item->getUUID() << " - "
 832						<< new_item->getName() << llendl;
 833				parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
 834				new_item->setParent(parent_id);
 835				item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
 836				if(item_array)
 837				{
 838					// *FIX: bit of a hack to call update server from
 839					// here...
 840					new_item->updateServer(TRUE);
 841					item_array->put(new_item);
 842				}
 843				else
 844				{
 845					llwarns << "Lost and found Not there!!" << llendl;
 846				}
 847			}
 848		}
 849		mask |= LLInventoryObserver::ADD;
 850	}
 851	if(new_item->getType() == LLAssetType::AT_CALLINGCARD)
 852	{
 853		mask |= LLInventoryObserver::CALLING_CARD;
 854		// Handle user created calling cards.
 855		// Target ID is stored in the description field of the card.
 856		LLUUID id;
 857		std::string desc = new_item->getDescription();
 858		BOOL isId = desc.empty() ? FALSE : id.set(desc, FALSE);
 859		if (isId)
 860		{
 861			// Valid UUID; set the item UUID and rename it
 862			new_item->setCreator(id);
 863			std::string avatar_name;
 864
 865			if (gCacheName->getFullName(id, avatar_name))
 866			{
 867				new_item->rename(avatar_name);
 868				mask |= LLInventoryObserver::LABEL;
 869			}
 870			else
 871			{
 872				// Fetch the current name
 873				gCacheName->get(id, FALSE,
 874					boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(),
 875					_1, _2, _3));
 876			}
 877
 878		}
 879	}
 880	else if (new_item->getType() == LLAssetType::AT_GESTURE)
 881	{
 882		mask |= LLInventoryObserver::GESTURE;
 883	}
 884	addChangedMask(mask, new_item->getUUID());
 885	return mask;
 886}
 887
 888LLInventoryModel::cat_array_t* LLInventoryModel::getUnlockedCatArray(const LLUUID& id)
 889{
 890	cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
 891	if (cat_array)
 892	{
 893		llassert_always(mCategoryLock[id] == false);
 894	}
 895	return cat_array;
 896}
 897
 898LLInventoryModel::item_array_t* LLInventoryModel::getUnlockedItemArray(const LLUUID& id)
 899{
 900	item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
 901	if (item_array)
 902	{
 903		llassert_always(mItemLock[id] == false);
 904	}
 905	return item_array;
 906}
 907
 908// Calling this method with an inventory category will either change
 909// an existing item with the matching id, or it will add the category.
 910void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
 911{
 912	if(cat->getUUID().isNull())
 913	{
 914		return;
 915	}
 916
 917	if(!isInventoryUsable())
 918	{
 919		llwarns << "Inventory is broken." << llendl;
 920		return;
 921	}
 922
 923	LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
 924	if(old_cat)
 925	{
 926		// We already have an old category, modify it's values
 927		U32 mask = LLInventoryObserver::NONE;
 928		LLUUID old_parent_id = old_cat->getParentUUID();
 929		LLUUID new_parent_id = cat->getParentUUID();
 930		if(old_parent_id != new_parent_id)
 931		{
 932			// need to update the parent-child tree
 933			cat_array_t* cat_array;
 934			cat_array = getUnlockedCatArray(old_parent_id);
 935			if(cat_array)
 936			{
 937				cat_array->removeObj(old_cat);
 938			}
 939			cat_array = getUnlockedCatArray(new_parent_id);
 940			if(cat_array)
 941			{
 942				cat_array->put(old_cat);
 943			}
 944			mask |= LLInventoryObserver::STRUCTURE;
 945		}
 946		if(old_cat->getName() != cat->getName())
 947		{
 948			mask |= LLInventoryObserver::LABEL;
 949		}
 950		old_cat->copyViewerCategory(cat);
 951		addChangedMask(mask, cat->getUUID());
 952	}
 953	else
 954	{
 955		// add this category
 956		LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat->getParentUUID());
 957		new_cat->copyViewerCategory(cat);
 958		addCategory(new_cat);
 959
 960		// make sure this category is correctly referenced by it's parent.
 961		cat_array_t* cat_array;
 962		cat_array = getUnlockedCatArray(cat->getParentUUID());
 963		if(cat_array)
 964		{
 965			cat_array->put(new_cat);
 966		}
 967
 968		// make space in the tree for this category's children.
 969		llassert_always(mCategoryLock[new_cat->getUUID()] == false);
 970		llassert_always(mItemLock[new_cat->getUUID()] == false);
 971		cat_array_t* catsp = new cat_array_t;
 972		item_array_t* itemsp = new item_array_t;
 973		mParentChildCategoryTree[new_cat->getUUID()] = catsp;
 974		mParentChildItemTree[new_cat->getUUID()] = itemsp;
 975		addChangedMask(LLInventoryObserver::ADD, cat->getUUID());
 976	}
 977}
 978
 979void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
 980{
 981	lldebugs << "LLInventoryModel::moveObject()" << llendl;
 982	if(!isInventoryUsable())
 983	{
 984		llwarns << "Inventory is broken." << llendl;
 985		return;
 986	}
 987
 988	if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))
 989	{
 990		llwarns << "Could not move inventory object " << object_id << " to "
 991				<< cat_id << llendl;
 992		return;
 993	}
 994	LLViewerInventoryCategory* cat = getCategory(object_id);
 995	if(cat && (cat->getParentUUID() != cat_id))
 996	{
 997		cat_array_t* cat_array;
 998		cat_array = getUnlockedCatArray(cat->getParentUUID());
 999		if(cat_array) cat_array->removeObj(cat);
1000		cat_array = getUnlockedCatArray(cat_id);
1001		cat->setParent(cat_id);
1002		if(cat_array) cat_array->put(cat);
1003		addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
1004		return;
1005	}
1006	LLViewerInventoryItem* item = getItem(object_id);
1007	if(item && (item->getParentUUID() != cat_id))
1008	{
1009		item_array_t* item_array;
1010		item_array = getUnlockedItemArray(item->getParentUUID());
1011		if(item_array) item_array->removeObj(item);
1012		item_array = getUnlockedItemArray(cat_id);
1013		item->setParent(cat_id);
1014		if(item_array) item_array->put(item);
1015		addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
1016		return;
1017	}
1018}
1019
1020// Delete a particular inventory object by ID.
1021void LLInventoryModel::deleteObject(const LLUUID& id)
1022{
1023	lldebugs << "LLInventoryModel::deleteObject()" << llendl;
1024	LLPointer<LLInventoryObject> obj = getObject(id);
1025	if (!obj) 
1026	{
1027		llwarns << "Deleting non-existent object [ id: " << id << " ] " << llendl;
1028		return;
1029	}
1030	
1031	lldebugs << "Deleting inventory object " << id << llendl;
1032	mLastItem = NULL;
1033	LLUUID parent_id = obj->getParentUUID();
1034	mCategoryMap.erase(id);
1035	mItemMap.erase(id);
1036	//mInventory.erase(id);
1037	item_array_t* item_list = getUnlockedItemArray(parent_id);
1038	if(item_list)
1039	{
1040		LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
1041		item_list->removeObj(item);
1042	}
1043	cat_array_t* cat_list = getUnlockedCatArray(parent_id);
1044	if(cat_list)
1045	{
1046		LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
1047		cat_list->removeObj(cat);
1048	}
1049	item_list = getUnlockedItemArray(id);
1050	if(item_list)
1051	{
1052		delete item_list;
1053		mParentChildItemTree.erase(id);
1054	}
1055	cat_list = getUnlockedCatArray(id);
1056	if(cat_list)
1057	{
1058		delete cat_list;
1059		mParentChildCategoryTree.erase(id);
1060	}
1061	addChangedMask(LLInventoryObserver::REMOVE, id);
1062	obj = NULL; // delete obj
1063	updateLinkedObjectsFromPurge(id);
1064	gInventory.notifyObservers();
1065}
1066
1067// Delete a particular inventory item by ID, and remove it from the server.
1068void LLInventoryModel::purgeObject(const LLUUID &id)
1069{
1070	lldebugs << "LLInventoryModel::purgeObject() [ id: " << id << " ] " << llendl;
1071	LLPointer<LLInventoryObject> obj = getObject(id);
1072	if(obj)
1073	{
1074		obj->removeFromServer();
1075		LLPreview::hide(id);
1076		deleteObject(id);
1077	}
1078}
1079
1080void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)
1081{
1082	LLInventoryModel::item_array_t item_array = collectLinkedItems(baseobj_id);
1083
1084	// REBUILD is expensive, so clear the current change list first else
1085	// everything else on the changelist will also get rebuilt.
1086	gInventory.notifyObservers();
1087	for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
1088		 iter != item_array.end();
1089		 iter++)
1090	{
1091		const LLViewerInventoryItem *linked_item = (*iter);
1092		const LLUUID &item_id = linked_item->getUUID();
1093		if (item_id == baseobj_id) continue;
1094		addChangedMask(LLInventoryObserver::REBUILD, item_id);
1095	}
1096	gInventory.notifyObservers();
1097}
1098
1099// This is a method which collects the descendents of the id
1100// provided. If the category is not found, no action is
1101// taken. This method goes through the long winded process of
1102// cancelling any calling cards, removing server representation of
1103// folders, items, etc in a fairly efficient manner.
1104void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
1105{
1106	EHasChildren children = categoryHasChildren(id);
1107	if(children == CHILDREN_NO)
1108	{
1109		llinfos << "Not purging descendents of " << id << llendl;
1110		return;
1111	}
1112	LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
1113	if(cat.notNull())
1114	{
1115		// do the cache accounting
1116		llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
1117				<< llendl;
1118		S32 descendents = cat->getDescendentCount();
1119		if(descendents > 0)
1120		{
1121			LLCategoryUpdate up(id, -descendents);
1122			accountForUpdate(up);
1123		}
1124
1125		// we know that descendent count is 0, aide since the
1126		// accounting may actually not do an update, we should force
1127		// it here.
1128		cat->setDescendentCount(0);
1129
1130		// send it upstream
1131		LLMessageSystem* msg = gMessageSystem;
1132		msg->newMessage("PurgeInventoryDescendents");
1133		msg->nextBlock("AgentData");
1134		msg->addUUID("AgentID", gAgent.getID());
1135		msg->addUUID("SessionID", gAgent.getSessionID());
1136		msg->nextBlock("InventoryData");
1137		msg->addUUID("FolderID", id);
1138		gAgent.sendReliableMessage();
1139
1140		// unceremoniously remove anything we have locally stored.
1141		cat_array_t categories;
1142		item_array_t items;
1143		collectDescendents(id,
1144						   categories,
1145						   items,
1146						   INCLUDE_TRASH);
1147		S32 count = items.count();
1148		S32 i;
1149		for(i = 0; i < count; ++i)
1150		{
1151			deleteObject(items.get(i)->getUUID());
1152		}
1153		count = categories.count();
1154		for(i = 0; i < count; ++i)
1155		{
1156			deleteObject(categories.get(i)->getUUID());
1157		}
1158	}
1159}
1160
1161// Add/remove an observer. If the observer is destroyed, be sure to
1162// remove it.
1163void LLInventoryModel::addObserver(LLInventoryObserver* observer)
1164{
1165	mObservers.insert(observer);
1166}
1167	
1168void LLInventoryModel::removeObserver(LLInventoryObserver* observer)
1169{
1170	mObservers.erase(observer);
1171}
1172
1173BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const
1174{
1175	return mObservers.find(observer) != mObservers.end();
1176}
1177
1178void LLInventoryModel::idleNotifyObservers()
1179{
1180	if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0))
1181	{
1182		return;
1183	}
1184	notifyObservers();
1185}
1186
1187// Call this method when it's time to update everyone on a new state.
1188void LLInventoryModel::notifyObservers()
1189{
1190	if (mIsNotifyObservers)
1191	{
1192		// Within notifyObservers, something called notifyObservers
1193		// again.  This type of recursion is unsafe because it causes items to be 
1194		// processed twice, and this can easily lead to infinite loops.
1195		llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl;
1196		return;
1197	}
1198
1199	mIsNotifyObservers = TRUE;
1200	for (observer_list_t::iterator iter = mObservers.begin();
1201		 iter != mObservers.end(); )
1202	{
1203		LLInventoryObserver* observer = *iter;
1204		observer->changed(mModifyMask);
1205
1206		// safe way to increment since changed may delete entries! (@!##%@!@&*!)
1207		iter = mObservers.upper_bound(observer); 
1208	}
1209
1210	mModifyMask = LLInventoryObserver::NONE;
1211	mChangedItemIDs.clear();
1212	mIsNotifyObservers = FALSE;
1213}
1214
1215// store flag for change
1216// and id of object change applies to
1217void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) 
1218{ 
1219	if (mIsNotifyObservers)
1220	{
1221		// Something marked an item for change within a call to notifyObservers
1222		// (which is in the process of processing the list of items marked for change).
1223		// This means the change may fail to be processed.
1224		llwarns << "Adding changed mask within notify observers!  Change will likely be lost." << llendl;
1225	}
1226	
1227	mModifyMask |= mask; 
1228	if (referent.notNull())
1229	{
1230		mChangedItemIDs.insert(referent);
1231	}
1232	
1233	// Update all linked items.  Starting with just LABEL because I'm
1234	// not sure what else might need to be accounted for this.
1235	if (mModifyMask & LLInventoryObserver::LABEL)
1236	{
1237		addChangedMaskForLinks(referent, LLInventoryObserver::LABEL);
1238	}
1239}
1240
1241// If we get back a normal response, handle it here
1242void  LLInventoryModel::fetchInventoryResponder::result(const LLSD& content)
1243{	
1244	start_new_inventory_observer();
1245
1246	/*LLUUID agent_id;
1247	agent_id = content["agent_id"].asUUID();
1248	if(agent_id != gAgent.getID())
1249	{
1250		llwarns << "Got a inventory update for the wrong agent: " << agent_id
1251				<< llendl;
1252		return;
1253	}*/
1254	item_array_t items;
1255	update_map_t update;
1256	S32 count = content["items"].size();
1257	bool all_one_folder = true;
1258	LLUUID folder_id;
1259	// Does this loop ever execute more than once?
1260	for(S32 i = 0; i < count; ++i)
1261	{
1262		LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
1263		titem->unpackMessage(content["items"][i]);
1264		
1265		lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
1266				 << titem->getUUID() << llendl;
1267		items.push_back(titem);
1268		// examine update for changes.
1269		LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
1270		if(itemp)
1271		{
1272			if(titem->getParentUUID() == itemp->getParentUUID())
1273			{
1274				update[titem->getParentUUID()];
1275			}
1276			else
1277			{
1278				++update[titem->getParentUUID()];
1279				--update[itemp->getParentUUID()];
1280			}
1281		}
1282		else
1283		{
1284			++update[titem->getParentUUID()];
1285		}
1286		if (folder_id.isNull())
1287		{
1288			folder_id = titem->getParentUUID();
1289		}
1290		else
1291		{
1292			all_one_folder = false;
1293		}
1294	}
1295
1296	U32 changes = 0x0;
1297	//as above, this loop never seems to loop more than once per call
1298	for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
1299	{
1300		changes |= gInventory.updateItem(*it);
1301	}
1302	gInventory.notifyObservers();
1303	gViewerWindow->getWindow()->decBusyCount();
1304}
1305
1306//If we get back an error (not found, etc...), handle it here
1307void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason)
1308{
1309	llinfos << "fetchInventory::error "
1310		<< status << ": " << reason << llendl;
1311	gInventory.notifyObservers();
1312}
1313
1314bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const
1315{
1316	if(folder_id.isNull()) 
1317	{
1318		llwarns << "Calling fetch descendents on NULL folder id!" << llendl;
1319		return false;
1320	}
1321	LLViewerInventoryCategory* cat = getCategory(folder_id);
1322	if(!cat)
1323	{
1324		llwarns << "Asked to fetch descendents of non-existent folder: "
1325				<< folder_id << llendl;
1326		return false;
1327	}
1328	//S32 known_descendents = 0;
1329	///cat_array_t* categories = get_ptr_in_map(mParentChildCategoryTree, folder_id);
1330	//item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id);
1331	//if(categories)
1332	//{
1333	//	known_descendents += categories->count();
1334	//}
1335	//if(items)
1336	//{
1337	//	known_descendents += items->count();
1338	//}
1339	return cat->fetch();
1340}
1341
1342void LLInventoryModel::cache(
1343	const LLUUID& parent_folder_id,
1344	const LLUUID& agent_id)
1345{
1346	lldebugs << "Caching " << parent_folder_id << " for " << agent_id
1347			 << llendl;
1348	LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);
1349	if(!root_cat) return;
1350	cat_array_t categories;
1351	categories.put(root_cat);
1352	item_array_t items;
1353
1354	LLCanCache can_cache(this);
1355	can_cache(root_cat, NULL);
1356	collectDescendentsIf(
1357		parent_folder_id,
1358		categories,
1359		items,
1360		INCLUDE_TRASH,
1361		can_cache);
1362	std::string agent_id_str;
1363	std::string inventory_filename;
1364	agent_id.toString(agent_id_str);
1365	std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_str));
1366	inventory_filename = llformat(CACHE_FORMAT_STRING, path.c_str());
1367	saveToFile(inventory_filename, categories, items);
1368	std::string gzip_filename(inventory_filename);
1369	gzip_filename.append(".gz");
1370	if(gzip_file(inventory_filename, gzip_filename))
1371	{
1372		lldebugs << "Successfully compressed " << inventory_filename << llendl;
1373		LLFile::remove(inventory_filename);
1374	}
1375	else
1376	{
1377		llwarns << "Unable to compress " << inventory_filename << llendl;
1378	}
1379}
1380
1381
1382void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
1383{
1384	//llinfos << "LLInventoryModel::addCategory()" << llendl;
1385	if(category)
1386	{
1387		// We aren't displaying the Meshes folder
1388		if (category->mPreferredType == LLFolderType::FT_MESH)
1389		{
1390			return;
1391		}
1392
1393		// try to localize default names first. See EXT-8319, EXT-7051.
1394		category->localizeName();
1395
1396		// Insert category uniquely into the map
1397		mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
1398		//mInventory[category->getUUID()] = category;
1399	}
1400}
1401
1402void LLInventoryModel::addItem(LLViewerInventoryItem* item)
1403{
1404	llassert(item);
1405	if(item)
1406	{
1407		// This can happen if assettype enums from llassettype.h ever change.
1408		// For example, there is a known backwards compatibility issue in some viewer prototypes prior to when 
1409		// the AT_LINK enum changed from 23 to 24.
1410		if ((item->getType() == LLAssetType::AT_NONE)
1411		    || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup())
1412		{
1413			llwarns << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << llendl;
1414			return;
1415		}
1416
1417		// This condition means that we tried to add a link without the baseobj being in memory.
1418		// The item will show up as a broken link.
1419		if (item->getIsBrokenLink())
1420		{
1421			llinfos << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " )  parent: " << item->getParentUUID() << llendl;
1422		}
1423
1424		mItemMap[item->getUUID()] = item;
1425	}
1426}
1427
1428// Empty the entire contents
1429void LLInventoryModel::empty()
1430{
1431//	llinfos << "LLInventoryModel::empty()" << llendl;
1432	std::for_each(
1433		mParentChildCategoryTree.begin(),
1434		mParentChildCategoryTree.end(),
1435		DeletePairedPointer());
1436	mParentChildCategoryTree.clear();
1437	std::for_each(
1438		mParentChildItemTree.begin(),
1439		mParentChildItemTree.end(),
1440		DeletePairedPointer());
1441	mParentChildItemTree.clear();
1442	mCategoryMap.clear(); // remove all references (should delete entries)
1443	mItemMap.clear(); // remove all references (should delete entries)
1444	mLastItem = NULL;
1445	//mInventory.clear();
1446}
1447
1448void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
1449{
1450	LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
1451	if(cat)
1452	{
1453		bool accounted = false;
1454		S32 version = cat->getVersion();
1455		if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
1456		{
1457			S32 descendents_server = cat->getDescendentCount();
1458			LLInventoryModel::cat_array_t* cats;
1459			LLInventoryModel::item_array_t* items;
1460			getDirectDescendentsOf(update.mCategoryID, cats, items);
1461			S32 descendents_actual = 0;
1462			if(cats && items)
1463			{
1464				descendents_actual = cats->count() + items->count();
1465			}
1466			if(descendents_server == descendents_actual)
1467			{
1468				accounted = true;
1469				descendents_actual += update.mDescendentDelta;
1470				cat->setDescendentCount(descendents_actual);
1471				cat->setVersion(++version);
1472				lldebugs << "accounted: '" << cat->getName() << "' "
1473						 << version << " with " << descendents_actual
1474						 << " descendents." << llendl;
1475			}
1476		}
1477		if(!accounted)
1478		{
1479			// Error condition, this means that the category did not register that
1480			// it got new descendents (perhaps because it is still being loaded)
1481			// which means its descendent count will be wrong.
1482			llwarns << "Accounting failed for '" << cat->getName() << "' version:"
1483					 << version << llendl;
1484		}
1485	}
1486	else
1487	{
1488		llwarns << "No category found for update " << update.mCategoryID << llendl;
1489	}
1490}
1491
1492void LLInventoryModel::accountForUpdate(
1493	const LLInventoryModel::update_list_t& update)
1494{
1495	update_list_t::const_iterator it = update.begin();
1496	update_list_t::const_iterator end = update.end();
1497	for(; it != end; ++it)
1498	{
1499		accountForUpdate(*it);
1500	}
1501}
1502
1503void LLInventoryModel::accountForUpdate(
1504	const LLInventoryModel::update_map_t& update)
1505{
1506	LLCategoryUpdate up;
1507	update_map_t::const_iterator it = update.begin();
1508	update_map_t::const_iterator end = update.end();
1509	for(; it != end; ++it)
1510	{
1511		up.mCategoryID = (*it).first;
1512		up.mDescendentDelta = (*it).second.mValue;
1513		accountForUpdate(up);
1514	}
1515}
1516
1517
1518/*
1519void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id)
1520{
1521	LLViewerInventoryCategory* cat = getCategory(category_id);
1522	if(cat)
1523	{
1524		S32 version = cat->getVersion();
1525		if(LLViewerInventoryCategory::VERSION_UNKNOWN != version)
1526		{
1527			cat->setVersion(version + 1);
1528			llinfos << "IncrementVersion: " << cat->getName() << " "
1529					<< cat->getVersion() << llendl;
1530		}
1531		else
1532		{
1533			llinfos << "Attempt to increment version when unknown: "
1534					<< category_id << llendl;
1535		}
1536	}
1537	else
1538	{
1539		llinfos << "Attempt to increment category: " << category_id << llendl;
1540	}
1541}
1542void LLInventoryModel::incrementCategorySetVersion(
1543	const std::set<LLUUID>& categories)
1544{
1545	if(!categories.empty())
1546	{ 
1547		std::set<LLUUID>::const_iterator it = categories.begin();
1548		std::set<LLUUID>::const_iterator end = categories.end();
1549		for(; it != end; ++it)
1550		{
1551			incrementCategoryVersion(*it);
1552		}
1553	}
1554}
1555*/
1556
1557
1558LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
1559	const LLUUID& cat_id) const
1560{
1561	LLViewerInventoryCategory* cat = getCategory(cat_id);
1562	if(!cat) return CHILDREN_NO;
1563	if(cat->getDescendentCount() > 0)
1564	{
1565		return CHILDREN_YES;
1566	}
1567	if(cat->getDescendentCount() == 0)
1568	{
1569		return CHILDREN_NO;
1570	}
1571	if((cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
1572	   || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
1573	{
1574		return CHILDREN_MAYBE;
1575	}
1576
1577	// Shouldn't have to run this, but who knows.
1578	parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID());
1579	if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0)
1580	{
1581		return CHILDREN_YES;
1582	}
1583	parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID());
1584	if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0)
1585	{
1586		return CHILDREN_YES;
1587	}
1588
1589	return CHILDREN_NO;
1590}
1591
1592bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
1593{
1594	LLViewerInventoryCategory* cat = getCategory(cat_id);
1595	if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
1596	{
1597		S32 descendents_server = cat->getDescendentCount();
1598		LLInventoryModel::cat_array_t* cats;
1599		LLInventoryModel::item_array_t* items;
1600		getDirectDescendentsOf(cat_id, cats, items);
1601		S32 descendents_actual = 0;
1602		if(cats && items)
1603		{
1604			descendents_actual = cats->count() + items->count();
1605		}
1606		if(descendents_server == descendents_actual)
1607		{
1608			return true;
1609		}
1610	}
1611	return false;
1612}
1613
1614bool LLInventoryModel::loadSkeleton(
1615	const LLSD& options,
1616	const LLUUID& owner_id)
1617{
1618	lldebugs << "importing inventory skeleton for " << owner_id << llendl;
1619
1620	typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
1621	cat_set_t temp_cats;
1622	bool rv = true;
1623
1624	for(LLSD::array_const_iterator it = options.beginArray(),
1625		end = options.endArray(); it != end; ++it)
1626	{
1627		LLSD name = (*it)["name"];
1628		LLSD folder_id = (*it)["folder_id"];
1629		LLSD parent_id = (*it)["parent_id"];
1630		LLSD version = (*it)["version"];
1631		if(name.isDefined()
1632			&& folder_id.isDefined()
1633			&& parent_id.isDefined()
1634			&& version.isDefined()
1635			&& folder_id.asUUID().notNull() // if an id is null, it locks the viewer.
1636			) 		
1637		{
1638			LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id);
1639			cat->rename(name.asString());
1640			cat->setUUID(folder_id.asUUID());
1641			cat->setParent(parent_id.asUUID());
1642
1643			LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
1644			LLSD type_default = (*it)["type_default"];
1645			if(type_default.isDefined())
1646            {
1647				preferred_type = (LLFolderType::EType)type_default.asInteger();
1648            }
1649            cat->setPreferredType(preferred_type);
1650			cat->setVersion(version.asInteger());
1651            temp_cats.insert(cat);
1652		}
1653		else
1654		{
1655			llwarns << "Unable to import near " << name.asString() << llendl;
1656            rv = false;
1657		}
1658	}
1659
1660	S32 cached_category_count = 0;
1661	S32 cached_item_count = 0;
1662	if(!temp_cats.empty())
1663	{
1664		update_map_t child_counts;
1665		cat_array_t categories;
1666		item_array_t items;
1667		cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded.
1668		std::string owner_id_str;
1669		owner_id.toString(owner_id_str);
1670		std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str));
1671		std::string inventory_filename;
1672		inventory_filename = llformat(CACHE_FORMAT_STRING, path.c_str());
1673		const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN;
1674		std::string gzip_filename(inventory_filename);
1675		gzip_filename.append(".gz");
1676		LLFILE* fp = LLFile::fopen(gzip_filename, "rb");
1677		bool remove_inventory_file = false;
1678		if(fp)
1679		{
1680			fclose(fp);
1681			fp = NULL;
1682			if(gunzip_file(gzip_filename, inventory_filename))
1683			{
1684				// we only want to remove the inventory file if it was
1685				// gzipped before we loaded, and we successfully
1686				// gunziped it.
1687				remove_inventory_file = true;
1688			}
1689			else
1690			{
1691				llinfos << "Unable to gunzip " << gzip_filename << llendl;
1692			}
1693		}
1694		bool is_cache_obsolete = false;
1695		if(loadFromFile(inventory_filename, categories, items, is_cache_obsolete))
1696		{
1697			// We were able to find a cache of files. So, use what we
1698			// found to generate a set of categories we should add. We
1699			// will go through each category loaded and if the version
1700			// does not match, invalidate the version.
1701			S32 count = categories.count();
1702			cat_set_t::iterator not_cached = temp_cats.end();
1703			std::set<LLUUID> cached_ids;
1704			for(S32 i = 0; i < count; ++i)
1705			{
1706				LLViewerInventoryCategory* cat = categories[i];
1707				cat_set_t::iterator cit = temp_cats.find(cat);
1708				if (cit == temp_cats.end())
1709				{
1710					continue; // cache corruption?? not sure why this happens -SJB
1711				}
1712				LLViewerInventoryCategory* tcat = *cit;
1713				
1714				// we can safely ignore anything loaded from file, but
1715				// not sent down in the skeleton.
1716				if(cit == not_cached)
1717				{
1718					continue;
1719				}
1720				if(cat->getVersion() != tcat->getVersion())
1721				{
1722					// if the cached version does not match the server version,
1723					// throw away the version we have so we can fetch the
1724					// correct contents the next time the viewer opens the folder.
1725					tcat->setVersion(NO_VERSION);
1726				}
1727				else
1728				{
1729					cached_ids.insert(tcat->getUUID());
1730				}
1731			}
1732
1733			// go ahead and add the cats returned during the download
1734			std::set<LLUUID>::const_iterator not_cached_id = cached_ids.end();
1735			cached_category_count = cached_ids.size();
1736			for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
1737			{
1738				if(cached_ids.find((*it)->getUUID()) == not_cached_id)
1739				{
1740					// this check is performed so that we do not
1741					// mark new folders in the skeleton (and not in cache)
1742					// as being cached.
1743					LLViewerInventoryCategory *llvic = (*it);
1744					llvic->setVersion(NO_VERSION);
1745				}
1746				addCategory(*it);
1747				++child_counts[(*it)->getParentUUID()];
1748			}
1749
1750			// Add all the items loaded which are parented to a
1751			// category with a correctly cached parent
1752			S32 bad_link_count = 0;
1753			cat_map_t::iterator unparented = mCategoryMap.end();
1754			for(item_array_t::const_iterator item_iter = items.begin();
1755				item_iter != items.end();
1756				++item_iter)
1757			{
1758				LLViewerInventoryItem *item = (*item_iter).get();
1759				const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID());
1760				
1761				if(cit != unparented)
1762				{
1763					const LLViewerInventoryCategory* cat = cit->second.get();
1764					if(cat->getVersion() != NO_VERSION)
1765					{
1766						// This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache.
1767						if (item->getIsBrokenLink())
1768						{
1769							bad_link_count++;
1770							lldebugs << "Attempted to add cached l…

Large files files are truncated, but you can click here to view the full file