PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llinventoryobserver.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 822 lines | 638 code | 96 blank | 88 comment | 128 complexity | db2212dedc240161e24b5021c0dfbfcd MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llinventoryobserver.cpp
  3. * @brief Implementation of the inventory observers 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. #include "llviewerprecompiledheaders.h"
  27. #include "llinventoryobserver.h"
  28. #include "llassetstorage.h"
  29. #include "llcrc.h"
  30. #include "lldir.h"
  31. #include "llsys.h"
  32. #include "llxfermanager.h"
  33. #include "message.h"
  34. #include "llagent.h"
  35. #include "llagentwearables.h"
  36. #include "llfloater.h"
  37. #include "llfocusmgr.h"
  38. #include "llinventorybridge.h"
  39. #include "llinventoryfunctions.h"
  40. #include "llinventorymodel.h"
  41. #include "llviewermessage.h"
  42. #include "llviewerwindow.h"
  43. #include "llviewerregion.h"
  44. #include "llappviewer.h"
  45. #include "lldbstrings.h"
  46. #include "llviewerstats.h"
  47. #include "llnotificationsutil.h"
  48. #include "llcallbacklist.h"
  49. #include "llpreview.h"
  50. #include "llviewercontrol.h"
  51. #include "llvoavatarself.h"
  52. #include "llsdutil.h"
  53. #include <deque>
  54. const F32 LLInventoryFetchItemsObserver::FETCH_TIMER_EXPIRY = 60.0f;
  55. LLInventoryObserver::LLInventoryObserver()
  56. {
  57. }
  58. // virtual
  59. LLInventoryObserver::~LLInventoryObserver()
  60. {
  61. }
  62. LLInventoryFetchObserver::LLInventoryFetchObserver(const LLUUID& id)
  63. {
  64. mIDs.clear();
  65. if (id != LLUUID::null)
  66. {
  67. setFetchID(id);
  68. }
  69. }
  70. LLInventoryFetchObserver::LLInventoryFetchObserver(const uuid_vec_t& ids)
  71. {
  72. setFetchIDs(ids);
  73. }
  74. BOOL LLInventoryFetchObserver::isFinished() const
  75. {
  76. return mIncomplete.empty();
  77. }
  78. void LLInventoryFetchObserver::setFetchIDs(const uuid_vec_t& ids)
  79. {
  80. mIDs = ids;
  81. }
  82. void LLInventoryFetchObserver::setFetchID(const LLUUID& id)
  83. {
  84. mIDs.clear();
  85. mIDs.push_back(id);
  86. }
  87. void LLInventoryCompletionObserver::changed(U32 mask)
  88. {
  89. // scan through the incomplete items and move or erase them as
  90. // appropriate.
  91. if (!mIncomplete.empty())
  92. {
  93. for (uuid_vec_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
  94. {
  95. const LLViewerInventoryItem* item = gInventory.getItem(*it);
  96. if (!item)
  97. {
  98. it = mIncomplete.erase(it);
  99. continue;
  100. }
  101. if (item->isFinished())
  102. {
  103. mComplete.push_back(*it);
  104. it = mIncomplete.erase(it);
  105. continue;
  106. }
  107. ++it;
  108. }
  109. if (mIncomplete.empty())
  110. {
  111. done();
  112. }
  113. }
  114. }
  115. void LLInventoryCompletionObserver::watchItem(const LLUUID& id)
  116. {
  117. if (id.notNull())
  118. {
  119. mIncomplete.push_back(id);
  120. }
  121. }
  122. LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const LLUUID& item_id) :
  123. LLInventoryFetchObserver(item_id)
  124. {
  125. mIDs.clear();
  126. mIDs.push_back(item_id);
  127. }
  128. LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const uuid_vec_t& item_ids) :
  129. LLInventoryFetchObserver(item_ids)
  130. {
  131. }
  132. void LLInventoryFetchItemsObserver::changed(U32 mask)
  133. {
  134. lldebugs << this << " remaining incomplete " << mIncomplete.size()
  135. << " complete " << mComplete.size()
  136. << " wait period " << mFetchingPeriod.getRemainingTimeF32()
  137. << llendl;
  138. // scan through the incomplete items and move or erase them as
  139. // appropriate.
  140. if (!mIncomplete.empty())
  141. {
  142. // Have we exceeded max wait time?
  143. bool timeout_expired = mFetchingPeriod.hasExpired();
  144. for (uuid_vec_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
  145. {
  146. const LLUUID& item_id = (*it);
  147. LLViewerInventoryItem* item = gInventory.getItem(item_id);
  148. if (item && item->isFinished())
  149. {
  150. mComplete.push_back(item_id);
  151. it = mIncomplete.erase(it);
  152. }
  153. else
  154. {
  155. if (timeout_expired)
  156. {
  157. // Just concede that this item hasn't arrived in reasonable time and continue on.
  158. llwarns << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL;
  159. it = mIncomplete.erase(it);
  160. }
  161. else
  162. {
  163. // Keep trying.
  164. ++it;
  165. }
  166. }
  167. }
  168. }
  169. if (mIncomplete.empty())
  170. {
  171. lldebugs << this << " done at remaining incomplete "
  172. << mIncomplete.size() << " complete " << mComplete.size() << llendl;
  173. done();
  174. }
  175. //llinfos << "LLInventoryFetchItemsObserver::changed() mComplete size " << mComplete.size() << llendl;
  176. //llinfos << "LLInventoryFetchItemsObserver::changed() mIncomplete size " << mIncomplete.size() << llendl;
  177. }
  178. void fetch_items_from_llsd(const LLSD& items_llsd)
  179. {
  180. if (!items_llsd.size() || gDisconnected) return;
  181. LLSD body;
  182. body[0]["cap_name"] = "FetchInventory2";
  183. body[1]["cap_name"] = "FetchLib2";
  184. for (S32 i=0; i<items_llsd.size();i++)
  185. {
  186. if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString())
  187. {
  188. body[0]["items"].append(items_llsd[i]);
  189. continue;
  190. }
  191. else if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString())
  192. {
  193. body[1]["items"].append(items_llsd[i]);
  194. continue;
  195. }
  196. }
  197. for (S32 i=0; i<body.size(); i++)
  198. {
  199. if (!gAgent.getRegion())
  200. {
  201. llwarns << "Agent's region is null" << llendl;
  202. break;
  203. }
  204. if (0 == body[i]["items"].size()) {
  205. lldebugs << "Skipping body with no items to fetch" << llendl;
  206. continue;
  207. }
  208. std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString());
  209. if (!url.empty())
  210. {
  211. body[i]["agent_id"] = gAgent.getID();
  212. LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i]));
  213. continue;
  214. }
  215. LLMessageSystem* msg = gMessageSystem;
  216. BOOL start_new_message = TRUE;
  217. for (S32 j=0; j<body[i]["items"].size(); j++)
  218. {
  219. LLSD item_entry = body[i]["items"][j];
  220. if (start_new_message)
  221. {
  222. start_new_message = FALSE;
  223. msg->newMessageFast(_PREHASH_FetchInventory);
  224. msg->nextBlockFast(_PREHASH_AgentData);
  225. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  226. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  227. }
  228. msg->nextBlockFast(_PREHASH_InventoryData);
  229. msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID());
  230. msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID());
  231. if (msg->isSendFull(NULL))
  232. {
  233. start_new_message = TRUE;
  234. gAgent.sendReliableMessage();
  235. }
  236. }
  237. if (!start_new_message)
  238. {
  239. gAgent.sendReliableMessage();
  240. }
  241. }
  242. }
  243. void LLInventoryFetchItemsObserver::startFetch()
  244. {
  245. LLUUID owner_id;
  246. LLSD items_llsd;
  247. for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it)
  248. {
  249. LLViewerInventoryItem* item = gInventory.getItem(*it);
  250. if (item)
  251. {
  252. if (item->isFinished())
  253. {
  254. // It's complete, so put it on the complete container.
  255. mComplete.push_back(*it);
  256. continue;
  257. }
  258. else
  259. {
  260. owner_id = item->getPermissions().getOwner();
  261. }
  262. }
  263. else
  264. {
  265. // assume it's agent inventory.
  266. owner_id = gAgent.getID();
  267. }
  268. // Ignore categories since they're not items. We
  269. // could also just add this to mComplete but not sure what the
  270. // side-effects would be, so ignoring to be safe.
  271. LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
  272. if (cat)
  273. {
  274. continue;
  275. }
  276. // It's incomplete, so put it on the incomplete container, and
  277. // pack this on the message.
  278. mIncomplete.push_back(*it);
  279. // Prepare the data to fetch
  280. LLSD item_entry;
  281. item_entry["owner_id"] = owner_id;
  282. item_entry["item_id"] = (*it);
  283. items_llsd.append(item_entry);
  284. }
  285. mFetchingPeriod.reset();
  286. mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY);
  287. fetch_items_from_llsd(items_llsd);
  288. }
  289. LLInventoryFetchDescendentsObserver::LLInventoryFetchDescendentsObserver(const LLUUID& cat_id) :
  290. LLInventoryFetchObserver(cat_id)
  291. {
  292. }
  293. LLInventoryFetchDescendentsObserver::LLInventoryFetchDescendentsObserver(const uuid_vec_t& cat_ids) :
  294. LLInventoryFetchObserver(cat_ids)
  295. {
  296. }
  297. // virtual
  298. void LLInventoryFetchDescendentsObserver::changed(U32 mask)
  299. {
  300. for (uuid_vec_t::iterator it = mIncomplete.begin(); it < mIncomplete.end();)
  301. {
  302. const LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
  303. if (!cat)
  304. {
  305. it = mIncomplete.erase(it);
  306. continue;
  307. }
  308. if (isCategoryComplete(cat))
  309. {
  310. mComplete.push_back(*it);
  311. it = mIncomplete.erase(it);
  312. continue;
  313. }
  314. ++it;
  315. }
  316. if (mIncomplete.empty())
  317. {
  318. done();
  319. }
  320. }
  321. void LLInventoryFetchDescendentsObserver::startFetch()
  322. {
  323. for (uuid_vec_t::const_iterator it = mIDs.begin(); it != mIDs.end(); ++it)
  324. {
  325. LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
  326. if (!cat) continue;
  327. if (!isCategoryComplete(cat))
  328. {
  329. cat->fetch(); //blindly fetch it without seeing if anything else is fetching it.
  330. mIncomplete.push_back(*it); //Add to list of things being downloaded for this observer.
  331. }
  332. else
  333. {
  334. mComplete.push_back(*it);
  335. }
  336. }
  337. }
  338. BOOL LLInventoryFetchDescendentsObserver::isCategoryComplete(const LLViewerInventoryCategory* cat) const
  339. {
  340. const S32 version = cat->getVersion();
  341. const S32 expected_num_descendents = cat->getDescendentCount();
  342. if ((version == LLViewerInventoryCategory::VERSION_UNKNOWN) ||
  343. (expected_num_descendents == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN))
  344. {
  345. return FALSE;
  346. }
  347. // it might be complete - check known descendents against
  348. // currently available.
  349. LLInventoryModel::cat_array_t* cats;
  350. LLInventoryModel::item_array_t* items;
  351. gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
  352. if (!cats || !items)
  353. {
  354. llwarns << "Category '" << cat->getName() << "' descendents corrupted, fetch failed." << llendl;
  355. // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean
  356. // that the cat just doesn't have any items or subfolders).
  357. // Unrecoverable, so just return done so that this observer can be cleared
  358. // from memory.
  359. return TRUE;
  360. }
  361. const S32 current_num_known_descendents = cats->count() + items->count();
  362. // Got the number of descendents that we were expecting, so we're done.
  363. if (current_num_known_descendents == expected_num_descendents)
  364. {
  365. return TRUE;
  366. }
  367. // Error condition, but recoverable. This happens if something was added to the
  368. // category before it was initialized, so accountForUpdate didn't update descendent
  369. // count and thus the category thinks it has fewer descendents than it actually has.
  370. if (current_num_known_descendents >= expected_num_descendents)
  371. {
  372. llwarns << "Category '" << cat->getName() << "' expected descendentcount:" << expected_num_descendents << " descendents but got descendentcount:" << current_num_known_descendents << llendl;
  373. const_cast<LLViewerInventoryCategory *>(cat)->setDescendentCount(current_num_known_descendents);
  374. return TRUE;
  375. }
  376. return FALSE;
  377. }
  378. LLInventoryFetchComboObserver::LLInventoryFetchComboObserver(const uuid_vec_t& folder_ids,
  379. const uuid_vec_t& item_ids)
  380. {
  381. mFetchDescendents = new LLInventoryFetchDescendentsObserver(folder_ids);
  382. uuid_vec_t pruned_item_ids;
  383. for (uuid_vec_t::const_iterator item_iter = item_ids.begin();
  384. item_iter != item_ids.end();
  385. ++item_iter)
  386. {
  387. const LLUUID& item_id = (*item_iter);
  388. const LLViewerInventoryItem* item = gInventory.getItem(item_id);
  389. if (item && std::find(folder_ids.begin(), folder_ids.end(), item->getParentUUID()) == folder_ids.end())
  390. {
  391. continue;
  392. }
  393. pruned_item_ids.push_back(item_id);
  394. }
  395. mFetchItems = new LLInventoryFetchItemsObserver(pruned_item_ids);
  396. mFetchDescendents = new LLInventoryFetchDescendentsObserver(folder_ids);
  397. }
  398. LLInventoryFetchComboObserver::~LLInventoryFetchComboObserver()
  399. {
  400. mFetchItems->done();
  401. mFetchDescendents->done();
  402. delete mFetchItems;
  403. delete mFetchDescendents;
  404. }
  405. void LLInventoryFetchComboObserver::changed(U32 mask)
  406. {
  407. mFetchItems->changed(mask);
  408. mFetchDescendents->changed(mask);
  409. if (mFetchItems->isFinished() && mFetchDescendents->isFinished())
  410. {
  411. done();
  412. }
  413. }
  414. void LLInventoryFetchComboObserver::startFetch()
  415. {
  416. mFetchItems->startFetch();
  417. mFetchDescendents->startFetch();
  418. }
  419. void LLInventoryExistenceObserver::watchItem(const LLUUID& id)
  420. {
  421. if (id.notNull())
  422. {
  423. mMIA.push_back(id);
  424. }
  425. }
  426. void LLInventoryExistenceObserver::changed(U32 mask)
  427. {
  428. // scan through the incomplete items and move or erase them as
  429. // appropriate.
  430. if (!mMIA.empty())
  431. {
  432. for (uuid_vec_t::iterator it = mMIA.begin(); it < mMIA.end(); )
  433. {
  434. LLViewerInventoryItem* item = gInventory.getItem(*it);
  435. if (!item)
  436. {
  437. ++it;
  438. continue;
  439. }
  440. mExist.push_back(*it);
  441. it = mMIA.erase(it);
  442. }
  443. if (mMIA.empty())
  444. {
  445. done();
  446. }
  447. }
  448. }
  449. void LLInventoryAddItemByAssetObserver::changed(U32 mask)
  450. {
  451. if(!(mask & LLInventoryObserver::ADD))
  452. {
  453. return;
  454. }
  455. // nothing is watched
  456. if (mWatchedAssets.size() == 0)
  457. {
  458. return;
  459. }
  460. LLMessageSystem* msg = gMessageSystem;
  461. if (!(msg->getMessageName() && (0 == strcmp(msg->getMessageName(), "UpdateCreateInventoryItem"))))
  462. {
  463. // this is not our message
  464. return; // to prevent a crash. EXT-7921;
  465. }
  466. LLPointer<LLViewerInventoryItem> item = new LLViewerInventoryItem;
  467. S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
  468. for(S32 i = 0; i < num_blocks; ++i)
  469. {
  470. item->unpackMessage(msg, _PREHASH_InventoryData, i);
  471. const LLUUID& asset_uuid = item->getAssetUUID();
  472. if (item->getUUID().notNull() && asset_uuid.notNull())
  473. {
  474. if (isAssetWatched(asset_uuid))
  475. {
  476. LL_DEBUGS("Inventory_Move") << "Found asset UUID: " << asset_uuid << LL_ENDL;
  477. mAddedItems.push_back(item->getUUID());
  478. }
  479. }
  480. }
  481. if (mAddedItems.size() == mWatchedAssets.size())
  482. {
  483. done();
  484. LL_DEBUGS("Inventory_Move") << "All watched items are added & processed." << LL_ENDL;
  485. mAddedItems.clear();
  486. // Unable to clean watched items here due to somebody can require to check them in current frame.
  487. // set dirty state to clean them while next watch cycle.
  488. mIsDirty = true;
  489. }
  490. }
  491. void LLInventoryAddItemByAssetObserver::watchAsset(const LLUUID& asset_id)
  492. {
  493. if(asset_id.notNull())
  494. {
  495. if (mIsDirty)
  496. {
  497. LL_DEBUGS("Inventory_Move") << "Watched items are dirty. Clean them." << LL_ENDL;
  498. mWatchedAssets.clear();
  499. mIsDirty = false;
  500. }
  501. mWatchedAssets.push_back(asset_id);
  502. onAssetAdded(asset_id);
  503. }
  504. }
  505. bool LLInventoryAddItemByAssetObserver::isAssetWatched( const LLUUID& asset_id )
  506. {
  507. return std::find(mWatchedAssets.begin(), mWatchedAssets.end(), asset_id) != mWatchedAssets.end();
  508. }
  509. void LLInventoryAddedObserver::changed(U32 mask)
  510. {
  511. if (!(mask & LLInventoryObserver::ADD))
  512. {
  513. return;
  514. }
  515. // *HACK: If this was in response to a packet off
  516. // the network, figure out which item was updated.
  517. LLMessageSystem* msg = gMessageSystem;
  518. std::string msg_name = msg->getMessageName();
  519. if (msg_name.empty())
  520. {
  521. return;
  522. }
  523. // We only want newly created inventory items. JC
  524. if ( msg_name != "UpdateCreateInventoryItem")
  525. {
  526. return;
  527. }
  528. LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
  529. S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
  530. for (S32 i = 0; i < num_blocks; ++i)
  531. {
  532. titem->unpackMessage(msg, _PREHASH_InventoryData, i);
  533. if (!(titem->getUUID().isNull()))
  534. {
  535. //we don't do anything with null keys
  536. mAdded.push_back(titem->getUUID());
  537. }
  538. }
  539. if (!mAdded.empty())
  540. {
  541. done();
  542. }
  543. }
  544. void LLInventoryCategoryAddedObserver::changed(U32 mask)
  545. {
  546. if (!(mask & LLInventoryObserver::ADD))
  547. {
  548. return;
  549. }
  550. const LLInventoryModel::changed_items_t& changed_ids = gInventory.getChangedIDs();
  551. for (LLInventoryModel::changed_items_t::const_iterator cit = changed_ids.begin(); cit != changed_ids.end(); ++cit)
  552. {
  553. LLViewerInventoryCategory* cat = gInventory.getCategory(*cit);
  554. if (cat)
  555. {
  556. mAddedCategories.push_back(cat);
  557. }
  558. }
  559. if (!mAddedCategories.empty())
  560. {
  561. done();
  562. mAddedCategories.clear();
  563. }
  564. }
  565. LLInventoryTransactionObserver::LLInventoryTransactionObserver(const LLTransactionID& transaction_id) :
  566. mTransactionID(transaction_id)
  567. {
  568. }
  569. void LLInventoryTransactionObserver::changed(U32 mask)
  570. {
  571. if (mask & LLInventoryObserver::ADD)
  572. {
  573. // This could be it - see if we are processing a bulk update
  574. LLMessageSystem* msg = gMessageSystem;
  575. if (msg->getMessageName()
  576. && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory")))
  577. {
  578. // we have a match for the message - now check the
  579. // transaction id.
  580. LLUUID id;
  581. msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id);
  582. if (id == mTransactionID)
  583. {
  584. // woo hoo, we found it
  585. uuid_vec_t folders;
  586. uuid_vec_t items;
  587. S32 count;
  588. count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
  589. S32 i;
  590. for (i = 0; i < count; ++i)
  591. {
  592. msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i);
  593. if (id.notNull())
  594. {
  595. folders.push_back(id);
  596. }
  597. }
  598. count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
  599. for (i = 0; i < count; ++i)
  600. {
  601. msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i);
  602. if (id.notNull())
  603. {
  604. items.push_back(id);
  605. }
  606. }
  607. // call the derived class the implements this method.
  608. done(folders, items);
  609. }
  610. }
  611. }
  612. }
  613. void LLInventoryCategoriesObserver::changed(U32 mask)
  614. {
  615. if (!mCategoryMap.size())
  616. return;
  617. for (category_map_t::iterator iter = mCategoryMap.begin();
  618. iter != mCategoryMap.end();
  619. ++iter)
  620. {
  621. const LLUUID& cat_id = (*iter).first;
  622. LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
  623. if (!category)
  624. continue;
  625. const S32 version = category->getVersion();
  626. const S32 expected_num_descendents = category->getDescendentCount();
  627. if ((version == LLViewerInventoryCategory::VERSION_UNKNOWN) ||
  628. (expected_num_descendents == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN))
  629. {
  630. continue;
  631. }
  632. // Check number of known descendents to find out whether it has changed.
  633. LLInventoryModel::cat_array_t* cats;
  634. LLInventoryModel::item_array_t* items;
  635. gInventory.getDirectDescendentsOf(cat_id, cats, items);
  636. if (!cats || !items)
  637. {
  638. llwarns << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << llendl;
  639. // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean
  640. // that the cat just doesn't have any items or subfolders).
  641. // Unrecoverable, so just skip this category.
  642. llassert(cats != NULL && items != NULL);
  643. continue;
  644. }
  645. const S32 current_num_known_descendents = cats->count() + items->count();
  646. LLCategoryData& cat_data = (*iter).second;
  647. bool cat_changed = false;
  648. // If category version or descendents count has changed
  649. // update category data in mCategoryMap
  650. if (version != cat_data.mVersion || current_num_known_descendents != cat_data.mDescendentsCount)
  651. {
  652. cat_data.mVersion = version;
  653. cat_data.mDescendentsCount = current_num_known_descendents;
  654. cat_changed = true;
  655. }
  656. // If any item names have changed, update the name hash
  657. // Only need to check if (a) name hash has not previously been
  658. // computed, or (b) a name has changed.
  659. if (!cat_data.mIsNameHashInitialized || (mask & LLInventoryObserver::LABEL))
  660. {
  661. LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cat_id);
  662. if (cat_data.mItemNameHash != item_name_hash)
  663. {
  664. cat_data.mIsNameHashInitialized = true;
  665. cat_data.mItemNameHash = item_name_hash;
  666. cat_changed = true;
  667. }
  668. }
  669. // If anything has changed above, fire the callback.
  670. if (cat_changed)
  671. cat_data.mCallback();
  672. }
  673. }
  674. bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t cb)
  675. {
  676. S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN;
  677. S32 current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN;
  678. bool can_be_added = true;
  679. LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
  680. // If category could not be retrieved it might mean that
  681. // inventory is unusable at the moment so the category is
  682. // stored with VERSION_UNKNOWN and DESCENDENT_COUNT_UNKNOWN,
  683. // it may be updated later.
  684. if (category)
  685. {
  686. // Inventory category version is used to find out if some changes
  687. // to a category have been made.
  688. version = category->getVersion();
  689. LLInventoryModel::cat_array_t* cats;
  690. LLInventoryModel::item_array_t* items;
  691. gInventory.getDirectDescendentsOf(cat_id, cats, items);
  692. if (!cats || !items)
  693. {
  694. llwarns << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << llendl;
  695. // NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean
  696. // that the cat just doesn't have any items or subfolders).
  697. // Unrecoverable, so just return "false" meaning that the category can't be observed.
  698. can_be_added = false;
  699. llassert(cats != NULL && items != NULL);
  700. }
  701. else
  702. {
  703. current_num_known_descendents = cats->count() + items->count();
  704. }
  705. }
  706. if (can_be_added)
  707. {
  708. mCategoryMap.insert(category_map_value_t(
  709. cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents)));
  710. }
  711. return can_be_added;
  712. }
  713. void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id)
  714. {
  715. mCategoryMap.erase(cat_id);
  716. }
  717. LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData(
  718. const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents)
  719. : mCatID(cat_id)
  720. , mCallback(cb)
  721. , mVersion(version)
  722. , mDescendentsCount(num_descendents)
  723. , mIsNameHashInitialized(false)
  724. {
  725. mItemNameHash.finalize();
  726. }