/indra/newview/llappearancemgr.cpp
C++ | 2239 lines | 1776 code | 267 blank | 196 comment | 280 complexity | ad524e20cc0a499602b9525c211390b7 MD5 | raw file
Possible License(s): LGPL-2.1
Large files files are truncated, but you can click here to view the full file
1/** 2 * @file llappearancemgr.cpp 3 * @brief Manager for initiating appearance changes on the viewer 4 * 5 * $LicenseInfo:firstyear=2004&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 29#include "llaccordionctrltab.h" 30#include "llagent.h" 31#include "llagentcamera.h" 32#include "llagentwearables.h" 33#include "llappearancemgr.h" 34#include "llattachmentsmgr.h" 35#include "llcommandhandler.h" 36#include "lleventtimer.h" 37#include "llfloatersidepanelcontainer.h" 38#include "llgesturemgr.h" 39#include "llinventorybridge.h" 40#include "llinventoryfunctions.h" 41#include "llinventoryobserver.h" 42#include "llnotificationsutil.h" 43#include "lloutfitobserver.h" 44#include "lloutfitslist.h" 45#include "llselectmgr.h" 46#include "llsidepanelappearance.h" 47#include "llviewerobjectlist.h" 48#include "llvoavatar.h" 49#include "llvoavatarself.h" 50#include "llviewerregion.h" 51#include "llwearablelist.h" 52 53// RAII thingy to guarantee that a variable gets reset when the Setter 54// goes out of scope. More general utility would be handy - TODO: 55// check boost. 56class BoolSetter 57{ 58public: 59 BoolSetter(bool& var): 60 mVar(var) 61 { 62 mVar = true; 63 } 64 ~BoolSetter() 65 { 66 mVar = false; 67 } 68private: 69 bool& mVar; 70}; 71 72char ORDER_NUMBER_SEPARATOR('@'); 73 74class LLOutfitUnLockTimer: public LLEventTimer 75{ 76public: 77 LLOutfitUnLockTimer(F32 period) : LLEventTimer(period) 78 { 79 // restart timer on BOF changed event 80 LLOutfitObserver::instance().addBOFChangedCallback(boost::bind( 81 &LLOutfitUnLockTimer::reset, this)); 82 stop(); 83 } 84 85 /*virtual*/ 86 BOOL tick() 87 { 88 if(mEventTimer.hasExpired()) 89 { 90 LLAppearanceMgr::instance().setOutfitLocked(false); 91 } 92 return FALSE; 93 } 94 void stop() { mEventTimer.stop(); } 95 void start() { mEventTimer.start(); } 96 void reset() { mEventTimer.reset(); } 97 BOOL getStarted() { return mEventTimer.getStarted(); } 98 99 LLTimer& getEventTimer() { return mEventTimer;} 100}; 101 102// support for secondlife:///app/appearance SLapps 103class LLAppearanceHandler : public LLCommandHandler 104{ 105public: 106 // requests will be throttled from a non-trusted browser 107 LLAppearanceHandler() : LLCommandHandler("appearance", UNTRUSTED_THROTTLE) {} 108 109 bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) 110 { 111 // support secondlife:///app/appearance/show, but for now we just 112 // make all secondlife:///app/appearance SLapps behave this way 113 if (!LLUI::sSettingGroups["config"]->getBOOL("EnableAppearance")) 114 { 115 LLNotificationsUtil::add("NoAppearance", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); 116 return true; 117 } 118 119 LLFloaterSidePanelContainer::showPanel("appearance", LLSD()); 120 return true; 121 } 122}; 123 124LLAppearanceHandler gAppearanceHandler; 125 126 127LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string& name) 128{ 129 LLInventoryModel::cat_array_t cat_array; 130 LLInventoryModel::item_array_t item_array; 131 LLNameCategoryCollector has_name(name); 132 gInventory.collectDescendentsIf(parent_id, 133 cat_array, 134 item_array, 135 LLInventoryModel::EXCLUDE_TRASH, 136 has_name); 137 if (0 == cat_array.count()) 138 return LLUUID(); 139 else 140 { 141 LLViewerInventoryCategory *cat = cat_array.get(0); 142 if (cat) 143 return cat->getUUID(); 144 else 145 { 146 llwarns << "null cat" << llendl; 147 return LLUUID(); 148 } 149 } 150} 151 152class LLWearInventoryCategoryCallback : public LLInventoryCallback 153{ 154public: 155 LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append) 156 { 157 mCatID = cat_id; 158 mAppend = append; 159 } 160 void fire(const LLUUID& item_id) 161 { 162 /* 163 * Do nothing. We only care about the destructor 164 * 165 * The reason for this is that this callback is used in a hack where the 166 * same callback is given to dozens of items, and the destructor is called 167 * after the last item has fired the event and dereferenced it -- if all 168 * the events actually fire! 169 */ 170 } 171 172protected: 173 ~LLWearInventoryCategoryCallback() 174 { 175 llinfos << "done all inventory callbacks" << llendl; 176 177 // Is the destructor called by ordinary dereference, or because the app's shutting down? 178 // If the inventory callback manager goes away, we're shutting down, no longer want the callback. 179 if( LLInventoryCallbackManager::is_instantiated() ) 180 { 181 LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend); 182 } 183 else 184 { 185 llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl; 186 } 187 } 188 189private: 190 LLUUID mCatID; 191 bool mAppend; 192}; 193 194 195//Inventory callback updating "dirty" state when destroyed 196class LLUpdateDirtyState: public LLInventoryCallback 197{ 198public: 199 LLUpdateDirtyState() {} 200 virtual ~LLUpdateDirtyState() 201 { 202 if (LLAppearanceMgr::instanceExists()) 203 { 204 LLAppearanceMgr::getInstance()->updateIsDirty(); 205 } 206 } 207 virtual void fire(const LLUUID&) {} 208}; 209 210 211LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering): 212 mFireCount(0), 213 mUpdateBaseOrder(update_base_outfit_ordering) 214{ 215} 216 217LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy() 218{ 219 llinfos << "done update appearance on destroy" << llendl; 220 221 if (!LLApp::isExiting()) 222 { 223 LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder); 224 } 225} 226 227void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item) 228{ 229 LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item); 230 const std::string item_name = item ? item->getName() : "ITEM NOT FOUND"; 231#ifndef LL_RELEASE_FOR_DOWNLOAD 232 llinfos << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << llendl; 233#endif 234 mFireCount++; 235} 236 237struct LLFoundData 238{ 239 LLFoundData() : 240 mAssetType(LLAssetType::AT_NONE), 241 mWearableType(LLWearableType::WT_INVALID), 242 mWearable(NULL) {} 243 244 LLFoundData(const LLUUID& item_id, 245 const LLUUID& asset_id, 246 const std::string& name, 247 const LLAssetType::EType& asset_type, 248 const LLWearableType::EType& wearable_type, 249 const bool is_replacement = false 250 ) : 251 mItemID(item_id), 252 mAssetID(asset_id), 253 mName(name), 254 mAssetType(asset_type), 255 mWearableType(wearable_type), 256 mIsReplacement(is_replacement), 257 mWearable( NULL ) {} 258 259 LLUUID mItemID; 260 LLUUID mAssetID; 261 std::string mName; 262 LLAssetType::EType mAssetType; 263 LLWearableType::EType mWearableType; 264 LLWearable* mWearable; 265 bool mIsReplacement; 266}; 267 268 269class LLWearableHoldingPattern 270{ 271public: 272 LLWearableHoldingPattern(); 273 ~LLWearableHoldingPattern(); 274 275 bool pollFetchCompletion(); 276 void onFetchCompletion(); 277 bool isFetchCompleted(); 278 bool isTimedOut(); 279 280 void checkMissingWearables(); 281 bool pollMissingWearables(); 282 bool isMissingCompleted(); 283 void recoverMissingWearable(LLWearableType::EType type); 284 void clearCOFLinksForMissingWearables(); 285 286 void onWearableAssetFetch(LLWearable *wearable); 287 void onAllComplete(); 288 289 typedef std::list<LLFoundData> found_list_t; 290 found_list_t& getFoundList(); 291 void eraseTypeToLink(LLWearableType::EType type); 292 void eraseTypeToRecover(LLWearableType::EType type); 293 void setObjItems(const LLInventoryModel::item_array_t& items); 294 void setGestItems(const LLInventoryModel::item_array_t& items); 295 bool isMostRecent(); 296 void handleLateArrivals(); 297 void resetTime(F32 timeout); 298 299private: 300 found_list_t mFoundList; 301 LLInventoryModel::item_array_t mObjItems; 302 LLInventoryModel::item_array_t mGestItems; 303 typedef std::set<S32> type_set_t; 304 type_set_t mTypesToRecover; 305 type_set_t mTypesToLink; 306 S32 mResolved; 307 LLTimer mWaitTime; 308 bool mFired; 309 typedef std::set<LLWearableHoldingPattern*> type_set_hp; 310 static type_set_hp sActiveHoldingPatterns; 311 bool mIsMostRecent; 312 std::set<LLWearable*> mLateArrivals; 313 bool mIsAllComplete; 314}; 315 316LLWearableHoldingPattern::type_set_hp LLWearableHoldingPattern::sActiveHoldingPatterns; 317 318LLWearableHoldingPattern::LLWearableHoldingPattern(): 319 mResolved(0), 320 mFired(false), 321 mIsMostRecent(true), 322 mIsAllComplete(false) 323{ 324 if (sActiveHoldingPatterns.size()>0) 325 { 326 llinfos << "Creating LLWearableHoldingPattern when " 327 << sActiveHoldingPatterns.size() 328 << " other attempts are active." 329 << " Flagging others as invalid." 330 << llendl; 331 for (type_set_hp::iterator it = sActiveHoldingPatterns.begin(); 332 it != sActiveHoldingPatterns.end(); 333 ++it) 334 { 335 (*it)->mIsMostRecent = false; 336 } 337 338 } 339 sActiveHoldingPatterns.insert(this); 340} 341 342LLWearableHoldingPattern::~LLWearableHoldingPattern() 343{ 344 sActiveHoldingPatterns.erase(this); 345} 346 347bool LLWearableHoldingPattern::isMostRecent() 348{ 349 return mIsMostRecent; 350} 351 352LLWearableHoldingPattern::found_list_t& LLWearableHoldingPattern::getFoundList() 353{ 354 return mFoundList; 355} 356 357void LLWearableHoldingPattern::eraseTypeToLink(LLWearableType::EType type) 358{ 359 mTypesToLink.erase(type); 360} 361 362void LLWearableHoldingPattern::eraseTypeToRecover(LLWearableType::EType type) 363{ 364 mTypesToRecover.erase(type); 365} 366 367void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items) 368{ 369 mObjItems = items; 370} 371 372void LLWearableHoldingPattern::setGestItems(const LLInventoryModel::item_array_t& items) 373{ 374 mGestItems = items; 375} 376 377bool LLWearableHoldingPattern::isFetchCompleted() 378{ 379 return (mResolved >= (S32)getFoundList().size()); // have everything we were waiting for? 380} 381 382bool LLWearableHoldingPattern::isTimedOut() 383{ 384 return mWaitTime.hasExpired(); 385} 386 387void LLWearableHoldingPattern::checkMissingWearables() 388{ 389 if (!isMostRecent()) 390 { 391 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 392 } 393 394 std::vector<S32> found_by_type(LLWearableType::WT_COUNT,0); 395 std::vector<S32> requested_by_type(LLWearableType::WT_COUNT,0); 396 for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it) 397 { 398 LLFoundData &data = *it; 399 if (data.mWearableType < LLWearableType::WT_COUNT) 400 requested_by_type[data.mWearableType]++; 401 if (data.mWearable) 402 found_by_type[data.mWearableType]++; 403 } 404 405 for (S32 type = 0; type < LLWearableType::WT_COUNT; ++type) 406 { 407 if (requested_by_type[type] > found_by_type[type]) 408 { 409 llwarns << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl; 410 } 411 if (found_by_type[type] > 0) 412 continue; 413 if ( 414 // If at least one wearable of certain types (pants/shirt/skirt) 415 // was requested but none was found, create a default asset as a replacement. 416 // In all other cases, don't do anything. 417 // For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud 418 // due to logic in LLVOAvatarSelf::getIsCloud(). 419 // For non-critical types (tatoo, socks, etc.) the wearable will just be missing. 420 (requested_by_type[type] > 0) && 421 ((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT))) 422 { 423 mTypesToRecover.insert(type); 424 mTypesToLink.insert(type); 425 recoverMissingWearable((LLWearableType::EType)type); 426 llwarns << "need to replace " << type << llendl; 427 } 428 } 429 430 resetTime(60.0F); 431 if (!pollMissingWearables()) 432 { 433 doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this)); 434 } 435} 436 437void LLWearableHoldingPattern::onAllComplete() 438{ 439 if (!isMostRecent()) 440 { 441 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 442 } 443 444 // Activate all gestures in this folder 445 if (mGestItems.count() > 0) 446 { 447 llinfos << "Activating " << mGestItems.count() << " gestures" << llendl; 448 449 LLGestureMgr::instance().activateGestures(mGestItems); 450 451 // Update the inventory item labels to reflect the fact 452 // they are active. 453 LLViewerInventoryCategory* catp = 454 gInventory.getCategory(LLAppearanceMgr::instance().getCOF()); 455 456 if (catp) 457 { 458 gInventory.updateCategory(catp); 459 gInventory.notifyObservers(); 460 } 461 } 462 463 // Update wearables. 464 llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl; 465 LLAppearanceMgr::instance().updateAgentWearables(this, false); 466 467 // Update attachments to match those requested. 468 if (isAgentAvatarValid()) 469 { 470 llinfos << "Updating " << mObjItems.count() << " attachments" << llendl; 471 LLAgentWearables::userUpdateAttachments(mObjItems); 472 } 473 474 if (isFetchCompleted() && isMissingCompleted()) 475 { 476 // Only safe to delete if all wearable callbacks and all missing wearables completed. 477 delete this; 478 } 479 else 480 { 481 mIsAllComplete = true; 482 handleLateArrivals(); 483 } 484} 485 486void LLWearableHoldingPattern::onFetchCompletion() 487{ 488 if (!isMostRecent()) 489 { 490 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 491 } 492 493 checkMissingWearables(); 494} 495 496// Runs as an idle callback until all wearables are fetched (or we time out). 497bool LLWearableHoldingPattern::pollFetchCompletion() 498{ 499 if (!isMostRecent()) 500 { 501 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 502 } 503 504 bool completed = isFetchCompleted(); 505 bool timed_out = isTimedOut(); 506 bool done = completed || timed_out; 507 508 if (done) 509 { 510 llinfos << "polling, done status: " << completed << " timed out " << timed_out 511 << " elapsed " << mWaitTime.getElapsedTimeF32() << llendl; 512 513 mFired = true; 514 515 if (timed_out) 516 { 517 llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl; 518 } 519 520 onFetchCompletion(); 521 } 522 return done; 523} 524 525class RecoveredItemLinkCB: public LLInventoryCallback 526{ 527public: 528 RecoveredItemLinkCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder): 529 mHolder(holder), 530 mWearable(wearable), 531 mType(type) 532 { 533 } 534 void fire(const LLUUID& item_id) 535 { 536 if (!mHolder->isMostRecent()) 537 { 538 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 539 } 540 541 llinfos << "Recovered item link for type " << mType << llendl; 542 mHolder->eraseTypeToLink(mType); 543 // Add wearable to FoundData for actual wearing 544 LLViewerInventoryItem *item = gInventory.getItem(item_id); 545 LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL; 546 547 if (linked_item) 548 { 549 gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID()); 550 551 if (item) 552 { 553 LLFoundData found(linked_item->getUUID(), 554 linked_item->getAssetUUID(), 555 linked_item->getName(), 556 linked_item->getType(), 557 linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID, 558 true // is replacement 559 ); 560 found.mWearable = mWearable; 561 mHolder->getFoundList().push_front(found); 562 } 563 else 564 { 565 llwarns << "inventory item not found for recovered wearable" << llendl; 566 } 567 } 568 else 569 { 570 llwarns << "inventory link not found for recovered wearable" << llendl; 571 } 572 } 573private: 574 LLWearableHoldingPattern* mHolder; 575 LLWearable *mWearable; 576 LLWearableType::EType mType; 577}; 578 579class RecoveredItemCB: public LLInventoryCallback 580{ 581public: 582 RecoveredItemCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder): 583 mHolder(holder), 584 mWearable(wearable), 585 mType(type) 586 { 587 } 588 void fire(const LLUUID& item_id) 589 { 590 if (!mHolder->isMostRecent()) 591 { 592 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 593 } 594 595 llinfos << "Recovered item for type " << mType << llendl; 596 LLViewerInventoryItem *itemp = gInventory.getItem(item_id); 597 mWearable->setItemID(item_id); 598 LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder); 599 mHolder->eraseTypeToRecover(mType); 600 llassert(itemp); 601 if (itemp) 602 { 603 link_inventory_item( gAgent.getID(), 604 item_id, 605 LLAppearanceMgr::instance().getCOF(), 606 itemp->getName(), 607 itemp->getDescription(), 608 LLAssetType::AT_LINK, 609 cb); 610 } 611 } 612private: 613 LLWearableHoldingPattern* mHolder; 614 LLWearable *mWearable; 615 LLWearableType::EType mType; 616}; 617 618void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type) 619{ 620 if (!isMostRecent()) 621 { 622 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 623 } 624 625 // Try to recover by replacing missing wearable with a new one. 626 LLNotificationsUtil::add("ReplacedMissingWearable"); 627 lldebugs << "Wearable " << LLWearableType::getTypeLabel(type) 628 << " could not be downloaded. Replaced inventory item with default wearable." << llendl; 629 LLWearable* wearable = LLWearableList::instance().createNewWearable(type); 630 631 // Add a new one in the lost and found folder. 632 const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); 633 LLPointer<LLInventoryCallback> cb = new RecoveredItemCB(type,wearable,this); 634 635 create_inventory_item(gAgent.getID(), 636 gAgent.getSessionID(), 637 lost_and_found_id, 638 wearable->getTransactionID(), 639 wearable->getName(), 640 wearable->getDescription(), 641 wearable->getAssetType(), 642 LLInventoryType::IT_WEARABLE, 643 wearable->getType(), 644 wearable->getPermissions().getMaskNextOwner(), 645 cb); 646} 647 648bool LLWearableHoldingPattern::isMissingCompleted() 649{ 650 return mTypesToLink.size()==0 && mTypesToRecover.size()==0; 651} 652 653void LLWearableHoldingPattern::clearCOFLinksForMissingWearables() 654{ 655 for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it) 656 { 657 LLFoundData &data = *it; 658 if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable)) 659 { 660 // Wearable link that was never resolved; remove links to it from COF 661 llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl; 662 LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false); 663 } 664 } 665} 666 667bool LLWearableHoldingPattern::pollMissingWearables() 668{ 669 if (!isMostRecent()) 670 { 671 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 672 } 673 674 bool timed_out = isTimedOut(); 675 bool missing_completed = isMissingCompleted(); 676 bool done = timed_out || missing_completed; 677 678 if (!done) 679 { 680 llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size() 681 << " links " << mTypesToLink.size() 682 << " wearables, timed out " << timed_out 683 << " elapsed " << mWaitTime.getElapsedTimeF32() 684 << " done " << done << llendl; 685 } 686 687 if (done) 688 { 689 gAgentAvatarp->debugWearablesLoaded(); 690 691 // BAP - if we don't call clearCOFLinksForMissingWearables() 692 // here, we won't have to add the link back in later if the 693 // wearable arrives late. This is to avoid corruption of 694 // wearable ordering info. Also has the effect of making 695 // unworn item links visible in the COF under some 696 // circumstances. 697 698 //clearCOFLinksForMissingWearables(); 699 onAllComplete(); 700 } 701 return done; 702} 703 704// Handle wearables that arrived after the timeout period expired. 705void LLWearableHoldingPattern::handleLateArrivals() 706{ 707 // Only safe to run if we have previously finished the missing 708 // wearables and other processing - otherwise we could be in some 709 // intermediate state - but have not been superceded by a later 710 // outfit change request. 711 if (mLateArrivals.size() == 0) 712 { 713 // Nothing to process. 714 return; 715 } 716 if (!isMostRecent()) 717 { 718 llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl; 719 } 720 if (!mIsAllComplete) 721 { 722 llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl; 723 } 724 725 llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl; 726 727 // Update mFoundList using late-arriving wearables. 728 std::set<LLWearableType::EType> replaced_types; 729 for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); 730 iter != getFoundList().end(); ++iter) 731 { 732 LLFoundData& data = *iter; 733 for (std::set<LLWearable*>::iterator wear_it = mLateArrivals.begin(); 734 wear_it != mLateArrivals.end(); 735 ++wear_it) 736 { 737 LLWearable *wearable = *wear_it; 738 739 if(wearable->getAssetID() == data.mAssetID) 740 { 741 data.mWearable = wearable; 742 743 replaced_types.insert(data.mWearableType); 744 745 // BAP - if we didn't call 746 // clearCOFLinksForMissingWearables() earlier, we 747 // don't need to restore the link here. Fixes 748 // wearable ordering problems. 749 750 // LLAppearanceMgr::instance().addCOFItemLink(data.mItemID,false); 751 752 // BAP failing this means inventory or asset server 753 // are corrupted in a way we don't handle. 754 llassert((data.mWearableType < LLWearableType::WT_COUNT) && (wearable->getType() == data.mWearableType)); 755 break; 756 } 757 } 758 } 759 760 // Remove COF links for any default wearables previously used to replace the late arrivals. 761 // All this pussyfooting around with a while loop and explicit 762 // iterator incrementing is to allow removing items from the list 763 // without clobbering the iterator we're using to navigate. 764 LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); 765 while (iter != getFoundList().end()) 766 { 767 LLFoundData& data = *iter; 768 769 // If an item of this type has recently shown up, removed the corresponding replacement wearable from COF. 770 if (data.mWearable && data.mIsReplacement && 771 replaced_types.find(data.mWearableType) != replaced_types.end()) 772 { 773 LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false); 774 std::list<LLFoundData>::iterator clobber_ator = iter; 775 ++iter; 776 getFoundList().erase(clobber_ator); 777 } 778 else 779 { 780 ++iter; 781 } 782 } 783 784 // Clear contents of late arrivals. 785 mLateArrivals.clear(); 786 787 // Update appearance based on mFoundList 788 LLAppearanceMgr::instance().updateAgentWearables(this, false); 789} 790 791void LLWearableHoldingPattern::resetTime(F32 timeout) 792{ 793 mWaitTime.reset(); 794 mWaitTime.setTimerExpirySec(timeout); 795} 796 797void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable) 798{ 799 if (!isMostRecent()) 800 { 801 llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl; 802 } 803 804 mResolved += 1; // just counting callbacks, not successes. 805 llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl; 806 if (!wearable) 807 { 808 llwarns << "no wearable found" << llendl; 809 } 810 811 if (mFired) 812 { 813 llwarns << "called after holder fired" << llendl; 814 if (wearable) 815 { 816 mLateArrivals.insert(wearable); 817 if (mIsAllComplete) 818 { 819 handleLateArrivals(); 820 } 821 } 822 return; 823 } 824 825 if (!wearable) 826 { 827 return; 828 } 829 830 for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin(); 831 iter != getFoundList().end(); ++iter) 832 { 833 LLFoundData& data = *iter; 834 if(wearable->getAssetID() == data.mAssetID) 835 { 836 // Failing this means inventory or asset server are corrupted in a way we don't handle. 837 if ((data.mWearableType >= LLWearableType::WT_COUNT) || (wearable->getType() != data.mWearableType)) 838 { 839 llwarns << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl; 840 break; 841 } 842 843 data.mWearable = wearable; 844 } 845 } 846} 847 848static void onWearableAssetFetch(LLWearable* wearable, void* data) 849{ 850 LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data; 851 holder->onWearableAssetFetch(wearable); 852} 853 854 855static void removeDuplicateItems(LLInventoryModel::item_array_t& items) 856{ 857 LLInventoryModel::item_array_t new_items; 858 std::set<LLUUID> items_seen; 859 std::deque<LLViewerInventoryItem*> tmp_list; 860 // Traverse from the front and keep the first of each item 861 // encountered, so we actually keep the *last* of each duplicate 862 // item. This is needed to give the right priority when adding 863 // duplicate items to an existing outfit. 864 for (S32 i=items.count()-1; i>=0; i--) 865 { 866 LLViewerInventoryItem *item = items.get(i); 867 LLUUID item_id = item->getLinkedUUID(); 868 if (items_seen.find(item_id)!=items_seen.end()) 869 continue; 870 items_seen.insert(item_id); 871 tmp_list.push_front(item); 872 } 873 for (std::deque<LLViewerInventoryItem*>::iterator it = tmp_list.begin(); 874 it != tmp_list.end(); 875 ++it) 876 { 877 new_items.put(*it); 878 } 879 items = new_items; 880} 881 882const LLUUID LLAppearanceMgr::getCOF() const 883{ 884 return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); 885} 886 887 888const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() 889{ 890 const LLUUID& current_outfit_cat = getCOF(); 891 LLInventoryModel::cat_array_t cat_array; 892 LLInventoryModel::item_array_t item_array; 893 // Can't search on FT_OUTFIT since links to categories return FT_CATEGORY for type since they don't 894 // return preferred type. 895 LLIsType is_category( LLAssetType::AT_CATEGORY ); 896 gInventory.collectDescendentsIf(current_outfit_cat, 897 cat_array, 898 item_array, 899 false, 900 is_category, 901 false); 902 for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); 903 iter != item_array.end(); 904 iter++) 905 { 906 const LLViewerInventoryItem *item = (*iter); 907 const LLViewerInventoryCategory *cat = item->getLinkedCategory(); 908 if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) 909 { 910 const LLUUID parent_id = cat->getParentUUID(); 911 LLViewerInventoryCategory* parent_cat = gInventory.getCategory(parent_id); 912 // if base outfit moved to trash it means that we don't have base outfit 913 if (parent_cat != NULL && parent_cat->getPreferredType() == LLFolderType::FT_TRASH) 914 { 915 return NULL; 916 } 917 return item; 918 } 919 } 920 return NULL; 921} 922 923bool LLAppearanceMgr::getBaseOutfitName(std::string& name) 924{ 925 const LLViewerInventoryItem* outfit_link = getBaseOutfitLink(); 926 if(outfit_link) 927 { 928 const LLViewerInventoryCategory *cat = outfit_link->getLinkedCategory(); 929 if (cat) 930 { 931 name = cat->getName(); 932 return true; 933 } 934 } 935 return false; 936} 937 938const LLUUID LLAppearanceMgr::getBaseOutfitUUID() 939{ 940 const LLViewerInventoryItem* outfit_link = getBaseOutfitLink(); 941 if (!outfit_link || !outfit_link->getIsLinkType()) return LLUUID::null; 942 943 const LLViewerInventoryCategory* outfit_cat = outfit_link->getLinkedCategory(); 944 if (!outfit_cat) return LLUUID::null; 945 946 if (outfit_cat->getPreferredType() != LLFolderType::FT_OUTFIT) 947 { 948 llwarns << "Expected outfit type:" << LLFolderType::FT_OUTFIT << " but got type:" << outfit_cat->getType() << " for folder name:" << outfit_cat->getName() << llendl; 949 return LLUUID::null; 950 } 951 952 return outfit_cat->getUUID(); 953} 954 955bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer<LLInventoryCallback> cb) 956{ 957 if (item_id_to_wear.isNull()) return false; 958 959 // *TODO: issue with multi-wearable should be fixed: 960 // in this case this method will be called N times - loading started for each item 961 // and than N times will be called - loading completed for each item. 962 // That means subscribers will be notified that loading is done after first item in a batch is worn. 963 // (loading indicator disappears for example before all selected items are worn) 964 // Have not fix this issue for 2.1 because of stability reason. EXT-7777. 965 966 // Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times 967// gAgentWearables.notifyLoadingStarted(); 968 969 LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); 970 if (!item_to_wear) return false; 971 972 if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) 973 { 974 LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(replace); 975 copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(),cb); 976 return false; 977 } 978 else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) 979 { 980 return false; // not in library and not in agent's inventory 981 } 982 else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) 983 { 984 LLNotificationsUtil::add("CannotWearTrash"); 985 return false; 986 } 987 else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), LLAppearanceMgr::instance().getCOF())) // EXT-84911 988 { 989 return false; 990 } 991 992 switch (item_to_wear->getType()) 993 { 994 case LLAssetType::AT_CLOTHING: 995 if (gAgentWearables.areWearablesLoaded()) 996 { 997 S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType()); 998 if ((replace && wearable_count != 0) || 999 (wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) ) 1000 { 1001 removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false); 1002 } 1003 addCOFItemLink(item_to_wear, do_update, cb); 1004 } 1005 break; 1006 case LLAssetType::AT_BODYPART: 1007 // TODO: investigate wearables may not be loaded at this point EXT-8231 1008 1009 // Remove the existing wearables of the same type. 1010 // Remove existing body parts anyway because we must not be able to wear e.g. two skins. 1011 removeCOFLinksOfType(item_to_wear->getWearableType(), false); 1012 1013 addCOFItemLink(item_to_wear, do_update, cb); 1014 break; 1015 case LLAssetType::AT_OBJECT: 1016 rez_attachment(item_to_wear, NULL, replace); 1017 break; 1018 default: return false;; 1019 } 1020 1021 return true; 1022} 1023 1024// Update appearance from outfit folder. 1025void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool append) 1026{ 1027 if (!proceed) 1028 return; 1029 LLAppearanceMgr::instance().updateCOF(category,append); 1030} 1031 1032void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit) 1033{ 1034 LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit); 1035 wearInventoryCategory(cat, false, false); 1036} 1037 1038// Open outfit renaming dialog. 1039void LLAppearanceMgr::renameOutfit(const LLUUID& outfit_id) 1040{ 1041 LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id); 1042 if (!cat) 1043 { 1044 return; 1045 } 1046 1047 LLSD args; 1048 args["NAME"] = cat->getName(); 1049 1050 LLSD payload; 1051 payload["cat_id"] = outfit_id; 1052 1053 LLNotificationsUtil::add("RenameOutfit", args, payload, boost::bind(onOutfitRename, _1, _2)); 1054} 1055 1056// User typed new outfit name. 1057// static 1058void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& response) 1059{ 1060 S32 option = LLNotificationsUtil::getSelectedOption(notification, response); 1061 if (option != 0) return; // canceled 1062 1063 std::string outfit_name = response["new_name"].asString(); 1064 LLStringUtil::trim(outfit_name); 1065 if (!outfit_name.empty()) 1066 { 1067 LLUUID cat_id = notification["payload"]["cat_id"].asUUID(); 1068 rename_category(&gInventory, cat_id, outfit_name); 1069 } 1070} 1071 1072void LLAppearanceMgr::setOutfitLocked(bool locked) 1073{ 1074 if (mOutfitLocked == locked) 1075 { 1076 return; 1077 } 1078 1079 mOutfitLocked = locked; 1080 if (locked) 1081 { 1082 mUnlockOutfitTimer->reset(); 1083 mUnlockOutfitTimer->start(); 1084 } 1085 else 1086 { 1087 mUnlockOutfitTimer->stop(); 1088 } 1089 1090 LLOutfitObserver::instance().notifyOutfitLockChanged(); 1091} 1092 1093void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id) 1094{ 1095 LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); 1096 wearInventoryCategory(cat, false, true); 1097} 1098 1099void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id) 1100{ 1101 LLInventoryModel::cat_array_t cats; 1102 LLInventoryModel::item_array_t items; 1103 LLFindWearablesEx collector(/*is_worn=*/ true, /*include_body_parts=*/ false); 1104 1105 gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector); 1106 1107 LLInventoryModel::item_array_t::const_iterator it = items.begin(); 1108 const LLInventoryModel::item_array_t::const_iterator it_end = items.end(); 1109 for( ; it_end != it; ++it) 1110 { 1111 LLViewerInventoryItem* item = *it; 1112 removeItemFromAvatar(item->getUUID()); 1113 } 1114} 1115 1116// Create a copy of src_id + contents as a subfolder of dst_id. 1117void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id, 1118 LLPointer<LLInventoryCallback> cb) 1119{ 1120 LLInventoryCategory *src_cat = gInventory.getCategory(src_id); 1121 if (!src_cat) 1122 { 1123 llwarns << "folder not found for src " << src_id.asString() << llendl; 1124 return; 1125 } 1126 llinfos << "starting, src_id " << src_id << " name " << src_cat->getName() << " dst_id " << dst_id << llendl; 1127 LLUUID parent_id = dst_id; 1128 if(parent_id.isNull()) 1129 { 1130 parent_id = gInventory.getRootFolderID(); 1131 } 1132 LLUUID subfolder_id = gInventory.createNewCategory( parent_id, 1133 LLFolderType::FT_NONE, 1134 src_cat->getName()); 1135 shallowCopyCategoryContents(src_id, subfolder_id, cb); 1136 1137 gInventory.notifyObservers(); 1138} 1139 1140// Copy contents of src_id to dst_id. 1141void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, 1142 LLPointer<LLInventoryCallback> cb) 1143{ 1144 LLInventoryModel::cat_array_t* cats; 1145 LLInventoryModel::item_array_t* items; 1146 gInventory.getDirectDescendentsOf(src_id, cats, items); 1147 llinfos << "copying " << items->count() << " items" << llendl; 1148 for (LLInventoryModel::item_array_t::const_iterator iter = items->begin(); 1149 iter != items->end(); 1150 ++iter) 1151 { 1152 const LLViewerInventoryItem* item = (*iter); 1153 switch (item->getActualType()) 1154 { 1155 case LLAssetType::AT_LINK: 1156 { 1157 //LLInventoryItem::getDescription() is used for a new description 1158 //to propagate ordering information saved in descriptions of links 1159 link_inventory_item(gAgent.getID(), 1160 item->getLinkedUUID(), 1161 dst_id, 1162 item->getName(), 1163 item->LLInventoryItem::getDescription(), 1164 LLAssetType::AT_LINK, cb); 1165 break; 1166 } 1167 case LLAssetType::AT_LINK_FOLDER: 1168 { 1169 LLViewerInventoryCategory *catp = item->getLinkedCategory(); 1170 // Skip copying outfit links. 1171 if (catp && catp->getPreferredType() != LLFolderType::FT_OUTFIT) 1172 { 1173 link_inventory_item(gAgent.getID(), 1174 item->getLinkedUUID(), 1175 dst_id, 1176 item->getName(), 1177 item->getDescription(), 1178 LLAssetType::AT_LINK_FOLDER, cb); 1179 } 1180 break; 1181 } 1182 case LLAssetType::AT_CLOTHING: 1183 case LLAssetType::AT_OBJECT: 1184 case LLAssetType::AT_BODYPART: 1185 case LLAssetType::AT_GESTURE: 1186 { 1187 llinfos << "copying inventory item " << item->getName() << llendl; 1188 copy_inventory_item(gAgent.getID(), 1189 item->getPermissions().getOwner(), 1190 item->getUUID(), 1191 dst_id, 1192 item->getName(), 1193 cb); 1194 break; 1195 } 1196 default: 1197 // Ignore non-outfit asset types 1198 break; 1199 } 1200 } 1201} 1202 1203BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id) 1204{ 1205 // These are the wearable items that are required for considering this 1206 // folder as containing a complete outfit. 1207 U32 required_wearables = 0; 1208 required_wearables |= 1LL << LLWearableType::WT_SHAPE; 1209 required_wearables |= 1LL << LLWearableType::WT_SKIN; 1210 required_wearables |= 1LL << LLWearableType::WT_HAIR; 1211 required_wearables |= 1LL << LLWearableType::WT_EYES; 1212 1213 // These are the wearables that the folder actually contains. 1214 U32 folder_wearables = 0; 1215 LLInventoryModel::cat_array_t* cats; 1216 LLInventoryModel::item_array_t* items; 1217 gInventory.getDirectDescendentsOf(folder_id, cats, items); 1218 for (LLInventoryModel::item_array_t::const_iterator iter = items->begin(); 1219 iter != items->end(); 1220 ++iter) 1221 { 1222 const LLViewerInventoryItem* item = (*iter); 1223 if (item->isWearableType()) 1224 { 1225 const LLWearableType::EType wearable_type = item->getWearableType(); 1226 folder_wearables |= 1LL << wearable_type; 1227 } 1228 } 1229 1230 // If the folder contains the required wearables, return TRUE. 1231 return ((required_wearables & folder_wearables) == required_wearables); 1232} 1233 1234bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id) 1235{ 1236 // Disallow removing the base outfit. 1237 if (outfit_cat_id == getBaseOutfitUUID()) 1238 { 1239 return false; 1240 } 1241 1242 // Check if the outfit folder itself is removable. 1243 if (!get_is_category_removable(&gInventory, outfit_cat_id)) 1244 { 1245 return false; 1246 } 1247 1248 // Check for the folder's non-removable descendants. 1249 LLFindNonRemovableObjects filter_non_removable; 1250 LLInventoryModel::cat_array_t cats; 1251 LLInventoryModel::item_array_t items; 1252 LLInventoryModel::item_array_t::const_iterator it; 1253 gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_non_removable); 1254 if (!cats.empty() || !items.empty()) 1255 { 1256 return false; 1257 } 1258 1259 return true; 1260} 1261 1262// static 1263bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id) 1264{ 1265 LLInventoryModel::cat_array_t cats; 1266 LLInventoryModel::item_array_t items; 1267 LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false); 1268 gInventory.collectDescendentsIf(outfit_cat_id, 1269 cats, 1270 items, 1271 LLInventoryModel::EXCLUDE_TRASH, 1272 is_worn); 1273 return items.size() > 0; 1274} 1275 1276// static 1277bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id) 1278{ 1279 if (gAgentWearables.isCOFChangeInProgress()) 1280 { 1281 return false; 1282 } 1283 1284 LLInventoryModel::cat_array_t cats; 1285 LLInventoryModel::item_array_t items; 1286 LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); 1287 gInventory.collectDescendentsIf(outfit_cat_id, 1288 cats, 1289 items, 1290 LLInventoryModel::EXCLUDE_TRASH, 1291 not_worn); 1292 return items.size() > 0; 1293} 1294 1295bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id) 1296{ 1297 // Don't allow wearing anything while we're changing appearance. 1298 if (gAgentWearables.isCOFChangeInProgress()) 1299 { 1300 return false; 1301 } 1302 1303 // Check whether it's the base outfit. 1304 if (outfit_cat_id.isNull() || outfit_cat_id == getBaseOutfitUUID()) 1305 { 1306 return false; 1307 } 1308 1309 // Check whether the outfit contains any wearables we aren't wearing already (STORM-702). 1310 LLInventoryModel::cat_array_t cats; 1311 LLInventoryModel::item_array_t items; 1312 LLFindWearablesEx is_worn(/*is_worn=*/ false, /*include_body_parts=*/ true); 1313 gInventory.collectDescendentsIf(outfit_cat_id, 1314 cats, 1315 items, 1316 LLInventoryModel::EXCLUDE_TRASH, 1317 is_worn); 1318 return items.size() > 0; 1319} 1320 1321void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category) 1322{ 1323 LLInventoryModel::cat_array_t cats; 1324 LLInventoryModel::item_array_t items; 1325 gInventory.collectDescendents(category, cats, items, 1326 LLInventoryModel::EXCLUDE_TRASH); 1327 for (S32 i = 0; i < items.count(); ++i) 1328 { 1329 LLViewerInventoryItem *item = items.get(i); 1330 if (item->getActualType() != LLAssetType::AT_LINK_FOLDER) 1331 continue; 1332 if (item->getIsLinkType()) 1333 { 1334 LLViewerInventoryCategory* catp = item->getLinkedCategory(); 1335 if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) 1336 { 1337 gInventory.purgeObject(item->getUUID()); 1338 } 1339 } 1340 } 1341} 1342 1343void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_links) 1344{ 1345 LLInventoryModel::cat_array_t cats; 1346 LLInventoryModel::item_array_t items; 1347 gInventory.collectDescendents(category, cats, items, 1348 LLInventoryModel::EXCLUDE_TRASH); 1349 for (S32 i = 0; i < items.count(); ++i) 1350 { 1351 LLViewerInventoryItem *item = items.get(i); 1352 if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER)) 1353 continue; 1354 if (item->getIsLinkType()) 1355 { 1356 gInventory.purgeObject(item->getUUID()); 1357 } 1358 } 1359} 1360 1361// Keep the last N wearables of each type. For viewer 2.0, N is 1 for 1362// both body parts and clothing items. 1363void LLAppearanceMgr::filterWearableItems( 1364 LLInventoryModel::item_array_t& items, S32 max_per_type) 1365{ 1366 // Divvy items into arrays by wearable type. 1367 std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT); 1368 divvyWearablesByType(items, items_by_type); 1369 1370 // rebuild items list, retaining the last max_per_type of each array 1371 items.clear(); 1372 for (S32 i=0; i<LLWearableType::WT_COUNT; i++) 1373 { 1374 S32 size = items_by_type[i].size(); 1375 if (size <= 0) 1376 continue; 1377 S32 start_index = llmax(0,size-max_per_type); 1378 for (S32 j = start_index; j<size; j++) 1379 { 1380 items.push_back(items_by_type[i][j]); 1381 } 1382 } 1383} 1384 1385// Create links to all listed items. 1386void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid, 1387 LLInventoryModel::item_array_t& items, 1388 LLPointer<LLInventoryCallback> cb) 1389{ 1390 for (S32 i=0; i<items.count(); i++) 1391 { 1392 const LLInventoryItem* item = items.get(i).get(); 1393 link_inventory_item(gAgent.getID(), 1394 item->getLinkedUUID(), 1395 cat_uuid, 1396 item->getName(), 1397 item->LLInventoryItem::getDescription(), 1398 LLAssetType::AT_LINK, 1399 cb); 1400 1401 const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); 1402 const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND"; 1403#ifndef LL_RELEASE_FOR_DOWNLOAD 1404 llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl; 1405#endif 1406 } 1407} 1408 1409void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) 1410{ 1411 LLViewerInventoryCategory *pcat = gInventory.getCategory(category); 1412 llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl; 1413 1414 const LLUUID cof = getCOF(); 1415 1416 // Deactivate currently active gestures in the COF, if replacing outfit 1417 if (!append) 1418 { 1419 LLInventoryModel::item_array_t gest_items; 1420 getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false); 1421 for(S32 i = 0; i < gest_items.count(); ++i) 1422 { 1423 LLViewerInventoryItem *gest_item = gest_items.get(i); 1424 if ( LLGestureMgr::instance().isGestureActive( gest_item->getLinkedUUID()) ) 1425 { 1426 LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() ); 1427 } 1428 } 1429 } 1430 1431 // Collect and filter descendents to determine new COF contents. 1432 1433 // - Body parts: always include COF contents as a fallback in case any 1434 // required parts are missing. 1435 // Preserve body parts from COF if appending. 1436 LLInventoryModel::item_array_t body_items; 1437 getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false); 1438 getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false); 1439 if (append) 1440 reverse(body_items.begin(), body_items.end()); 1441 // Reduce body items to max of one per type. 1442 removeDuplicateItems(body_items); 1443 filterWearableItems(body_items, 1); 1444 1445 // - Wearables: include COF contents only if appending. 1446 LLInventoryModel::item_array_t wear_items; 1447 if (append) 1448 getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false); 1449 getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false); 1450 // Reduce wearables to max of one per type. 1451 removeDuplicateItems(wear_items); 1452 filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE); 1453 1454 // - Attachments: include COF contents only if appending. 1455 LLInventoryModel::item_array_t obj_items; 1456 if (append) 1457 getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false); 1458 getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false); 1459 removeDuplicateItems(obj_items); 1460 1461 // - Gestures: include COF contents only if appending. 1462 LLInventoryModel::item_array_t gest_items; 1463 if (append) 1464 getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false); 1465 getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE, false); 1466 removeDuplicateItems(gest_items); 1467 1468 // Remove current COF contents. 1469 bool keep_outfit_links = append; 1470 purgeCategory(cof, keep_outfit_links); 1471 gInventory.notifyObservers(); 1472 1473 // Create links to new COF contents. 1474 llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl; 1475 LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(!append); 1476 1477#ifndef LL_RELEASE_FOR_DOWNLOAD 1478 llinfos << "Linking body items" << llendl; 1479#endif 1480 linkAll(cof, body_items, link_waiter); 1481 1482#ifndef LL_RELEASE_FOR_DOWNLOAD 1483 llinfos << "Linking wear items" << llendl; 1484#endif 1485 linkAll(cof, wear_items, link_waiter); 1486 1487#ifndef LL_RELEASE_FOR_DOWNLOAD 1488 llinfos << "Linking obj items" << llendl; 1489#endif 1490 linkAll(cof, obj_items, link_waiter); 1491 1492#ifndef LL_RELEASE_FOR_DOWNLOAD 1493 llinfos << "Linking gesture items" << llendl; 1494#endif 1495 linkAll(cof, gest_items, link_waiter); 1496 1497 // Add link to outfit if category is an outfit. 1498 if (!append) 1499 { 1500 createBaseOutfitLink(category, link_waiter); 1501 } 1502 llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl; 1503} 1504 1505void LLAppearanceMgr::updatePanelOutfitName(const std::string& name) 1506{ 1507 LLSidepanelAppearance* panel_appearance = 1508 dynamic_cast<LLSidepanelAppearance *>(LLFloaterSidePanelContainer::getPanel("appearance")); 1509 if (panel_appearance) 1510 { 1511 panel_appearance->refreshCurrentOutfitName(name); 1512 } 1513} 1514 1515void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> link_waiter) 1516{ 1517 const LLUUID cof = getCOF(); 1518 LLViewerInventoryCategory* catp = gInventory.getCategory(category); 1519 std::string new_outfit_name = ""; 1520 1521 purgeBaseOutfitLink(cof); 1522 1523 if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT) 1524 { 1525 link_inventory_item(gAgent.getID(), category, cof, catp->getName(), "", 1526 LLAssetType::AT_LINK_FOLDER, link_waiter); 1527 new_outfit_name = catp->getName(); 1528 } 1529 1530 updatePanelOutfitName(new_outfit_name); 1531} 1532 1533void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, bool append) 1534{ 1535 lldebugs << "updateAgentWearables()" << llendl; 1536 LLInventoryItem::item_array_t items; 1537 LLDynamicArray< LLWearable* > wearables; 1538 1539 // For each wearable type, find the wearables of that type. 1540 for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ ) 1541 { 1542 for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->getFoundList().begin(); 1543 iter != holder->getFoundList().end(); ++iter) 1544 { 1545 LLFoundData& data = *iter; 1546 LLWearable* wearable = data.mWearable; 1547 if( wearable && ((S32)wearable->getType() == i) ) 1548 { 1549 LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(data.mItemID); 1550 if( item && (item->getAssetUUID() == wearable->getAssetID()) ) 1551 { 1552 items.put(item); 1553 wearables.put(wearable); 1554 } 1555 } 1556 } 1557 } 1558 1559 if(wearables.count() > 0) 1560 { 1561 gAgentWearables.setWearableOutfit(items, wearables, !append); 1562 } 1563 1564// dec_busy_count(); 1565} 1566 1567static void remove_non_link_items(LLInventoryModel::item_array_t &items) 1568{ 1569 LLInventoryModel::item_array_t pruned_items; 1570 for (LLInventoryModel::item_array_t::const_iterator iter = items.begin(); 1571 iter != items.end(); 1572 ++iter) 1573 { 1574 const LLViewerInventoryItem *item = (*iter); 1575 if (item && item->getIsLinkType()) 1576 { 1577 pruned_items.push_back((*iter)); 1578 } 1579 } 1580 items = pruned_items; 1581} 1582 1583//a predicate for sorting inventory items by actual descriptions 1584bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* item2) 1585{ 1586 if (!item1 || !item2) 1587 { 1588 llwarning("either item1 or item2 is NULL", 0); 1589 return true; 1590 } 1591 1592 return item1->LLInventoryItem::getDescription() < item2->LLInventoryItem::getDescription(); 1593} 1594 1595void item_array_diff(LLInventoryModel::item_array_t& full_list, 1596 LLInventoryModel::item_array_t& keep_list, 1597 LLInventoryModel::item_array_t& kill_list) 1598 1599{ 1600 for (LLInventoryModel::item_array_t::iterator it = full_list.begin(); 1601 it != full_list.end(); 1602 ++it) 1603 { 1604 LLViewerInventoryItem *item = *it; 1605 if (keep_list.find(item) < 0) // Why on earth does LLDynamicArray need to redefine find()? 1606 { 1607 kill_list.push_back(item); 1608 } 1609 } 1610} 1611 1612S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id, 1613 LLAssetType::EType type, 1614 S32 max_items, 1615 LLInventoryModel::item_array_t& items_to_kill) 1616{ 1617 S32 to_kill_count = 0; 1618 1619 LLInventoryModel::item_array_t items; 1620 getDescendentsOfAssetType(cat_id, items, type, false); 1621 LLInventoryModel::item_array_t curr_items = items; 1622 removeDuplicateItems(items); 1623 if (max_items > 0) 1624 { 1625 filterWearableItems(items, max_items); 1626 } 1627 LLInventoryModel::item_array_t kill_items; 1628 item_array_diff(curr_items,items,kill_items); 1629 for (LLInventoryModel::item_array_t::iterator it = kill_items.begin(); 1630 it != kill_items.end(); 1631 ++it) 1632 { 1633 items_to_kill.push_back(*it); 1634 to_kill_count++; 1635 } 1636 return to_kill_count; 1637} 1638 1639 1640void LLAppearanceMgr::enforceItemRestrictions() 1641{ 1642 S32 purge_count = 0; 1643 LLInventoryModel::item_array_t items_to_kill; 1644 1645 purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART, 1646 1, items_to_kill); 1647 purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING, 1648 LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill); 1649 purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT, 1650 -1, items_to_kill); 1651 1652 if (items_to_kill.size()>0) 1653 { 1654 for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin(); 1655 it != items_to_kill.end(); 1656 ++it) 1657 { 1658 LLViewerInventoryItem *item = *it; 1659 llinfos << "purging duplicate or excess item " << item->getName() << llendl; 1660 gInventory.purgeObject(item->getUUID()); 1661 } 1662 gInventory.notifyObservers(); 1663 } 1664} 1665 1666void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) 1667{ 1668 if (mIsInUpdateAppearanceFromCOF) 1669 { 1670 llwarns << "Called updateAppearanceFromCOF inside updateAppearanceFromCOF, skipping" << llendl; 1671 return; 1672 } 1673 1674 BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF); 1675 1676 llinfos << "starting" << llendl; 1677 1678 //checking integrity of the COF in terms of ordering of wearables, 1679 //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state) 1680 updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering); 1681 1682 // Remove duplicate or excess wearables. Should normally be enforced at the UI level, but 1683 // this should catch anything that gets through. 1684 enforceItemRestrictions(); 1685 1686 // update dirty flag to see if the state of the COF matches 1687 // the saved outfit stored as a folder link 1688 updateIsDirty(); 1689 1690 //dumpCat(getCOF(),"COF, start"); 1691 1692 bool follow_folder_links …
Large files files are truncated, but you can click here to view the full file