diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 84d3bfd5d8..e46569c606 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -44,6 +44,7 @@ static const std::string INV_ITEM_ID_LABEL("item_id"); static const std::string INV_FOLDER_ID_LABEL("cat_id"); static const std::string INV_PARENT_ID_LABEL("parent_id"); +static const std::string INV_THUMBNAIL_LABEL("thumbnail"); static const std::string INV_THUMBNAIL_ID_LABEL("thumbnail_id"); static const std::string INV_ASSET_TYPE_LABEL("type"); static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type"); @@ -748,6 +749,26 @@ BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream) { mType = LLAssetType::lookup(valuestr); } + else if (0 == strcmp("metadata", keyword)) + { + LLSD metadata(valuestr); + if (metadata.has("thumbnail")) + { + const LLSD& thumbnail = metadata["thumbnail"]; + if (thumbnail.has("asset_id")) + { + setThumbnailUUID(thumbnail["asset_id"].asUUID()); + } + else + { + setThumbnailUUID(LLUUID::null); + } + } + else + { + setThumbnailUUID(LLUUID::null); + } + } else if(0 == strcmp("inv_type", keyword)) { mInventoryType = LLInventoryType::lookup(std::string(valuestr)); @@ -837,6 +858,13 @@ BOOL LLInventoryItem::exportLegacyStream(std::ostream& output_stream, BOOL inclu output_stream << "\t\tparent_id\t" << uuid_str << "\n"; mPermissions.exportLegacyStream(output_stream); + if (mThumbnailUUID.notNull()) + { + LLSD metadata; + metadata["thumbnail"] = LLSD().with("asset_id", mThumbnailUUID); + output_stream << "\t\tmetadata\t" << metadata << "|\n"; + } + // Check for permissions to see the asset id, and if so write it // out as an asset id. Otherwise, apply our cheesy encryption. if(include_asset_key) @@ -890,6 +918,11 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const sd[INV_PARENT_ID_LABEL] = mParentUUID; sd[INV_PERMISSIONS_LABEL] = ll_create_sd_from_permissions(mPermissions); + if (mThumbnailUUID.notNull()) + { + sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); + } + U32 mask = mPermissions.getMaskBase(); if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) || (mAssetUUID.isNull())) @@ -941,6 +974,35 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) { mParentUUID = sd[w]; } + mThumbnailUUID.setNull(); + w = INV_THUMBNAIL_LABEL; + if (sd.has(w)) + { + const LLSD &thumbnail_map = sd[w]; + w = INV_ASSET_ID_LABEL; + if (thumbnail_map.has(w)) + { + mThumbnailUUID = thumbnail_map[w]; + } + /* Example: + asset_id + acc0ec86 - 17f2 - 4b92 - ab41 - 6718b1f755f7 + perms + 8 + service + 3 + version + 1 + */ + } + else + { + w = INV_THUMBNAIL_ID_LABEL; + if (sd.has(w)) + { + mThumbnailUUID = sd[w].asUUID(); + } + } w = INV_PERMISSIONS_LABEL; if (sd.has(w)) { @@ -1116,11 +1178,15 @@ LLSD LLInventoryCategory::asLLSD() const LLSD sd = LLSD(); sd["item_id"] = mUUID; sd["parent_id"] = mParentUUID; - sd["thumbnail_id"] = mThumbnailUUID; S8 type = static_cast(mPreferredType); sd["type"] = type; sd["name"] = mName; + if (mThumbnailUUID.notNull()) + { + sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); + } + return sd; } @@ -1149,10 +1215,24 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd) { mParentUUID = sd[w]; } - w = INV_THUMBNAIL_ID_LABEL; + mThumbnailUUID.setNull(); + w = INV_THUMBNAIL_LABEL; if (sd.has(w)) { - mThumbnailUUID = sd[w]; + const LLSD &thumbnail_map = sd[w]; + w = INV_ASSET_ID_LABEL; + if (thumbnail_map.has(w)) + { + mThumbnailUUID = thumbnail_map[w]; + } + } + else + { + w = INV_THUMBNAIL_ID_LABEL; + if (sd.has(w)) + { + mThumbnailUUID = sd[w]; + } } w = INV_ASSET_TYPE_LABEL; if (sd.has(w)) @@ -1245,6 +1325,26 @@ BOOL LLInventoryCategory::importLegacyStream(std::istream& input_stream) LLStringUtil::replaceNonstandardASCII(mName, ' '); LLStringUtil::replaceChar(mName, '|', ' '); } + else if (0 == strcmp("metadata", keyword)) + { + LLSD metadata(valuestr); + if (metadata.has("thumbnail")) + { + const LLSD& thumbnail = metadata["thumbnail"]; + if (thumbnail.has("asset_id")) + { + setThumbnailUUID(thumbnail["asset_id"].asUUID()); + } + else + { + setThumbnailUUID(LLUUID::null); + } + } + else + { + setThumbnailUUID(LLUUID::null); + } + } else { LL_WARNS() << "unknown keyword '" << keyword @@ -1265,6 +1365,12 @@ BOOL LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, BOOL) output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n"; output_stream << "\t\tpref_type\t" << LLFolderType::lookup(mPreferredType) << "\n"; output_stream << "\t\tname\t" << mName.c_str() << "|\n"; + if (mThumbnailUUID.notNull()) + { + LLSD metadata; + metadata["thumbnail"] = LLSD().with("asset_id", mThumbnailUUID); + output_stream << "\t\tmetadata\t" << metadata << "|\n"; + } output_stream << "\t}\n"; return TRUE; } @@ -1278,6 +1384,11 @@ LLSD LLInventoryCategory::exportLLSD() const cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType); cat_data[INV_NAME_LABEL] = mName; + if (mThumbnailUUID.notNull()) + { + cat_data[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); + } + return cat_data; } @@ -1299,6 +1410,16 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data) { setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString())); } + LLUUID thumbnail_uuid; + if (cat_data.has(INV_THUMBNAIL_LABEL)) + { + const LLSD &thumbnail_data = cat_data[INV_THUMBNAIL_LABEL]; + if (thumbnail_data.has(INV_ASSET_ID_LABEL)) + { + thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID(); + } + } + setThumbnailUUID(thumbnail_uuid); if (cat_data.has(INV_NAME_LABEL)) { mName = cat_data[INV_NAME_LABEL].asString(); diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 1898e20c1b..037ee0aa19 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -118,6 +118,7 @@ LLFolderViewItem::Params::Params() text_pad("text_pad", 0), text_pad_right("text_pad_right", 0), single_folder_mode("single_folder_mode", false), + double_click_override("double_click_override", false), arrow_size("arrow_size", 0), max_folder_item_overlap("max_folder_item_overlap", 0), // Inventory specials @@ -161,9 +162,10 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mArrowSize(p.arrow_size), mSingleFolderMode(p.single_folder_mode), mMaxFolderItemOverlap(p.max_folder_item_overlap), - // Inventory specials - mForInventory(p.for_inventory), - mItemTopPad(p.item_top_pad) + mDoubleClickOverride(p.double_click_override), + // Inventory specials + mForInventory(p.for_inventory), + mItemTopPad(p.item_top_pad) { if (!sColorSetInitialized) { @@ -2192,16 +2194,19 @@ BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) } if( !handled ) { - static LLUICachedControl double_click_action("MultiModeDoubleClickFolder", false); - if (double_click_action == 1) + if(mDoubleClickOverride) { - getViewModelItem()->navigateToFolder(true); - return TRUE; - } - if (double_click_action == 2) - { - getViewModelItem()->navigateToFolder(false, true); - return TRUE; + static LLUICachedControl double_click_action("MultiModeDoubleClickFolder", false); + if (double_click_action == 1) + { + getViewModelItem()->navigateToFolder(true); + return TRUE; + } + if (double_click_action == 2) + { + getViewModelItem()->navigateToFolder(false, true); + return TRUE; + } } if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) { diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 2cd1f7c5f2..58978cd899 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -72,7 +72,8 @@ public: text_pad_right, arrow_size, max_folder_item_overlap; - Optional single_folder_mode; + Optional single_folder_mode, + double_click_override; // Inventory specials Optional for_inventory; @@ -127,6 +128,7 @@ protected: mAllowWear, mAllowDrop, mSingleFolderMode, + mDoubleClickOverride, mSelectPending, mIsItemCut; diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 8c12a33f12..5d012dad83 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -539,6 +539,15 @@ void LLUICtrl::setControlVariable(LLControlVariable* control) } } +void LLUICtrl::removeControlVariable() +{ + if (mControlVariable) + { + mControlConnection.disconnect(); + mControlVariable = NULL; + } +} + //virtual void LLUICtrl::setControlName(const std::string& control_name, LLView *context) { diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 847e75a1aa..cc8659834b 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -183,6 +183,7 @@ public: bool setControlValue(const LLSD& value); void setControlVariable(LLControlVariable* control); virtual void setControlName(const std::string& control, LLView *context = NULL); + void removeControlVariable(); LLControlVariable* getControlVariable() { return mControlVariable; } // Accessors for other ControlVariables diff --git a/indra/newview/ao.cpp b/indra/newview/ao.cpp index f7a8f3f7e3..4f5a32fece 100644 --- a/indra/newview/ao.cpp +++ b/indra/newview/ao.cpp @@ -534,11 +534,10 @@ bool FloaterAO::newSetCallback(const LLSD& notification, const LLSD& response) if (option == 0) { - if (AOEngine::instance().addSet(newSetName).notNull()) + return AOEngine::instance().addSet(newSetName, [this](const LLUUID& new_cat_id) { reloading(true); - return true; - } + }); } return false; } diff --git a/indra/newview/aoengine.cpp b/indra/newview/aoengine.cpp index e649c2a879..a726483d6a 100644 --- a/indra/newview/aoengine.cpp +++ b/indra/newview/aoengine.cpp @@ -40,7 +40,6 @@ #include "llnotificationsutil.h" #include "llstring.h" #include "llviewercontrol.h" -#include "llviewerinventory.h" #define ROOT_AO_FOLDER "#AO" #include @@ -978,26 +977,30 @@ void AOEngine::updateSortOrder(AOSet::AOState* state) } } -LLUUID AOEngine::addSet(const std::string& name, bool reload) +bool AOEngine::addSet(const std::string& name, inventory_func_type callback, bool reload) { if (mAOFolder.isNull()) { LL_WARNS("AOEngine") << ROOT_AO_FOLDER << " folder not there yet. Requesting recreation." << LL_ENDL; tick(); - return LLUUID::null; + return false; } BOOL wasProtected = gSavedPerAccountSettings.getBOOL("LockAOFolders"); gSavedPerAccountSettings.setBOOL("LockAOFolders", FALSE); LL_DEBUGS("AOEngine") << "adding set folder " << name << LL_ENDL; - LLUUID newUUID = gInventory.createNewCategory(mAOFolder, LLFolderType::FT_NONE, name); - gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected); - - if (reload) + gInventory.createNewCategory(mAOFolder, LLFolderType::FT_NONE, name, [callback, wasProtected, reload, this](const LLUUID &new_cat_id) { - mTimerCollection.enableReloadTimer(true); - } - return newUUID; + gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected); + + if (reload) + { + mTimerCollection.enableReloadTimer(true); + } + + callback(new_cat_id); + }); + return true; } bool AOEngine::createAnimationLink(const AOSet* set, AOSet::AOState* state, const LLInventoryItem* item) @@ -2177,8 +2180,51 @@ void AOEngine::processImport(bool from_timer) { if (mImportCategory.isNull()) { - mImportCategory = addSet(mImportSet->getName(), false); - if (mImportCategory.isNull()) + bool success = addSet(mImportSet->getName(), [this, from_timer](const LLUUID& new_cat_id) + { + mImportCategory = new_cat_id; + mImportSet->setInventoryUUID(mImportCategory); + + bool allComplete = true; + for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index) + { + AOSet::AOState* state = mImportSet->getState(index); + if (state->mAnimations.size()) + { + allComplete = false; + LL_DEBUGS("AOEngine") << "state " << state->mName << " still has animations to link." << LL_ENDL; + + for (S32 animationIndex = state->mAnimations.size() - 1; animationIndex >= 0; --animationIndex) + { + LL_DEBUGS("AOEngine") << "linking animation " << state->mAnimations[animationIndex].mName << LL_ENDL; + if (createAnimationLink(mImportSet, state, gInventory.getItem(state->mAnimations[animationIndex].mInventoryUUID))) + { + LL_DEBUGS("AOEngine") << "link success, size " << state->mAnimations.size() << ", removing animation " + << (*(state->mAnimations.begin() + animationIndex)).mName << " from import state" << LL_ENDL; + state->mAnimations.erase(state->mAnimations.begin() + animationIndex); + LL_DEBUGS("AOEngine") << "deleted, size now: " << state->mAnimations.size() << LL_ENDL; + } + else + { + LLSD args; + args["NAME"] = state->mAnimations[animationIndex].mName; + LLNotificationsUtil::add("AOImportLinkFailed", args); + } + } + } + } + + if (allComplete) + { + mTimerCollection.enableImportTimer(false); + mOldImportSets.push_back(mImportSet); // FIRE-3801; Cannot delete here, or LLInstanceTracker gets upset. Just remember and delete mOldImportSets once we can. + mImportSet = nullptr; + mImportCategory.setNull(); + reload(from_timer); + } + }, false); + + if (!success) { mImportRetryCount++; if (mImportRetryCount == 5) @@ -2199,47 +2245,7 @@ void AOEngine::processImport(bool from_timer) args["NAME"] = mImportSet->getName(); LLNotificationsUtil::add("AOImportRetryCreateSet", args); } - return; } - mImportSet->setInventoryUUID(mImportCategory); - } - - bool allComplete = true; - for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index) - { - AOSet::AOState* state = mImportSet->getState(index); - if (state->mAnimations.size()) - { - allComplete = false; - LL_DEBUGS("AOEngine") << "state " << state->mName << " still has animations to link." << LL_ENDL; - - for (S32 animationIndex = state->mAnimations.size() - 1; animationIndex >= 0; --animationIndex) - { - LL_DEBUGS("AOEngine") << "linking animation " << state->mAnimations[animationIndex].mName << LL_ENDL; - if (createAnimationLink(mImportSet, state, gInventory.getItem(state->mAnimations[animationIndex].mInventoryUUID))) - { - LL_DEBUGS("AOEngine") << "link success, size "<< state->mAnimations.size() << ", removing animation " - << (*(state->mAnimations.begin() + animationIndex)).mName << " from import state" << LL_ENDL; - state->mAnimations.erase(state->mAnimations.begin() + animationIndex); - LL_DEBUGS("AOEngine") << "deleted, size now: " << state->mAnimations.size() << LL_ENDL; - } - else - { - LLSD args; - args["NAME"] = state->mAnimations[animationIndex].mName; - LLNotificationsUtil::add("AOImportLinkFailed", args); - } - } - } - } - - if (allComplete) - { - mTimerCollection.enableImportTimer(false); - mOldImportSets.push_back(mImportSet); // FIRE-3801; Cannot delete here, or LLInstanceTracker gets upset. Just remember and delete mOldImportSets once we can. - mImportSet = nullptr; - mImportCategory.setNull(); - reload(from_timer); } } diff --git a/indra/newview/aoengine.h b/indra/newview/aoengine.h index a9b5c46686..062786b689 100644 --- a/indra/newview/aoengine.h +++ b/indra/newview/aoengine.h @@ -31,6 +31,7 @@ #include "lleventtimer.h" #include "llextendedstatus.h" #include "llsingleton.h" +#include "llviewerinventory.h" #include class AOTimerCollection @@ -105,7 +106,7 @@ class AOEngine const LLUUID& getAOFolder() const; - LLUUID addSet(const std::string& name, bool reload = true); + bool addSet(const std::string& name, inventory_func_type callback, bool reload = true); bool removeSet(AOSet* set); bool addAnimation(const AOSet* set, AOSet::AOState* state, const LLInventoryItem* item, bool reload = true); diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4bcee5a761..d023b7c84d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7226,17 +7226,6 @@ Boolean Value 0 - - InventoryInboxToggleState - - Comment - Stores the open/closed state of inventory Received items panel - Persist - 1 - Type - Boolean - Value - 0 InventoryLinking diff --git a/indra/newview/fsfloaterwearablefavorites.cpp b/indra/newview/fsfloaterwearablefavorites.cpp index bbb1f50751..cf61e42a6e 100644 --- a/indra/newview/fsfloaterwearablefavorites.cpp +++ b/indra/newview/fsfloaterwearablefavorites.cpp @@ -152,35 +152,42 @@ void FSFloaterWearableFavorites::onOpen(const LLSD& /*info*/) { if (sFolderID.isNull()) { - initCategory(); + initCategory(boost::bind(&FSFloaterWearableFavorites::initialize, this)); } - - LLViewerInventoryCategory* category = gInventory.getCategory(sFolderID); - if (!category) + else { - return; + initialize(); } - - const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); - LLViewerInventoryCategory* category_cof = gInventory.getCategory(cof); - if (!category_cof) - { - return; - } - - gInventory.addObserver(mCategoriesObserver); - mCategoriesObserver->addCategory(sFolderID, boost::bind(&FSFloaterWearableFavorites::updateList, this, sFolderID)); - mCategoriesObserver->addCategory(cof, boost::bind(&FSFloaterWearableFavorites::updateList, this, sFolderID)); - category->fetch(); - - mItemsList->setSortOrder((LLWearableItemsList::ESortOrder)gSavedSettings.getU32("FSWearableFavoritesSortOrder")); - updateList(sFolderID); - mItemsList->setDADCallback(boost::bind(&FSFloaterWearableFavorites::onItemDAD, this, _1)); - - mInitialized = true; } } +void FSFloaterWearableFavorites::initialize() +{ + LLViewerInventoryCategory* category = gInventory.getCategory(sFolderID); + if (!category) + { + return; + } + + const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + LLViewerInventoryCategory* category_cof = gInventory.getCategory(cof); + if (!category_cof) + { + return; + } + + gInventory.addObserver(mCategoriesObserver); + mCategoriesObserver->addCategory(sFolderID, boost::bind(&FSFloaterWearableFavorites::updateList, this, sFolderID)); + mCategoriesObserver->addCategory(cof, boost::bind(&FSFloaterWearableFavorites::updateList, this, sFolderID)); + category->fetch(); + + mItemsList->setSortOrder((LLWearableItemsList::ESortOrder)gSavedSettings.getU32("FSWearableFavoritesSortOrder")); + updateList(sFolderID); + mItemsList->setDADCallback(boost::bind(&FSFloaterWearableFavorites::onItemDAD, this, _1)); + + mInitialized = true; +} + //virtual void FSFloaterWearableFavorites::draw() { @@ -226,7 +233,7 @@ std::optional FSFloaterWearableFavorites::getWearableFavoritesFolderID() } // static -void FSFloaterWearableFavorites::initCategory() +void FSFloaterWearableFavorites::initCategory(inventory_func_type callback) { if (!gInventory.isInventoryUsable()) { @@ -237,16 +244,30 @@ void FSFloaterWearableFavorites::initCategory() if (auto fs_favs_id = getWearableFavoritesFolderID(); fs_favs_id.has_value()) { sFolderID = fs_favs_id.value(); + callback(sFolderID); } else { LLUUID fs_root_cat_id = gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER); if (fs_root_cat_id.isNull()) { - fs_root_cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER); + gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER, [callback](const LLUUID& new_cat_id) + { + gInventory.createNewCategory(new_cat_id, LLFolderType::FT_NONE, FS_WEARABLE_FAVORITES_FOLDER, [callback](const LLUUID& new_cat_id) + { + FSFloaterWearableFavorites::sFolderID = new_cat_id; + callback(new_cat_id); + }); + }); + } + else + { + gInventory.createNewCategory(fs_root_cat_id, LLFolderType::FT_NONE, FS_WEARABLE_FAVORITES_FOLDER, [callback](const LLUUID& new_cat_id) + { + FSFloaterWearableFavorites::sFolderID = new_cat_id; + callback(new_cat_id); + }); } - - sFolderID = gInventory.createNewCategory(fs_root_cat_id, LLFolderType::FT_NONE, FS_WEARABLE_FAVORITES_FOLDER); } } diff --git a/indra/newview/fsfloaterwearablefavorites.h b/indra/newview/fsfloaterwearablefavorites.h index 8f09524aa4..e0a5e8f733 100644 --- a/indra/newview/fsfloaterwearablefavorites.h +++ b/indra/newview/fsfloaterwearablefavorites.h @@ -29,6 +29,7 @@ #define FS_FLOATERWEARABLEFAVORITES_H #include "llfloater.h" +#include "llviewerinventory.h" #include "llwearableitemslist.h" #include @@ -79,10 +80,14 @@ public: /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ bool hasAccelerators() const { return true; } - static void initCategory(); + static void initCategory(inventory_func_type callback = no_op_inventory_func); static LLUUID getFavoritesFolder(); + static LLUUID sFolderID; + private: + void initialize(); + void updateList(const LLUUID& folder_id); void onItemDAD(const LLUUID& item_id); @@ -106,8 +111,6 @@ private: LLFilterEditor* mFilterEditor; LLMenuButton* mOptionsButton; LLHandle mOptionsMenuHandle; - - static LLUUID sFolderID; }; #endif // FS_FLOATERWEARABLEFAVORITES_H diff --git a/indra/newview/fslslbridge.cpp b/indra/newview/fslslbridge.cpp index 7e169ace50..e57a92469d 100644 --- a/indra/newview/fslslbridge.cpp +++ b/indra/newview/fslslbridge.cpp @@ -89,6 +89,7 @@ private: FSLSLBridge::FSLSLBridge(): mBridgeCreating(false), mpBridge(nullptr), + mBridgeFolderID(LLUUID::null), mIsFirstCallDone(false), mAllowDetach(false), mFinishCreation(false), @@ -176,7 +177,7 @@ bool FSLSLBridge::lslToViewer(std::string_view message, const LLUUID& fromID, co return false; } std::string_view tag = message.substr(0, tagend + 1); - std::string ourBridge = findFSCategory().asString(); + std::string ourBridge = getBridgeFolder().asString(); // FIRE-962 bool bridgeIsEnabled = gSavedSettings.getBOOL("UseLSLBridge"); @@ -213,7 +214,7 @@ bool FSLSLBridge::lslToViewer(std::string_view message, const LLUUID& fromID, co // If something that looks like our current bridge is attached but failed auth, detach and recreate. - const LLUUID catID = findFSCategory(); + const LLUUID catID = getBridgeFolder(); LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID); if (fsBridge && get_is_item_worn(fsBridge->getUUID())) { @@ -251,7 +252,7 @@ bool FSLSLBridge::lslToViewer(std::string_view message, const LLUUID& fromID, co if (!mpBridge) { - LLUUID catID = findFSCategory(); + LLUUID catID = getBridgeFolder(); LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID); mpBridge = fsBridge; } @@ -642,7 +643,7 @@ void FSLSLBridge::recreateBridge() //announce yourself report_to_nearby_chat(LLTrans::getString("fsbridge_creating")); - LLUUID catID = findFSCategory(); + LLUUID catID = getBridgeFolder(); FSLSLBridgeInventoryPreCreationCleanupObserver* bridgeInventoryObserver = new FSLSLBridgeInventoryPreCreationCleanupObserver(catID); bridgeInventoryObserver->startFetch(); @@ -663,7 +664,7 @@ void FSLSLBridge::cleanUpPreCreation() LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; NameCollectFunctor namefunctor(mCurrentFullName); - gInventory.collectDescendentsIf(findFSCategory(), cats, items, FALSE, namefunctor); + gInventory.collectDescendentsIf(getBridgeFolder(), cats, items, FALSE, namefunctor); mAllowedDetachables.clear(); for (const auto& item : items) @@ -699,7 +700,7 @@ void FSLSLBridge::finishCleanUpPreCreation() LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; NameCollectFunctor namefunctor(mCurrentFullName); - gInventory.collectDescendentsIf(findFSCategory(), cats, items, FALSE, namefunctor); + gInventory.collectDescendentsIf(getBridgeFolder(), cats, items, FALSE, namefunctor); for (const auto& item : items) { @@ -743,26 +744,26 @@ void FSLSLBridge::initBridge() return; } - LLUUID catID = findFSCategory(); - LLUUID libCatID = findFSBridgeContainerCategory(); + setupFSCategory([this](const LLUUID& bridge_folder_id) + { + LLUUID libCatID = findFSBridgeContainerCategory(); - //check for inventory load - // AH: Use overloaded LLInventoryFetchDescendentsObserver to check for load of - // bridge and bridge rock category before doing anything! - LL_INFOS("FSLSLBridge") << "initBridge called. gInventory.isInventoryUsable = " << (gInventory.isInventoryUsable() ? "true" : "false") << LL_ENDL; - uuid_vec_t cats; - cats.push_back(catID); - cats.push_back(libCatID); - FSLSLBridgeInventoryObserver* bridgeInventoryObserver = new FSLSLBridgeInventoryObserver(cats); - bridgeInventoryObserver->startFetch(); - if (bridgeInventoryObserver->isFinished()) - { - bridgeInventoryObserver->done(); - } - else - { - gInventory.addObserver(bridgeInventoryObserver); - } + //check for inventory load + // AH: Use overloaded LLInventoryFetchDescendentsObserver to check for load of + // bridge and bridge rock category before doing anything! + LL_INFOS("FSLSLBridge") << "initBridge called. gInventory.isInventoryUsable = " << (gInventory.isInventoryUsable() ? "true" : "false") << LL_ENDL; + uuid_vec_t cats{ bridge_folder_id, libCatID }; + FSLSLBridgeInventoryObserver* bridgeInventoryObserver = new FSLSLBridgeInventoryObserver(cats); + bridgeInventoryObserver->startFetch(); + if (bridgeInventoryObserver->isFinished()) + { + bridgeInventoryObserver->done(); + } + else + { + gInventory.addObserver(bridgeInventoryObserver); + } + }); } @@ -786,7 +787,7 @@ void FSLSLBridge::startCreation() LL_INFOS("FSLSLBridge") << "startCreation called. gInventory.isInventoryUsable = " << (gInventory.isInventoryUsable() ? "true" : "false") << LL_ENDL; //if bridge object doesn't exist - create and attach it, update script. - const LLUUID catID = findFSCategory(); + const LLUUID catID = getBridgeFolder(); LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID); //detach everything else @@ -836,7 +837,7 @@ void FSLSLBridge::startCreation() void FSLSLBridge::createNewBridge() { //check if user has a bridge - const LLUUID catID = findFSCategory(); + const LLUUID catID = getBridgeFolder(); //attach the Linden rock from the library (will resize as soon as attached) const LLUUID libID = gInventory.getLibraryRootFolderID(); @@ -1193,7 +1194,7 @@ void FSLSLBridge::create_script_inner() return; } - const LLUUID catID = findFSCategory(); + const LLUUID catID = getBridgeFolder(); LLPointer cb = new FSLSLBridgeScriptCallback(); create_inventory_item(gAgentID, @@ -1378,7 +1379,7 @@ std::string FSLSLBridgeScriptCallback::prepUploadFile(std::string &aBuffer) LL_WARNS("FSLSLBridge") << "Invalid bridge script" << LL_ENDL; return std::string(); } - aBuffer.replace(pos, bridgekey.length(), FSLSLBridge::getInstance()->findFSCategory().asString()); + aBuffer.replace(pos, bridgekey.length(), FSLSLBridge::getInstance()->getBridgeFolder().asString()); LLFILE *fpOut = LLFile::fopen(fNew, "wt"); if (!fpOut) @@ -1483,18 +1484,9 @@ bool FSLSLBridge::isItemAttached(const LLUUID& iID) return (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(iID)); } -LLUUID FSLSLBridge::findFSCategory() +void FSLSLBridge::setupFSCategory(inventory_func_type callback) { - if (!mBridgeFolderID.isNull()) - { - return mBridgeFolderID; - } - - LLUUID fsCatID; - LLUUID bridgeCatID; - - fsCatID = gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER); - if (!fsCatID.isNull()) + if (LLUUID fsCatID = gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER); !fsCatID.isNull()) { LLInventoryModel::item_array_t* items; LLInventoryModel::cat_array_t* cats; @@ -1505,25 +1497,64 @@ LLUUID FSLSLBridge::findFSCategory() { if (cat->getName() == FS_BRIDGE_FOLDER) { - bridgeCatID = cat->getUUID(); - break; + mBridgeFolderID = cat->getUUID(); + callback(mBridgeFolderID); + return; + } + } + } + + gInventory.createNewCategory(fsCatID, LLFolderType::FT_NONE, FS_BRIDGE_FOLDER, [this, callback](const LLUUID& new_cat_id) + { + mBridgeFolderID = new_cat_id; + callback(mBridgeFolderID); + }); + } + else + { + gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER, [this, callback](const LLUUID& new_cat_id) + { + gInventory.createNewCategory(new_cat_id, LLFolderType::FT_NONE, FS_BRIDGE_FOLDER, [this, callback](const LLUUID& new_cat_id) + { + mBridgeFolderID = new_cat_id; + callback(mBridgeFolderID); + }); + }); + } +} + +// This used to be the place where the bridge folder was also created before it got moved to setupFSCategory. +// We still need this method because it is used in processAttach / processDetach, which (unfortunately) might be called +// before initBridge is called that sets up the bridge folder. But since apparently a bridge is already attached, +// we can assume the folder already exists and we do not need to create it here anymore. And if something is attached +// to where we attach the bridge and also has the same name as the bridge but is in a different folder, it won't make +// any difference if we return the actual category ID or a null UUID for the check performed in processAttach. +LLUUID FSLSLBridge::findFSCategory() +{ + if (!mBridgeFolderID.isNull()) + { + return mBridgeFolderID; + } + + if (LLUUID fsCatID = gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER); !fsCatID.isNull()) + { + LLInventoryModel::item_array_t* items; + LLInventoryModel::cat_array_t* cats; + gInventory.getDirectDescendentsOf(fsCatID, cats, items); + if (cats) + { + for (const auto& cat : *cats) + { + if (cat->getName() == FS_BRIDGE_FOLDER) + { + mBridgeFolderID = cat->getUUID(); + return mBridgeFolderID; } } } } - else - { - fsCatID = gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER); - } - if (bridgeCatID.isNull()) - { - bridgeCatID = gInventory.createNewCategory(fsCatID, LLFolderType::FT_NONE, FS_BRIDGE_FOLDER); - } - - mBridgeFolderID = bridgeCatID; - - return mBridgeFolderID; + return LLUUID::null; } LLUUID FSLSLBridge::findFSBridgeContainerCategory() @@ -1613,7 +1644,7 @@ void FSLSLBridge::cleanUpBridgeFolder(const std::string& nameToCleanUp) return; } - LLUUID catID = findFSCategory(); + LLUUID catID = getBridgeFolder(); LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; @@ -1665,7 +1696,7 @@ void FSLSLBridge::cleanUpOldVersions() void FSLSLBridge::detachOtherBridges() { - LLUUID catID = findFSCategory(); + LLUUID catID = getBridgeFolder(); LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; diff --git a/indra/newview/fslslbridge.h b/indra/newview/fslslbridge.h index 31a0be7df9..630c98a4be 100644 --- a/indra/newview/fslslbridge.h +++ b/indra/newview/fslslbridge.h @@ -124,6 +124,7 @@ private: protected: LLViewerInventoryItem* findInvObject(const std::string& obj_name, const LLUUID& catID); + void setupFSCategory(inventory_func_type callback); LLUUID findFSCategory(); LLUUID findFSBridgeContainerCategory(); diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp index 2db58d41a6..d98e00dbc9 100644 --- a/indra/newview/llagentwearablesfetch.cpp +++ b/indra/newview/llagentwearablesfetch.cpp @@ -338,7 +338,7 @@ void LLLibraryOutfitsFetch::folderDone() } mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); - mLibraryClothingID = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_CLOTHING, false); + mLibraryClothingID = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_CLOTHING); // If Library->Clothing->Initial Outfits exists, use that. LLNameCategoryCollector matchFolderFunctor("Initial Outfits"); @@ -461,46 +461,50 @@ void LLLibraryOutfitsFetch::libraryDone() gInventory.removeObserver(this); LLPointer copy_waiter = new LLLibraryOutfitsCopyDone(this); - mImportedClothingID = gInventory.createNewCategory(mClothingID, - LLFolderType::FT_NONE, - mImportedClothingName); - // Copy each folder from library into clothing unless it already exists. - for (uuid_vec_t::const_iterator iter = mLibraryClothingFolders.begin(); - iter != mLibraryClothingFolders.end(); - ++iter) - { - const LLUUID& src_folder_id = (*iter); // Library clothing folder ID - const LLViewerInventoryCategory *cat = gInventory.getCategory(src_folder_id); - if (!cat) + gInventory.createNewCategory(mClothingID, LLFolderType::FT_NONE, + mImportedClothingName, [this, copy_waiter](const LLUUID& new_cat_id) { - LL_WARNS() << "Library folder import for uuid:" << src_folder_id << " failed to find folder." << LL_ENDL; - continue; - } + mImportedClothingID = new_cat_id; + // Copy each folder from library into clothing unless it already exists. + for (uuid_vec_t::const_iterator iter = mLibraryClothingFolders.begin(); + iter != mLibraryClothingFolders.end(); + ++iter) + { + const LLUUID& src_folder_id = (*iter); // Library clothing folder ID + const LLViewerInventoryCategory *cat = gInventory.getCategory(src_folder_id); + if (!cat) + { + LL_WARNS() << "Library folder import for uuid:" << src_folder_id << " failed to find folder." << LL_ENDL; + continue; + } - if (!LLAppearanceMgr::getInstance()->getCanMakeFolderIntoOutfit(src_folder_id)) - { - LL_INFOS() << "Skipping non-outfit folder name:" << cat->getName() << LL_ENDL; - continue; - } + if (!LLAppearanceMgr::getInstance()->getCanMakeFolderIntoOutfit(src_folder_id)) + { + LL_INFOS() << "Skipping non-outfit folder name:" << cat->getName() << LL_ENDL; + continue; + } - // Don't copy the category if it already exists. - LLNameCategoryCollector matchFolderFunctor(cat->getName()); - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t wearable_array; - gInventory.collectDescendentsIf(mImportedClothingID, - cat_array, wearable_array, - LLInventoryModel::EXCLUDE_TRASH, - matchFolderFunctor); - if (cat_array.size() > 0) - { - continue; - } + // Don't copy the category if it already exists. + LLNameCategoryCollector matchFolderFunctor(cat->getName()); + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; + gInventory.collectDescendentsIf(mImportedClothingID, + cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH, + matchFolderFunctor); + if (cat_array.size() > 0) + { + continue; + } - LLUUID dst_folder_id = gInventory.createNewCategory(mImportedClothingID, - LLFolderType::FT_NONE, - cat->getName()); - LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_folder_id, dst_folder_id, copy_waiter); - } + gInventory.createNewCategory(mImportedClothingID, + LLFolderType::FT_NONE, + cat->getName(), + [src_folder_id, copy_waiter](const LLUUID& new_cat_id) { + LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_folder_id, new_cat_id, copy_waiter); + }); + } + }); } void LLLibraryOutfitsFetch::importedFolderFetch() @@ -556,8 +560,6 @@ void LLLibraryOutfitsFetch::contentsDone() { LL_INFOS() << "start" << LL_ENDL; - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t wearable_array; LLPointer order_myoutfits_on_destroy = new LLBoostFuncInventoryCallback(no_op_inventory_func, order_my_outfits_cb); @@ -577,24 +579,26 @@ void LLLibraryOutfitsFetch::contentsDone() if (cat->getName() == LLStartUp::getInitialOutfitName()) continue; // First, make a folder in the My Outfits directory. - LLUUID new_outfit_folder_id = gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, cat->getName()); - - cat_array.clear(); - wearable_array.clear(); - // Collect the contents of each imported clothing folder, so we can create new outfit links for it - gInventory.collectDescendents(folder_id, cat_array, wearable_array, - LLInventoryModel::EXCLUDE_TRASH); - - LLInventoryObject::const_object_list_t item_array; - for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin(); - wearable_iter != wearable_array.end(); - ++wearable_iter) + gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, cat->getName(), [folder_id, order_myoutfits_on_destroy](const LLUUID&) { - LLConstPointer item = wearable_iter->get(); - item_array.push_back(item); - } + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t wearable_array; - link_inventory_array(LLAppearanceMgr::instance().getCOF(), item_array, order_myoutfits_on_destroy); + // Collect the contents of each imported clothing folder, so we can create new outfit links for it + gInventory.collectDescendents(folder_id, cat_array, wearable_array, + LLInventoryModel::EXCLUDE_TRASH); + + LLInventoryObject::const_object_list_t item_array; + for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin(); + wearable_iter != wearable_array.end(); + ++wearable_iter) + { + LLConstPointer item = wearable_iter->get(); + item_array.push_back(item); + } + + link_inventory_array(LLAppearanceMgr::instance().getCOF(), item_array, order_myoutfits_on_destroy); + }); } mOutfitsPopulated = true; diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index 056cdf8a0a..a823a6e812 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -33,6 +33,8 @@ #include "llinventorymodel.h" #include "llsdutil.h" #include "llviewerregion.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" #include "llinventoryobserver.h" #include "llviewercontrol.h" @@ -98,6 +100,7 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + callback(LLUUID::null); return; } @@ -372,6 +375,125 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t EnqueueAISCommand("UpdateItem", proc); } +/*static*/ +void AISAPI::FetchItem(const LLUUID &itemId, ITEM_TYPE type, completion_t callback) +{ + + std::string cap; + + cap = (type == INVENTORY) ? getInvCap() : getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } + std::string url = cap + std::string("/item/") + itemId.asString(); + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, itemId, LLSD(), callback, FETCHITEM)); + + EnqueueAISCommand("FetchItem", proc); +} + +/*static*/ +void AISAPI::FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type, bool recursive, completion_t callback, S32 depth) +{ + std::string cap; + + cap = (type == INVENTORY) ? getInvCap() : getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + callback(LLUUID::null); + return; + } + std::string url = cap + std::string("/category/") + catId.asString() + "/children"; + + if (recursive) + { + url += "?depth=*"; + } + else + { + url += "?depth=" + std::to_string(depth); + } + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + // get doesn't use body, can pass additional data + LLSD body; + body["depth"] = recursive ? S32_MAX : depth; + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, catId, body, callback, FETCHCATEGORYCHILDREN)); + + EnqueueAISCommand("FetchCategoryChildren", proc); +} + +/*static*/ +void AISAPI::FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type, bool recursive, completion_t callback, S32 depth) +{ + std::string cap; + + cap = (type == INVENTORY) ? getInvCap() : getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + return; + } + std::string url = cap + std::string("/category/") + catId.asString() + "/categories"; + + if (recursive) + { + url += "?depth=*"; + } + else + { + url += "?depth=" + std::to_string(depth); + } + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + // get doesn't use body, can pass additional data + LLSD body; + body["depth"] = depth; + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, catId, body, callback, FETCHCATEGORYCATEGORIES)); + + EnqueueAISCommand("FetchCategoryCategories", proc); +} + /*static*/ void AISAPI::EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc) { @@ -422,6 +544,28 @@ void AISAPI::onIdle(void *userdata) } } +/*static*/ +void AISAPI::onUpdateReceived(const std::string& context, const LLSD& update, COMMAND_TYPE type, const LLSD& request_body) +{ + LLTimer timer; + if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); + } + bool is_fetch = (type == FETCHITEM) + || (type == FETCHCATEGORYCHILDREN) + || (type == FETCHCATEGORYCATEGORIES); + // parse update llsd into stuff to do or parse received items. + S32 depth = 0; + if (is_fetch && request_body.has("depth")) + { + depth = request_body["depth"].asInteger(); + } + AISUpdate ais_update(update, is_fetch, depth); + ais_update.doUpdate(); // execute the updates in the appropriate order. + LL_INFOS("Inventory") << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL; +} + /*static*/ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter, invokationFn_t invoke, std::string url, @@ -431,7 +575,7 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); LLCore::HttpHeaders::ptr_t httpHeaders; - httpOptions->setTimeout(LLCoreHttpUtil::HTTP_REQUEST_EXPIRY_SECS); + httpOptions->setTimeout(180); LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; @@ -479,23 +623,42 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht } } } + else if (status.getType() == 403) + { + if (type == FETCHCATEGORYCHILDREN) + { + LL_DEBUGS("Inventory") << "Fetch failed, content is over imit" << LL_ENDL; + } + } LL_WARNS("Inventory") << "Inventory error: " << status.toString() << LL_ENDL; LL_WARNS("Inventory") << ll_pretty_print_sd(result) << LL_ENDL; } LL_DEBUGS("Inventory") << result << LL_ENDL; - gInventory.onAISUpdateReceived("AISCommand", result); + onUpdateReceived("AISCommand", result, type, body); if (callback && !callback.empty()) - { + { // [SL:KB] - Patch: Appearance-SyncAttach | Checked: Catznip-3.7 uuid_list_t ids; switch (type) { case COPYLIBRARYCATEGORY: + case FETCHCATEGORYCATEGORIES: + case FETCHCATEGORYCHILDREN: if (result.has("category_id")) { - ids.insert(result["category_id"]); + ids.emplace(result["category_id"]); + } + break; + case FETCHITEM: + if (result.has("linked_id")) + { + ids.emplace(result["linked_id"]); + } + else if (result.has("item_id")) + { + ids.emplace(result["item_id"]); } break; case COPYINVENTORY: @@ -514,48 +677,80 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht break; } - if (!ids.empty()) + // Call callback at least once regardless of failure. + if (ids.empty()) { - for (const auto& id : ids) - callback(id); + ids.emplace(LLUUID::null); } + + for (const auto& id : ids) + { + callback(id); + } + // [/SL:KB] -// LLUUID id(LLUUID::null); -// -// if (result.has("category_id") && (type == COPYLIBRARYCATEGORY)) -// { -// id = result["category_id"]; -// callback(id); -// } -// if (type == CREATEINVENTORY) -// { -// if (result.has("_created_categories")) -// { -// LLSD& cats = result["_created_categories"]; -// LLSD::array_const_iterator cat_iter; -// for (cat_iter = cats.beginArray(); cat_iter != cats.endArray(); ++cat_iter) -// { -// LLUUID cat_id = *cat_iter; -// callback(cat_id); -// } -// } -// if (result.has("_created_items")) -// { -// LLSD& items = result["_created_items"]; -// LLSD::array_const_iterator item_iter; -// for (item_iter = items.beginArray(); item_iter != items.endArray(); ++item_iter) -// { -// LLUUID item_id = *item_iter; -// callback(item_id); -// } -// } -// } + // bool needs_callback = true; + // LLUUID id(LLUUID::null); + + // if ( ( (type == COPYLIBRARYCATEGORY) + // || (type == FETCHCATEGORYCATEGORIES) + // || (type == FETCHCATEGORYCHILDREN)) + // && result.has("category_id")) + // { + // id = result["category_id"]; + // } + // if (type == FETCHITEM) + // { + // if (result.has("item_id")) + // { + // id = result["item_id"]; + // } + // if (result.has("linked_id")) + // { + // id = result["linked_id"]; + // } + // } + //if (type == CREATEINVENTORY) + //{ + // // CREATEINVENTORY can have multiple callbacks + // if (result.has("_created_categories")) + // { + // LLSD& cats = result["_created_categories"]; + // LLSD::array_const_iterator cat_iter; + // for (cat_iter = cats.beginArray(); cat_iter != cats.endArray(); ++cat_iter) + // { + // LLUUID cat_id = *cat_iter; + // callback(cat_id); + // needs_callback = false; + // } + // } + // if (result.has("_created_items")) + // { + // LLSD& items = result["_created_items"]; + // LLSD::array_const_iterator item_iter; + // for (item_iter = items.beginArray(); item_iter != items.endArray(); ++item_iter) + // { + // LLUUID item_id = *item_iter; + // callback(item_id); + // needs_callback = false; + // } + // } + //} + + // if (needs_callback) + // { + // // Call callback at least once regardless of failure. + // // UPDATEITEM doesn't expect an id + // callback(id); + // } } } //------------------------------------------------------------------------- -AISUpdate::AISUpdate(const LLSD& update) +AISUpdate::AISUpdate(const LLSD& update, bool fetch, S32 depth) +: mFetch(fetch) +, mFetchDepth(depth) { parseUpdate(update); } @@ -672,13 +867,13 @@ void AISUpdate::parseContent(const LLSD& update) if (update.has("category_id")) { - parseCategory(update); + parseCategory(update, mFetchDepth); } else { if (update.has("_embedded")) { - parseEmbedded(update["_embedded"]); + parseEmbedded(update["_embedded"], mFetchDepth); } } } @@ -696,7 +891,12 @@ void AISUpdate::parseItem(const LLSD& item_map) BOOL rv = new_item->unpackMessage(item_map); if (rv) { - if (curr_item) + if (mFetch) + { + mItemsCreated[item_id] = new_item; + mCatDescendentDeltas[new_item->getParentUUID()]; + } + else if (curr_item) { mItemsUpdated[item_id] = new_item; // This statement is here to cause a new entry with 0 @@ -731,7 +931,19 @@ void AISUpdate::parseLink(const LLSD& link_map) if (rv) { const LLUUID& parent_id = new_link->getParentUUID(); - if (curr_link) + if (mFetch) + { + LLPermissions default_perms; + default_perms.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + default_perms.initMasks(PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); + new_link->setPermissions(default_perms); + LLSaleInfo default_sale_info; + new_link->setSaleInfo(default_sale_info); + //LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL; + mItemsCreated[item_id] = new_link; + mCatDescendentDeltas[parent_id]; + } + else if (curr_link) { mItemsUpdated[item_id] = new_link; // This statement is here to cause a new entry with 0 @@ -760,9 +972,27 @@ void AISUpdate::parseLink(const LLSD& link_map) } -void AISUpdate::parseCategory(const LLSD& category_map) +void AISUpdate::parseCategory(const LLSD& category_map, S32 depth) { - LLUUID category_id = category_map["category_id"].asUUID(); + LLUUID category_id = category_map["category_id"].asUUID(); + S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN; + + if (category_map.has("version")) + { + version = category_map["version"].asInteger(); + } + + LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id); + + if (curr_cat + && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN + && version > LLViewerInventoryCategory::VERSION_UNKNOWN + && version < curr_cat->getVersion()) + { + LL_WARNS() << "Got stale folder, known: " << curr_cat->getVersion() + << ", received: " << version << LL_ENDL; + return; + } // Check descendent count first, as it may be needed // to populate newly created categories @@ -772,7 +1002,6 @@ void AISUpdate::parseCategory(const LLSD& category_map) } LLPointer new_cat; - LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id); if (curr_cat) { // Default to current values where not provided. @@ -792,13 +1021,33 @@ void AISUpdate::parseCategory(const LLSD& category_map) } BOOL rv = new_cat->unpackMessage(category_map); // *NOTE: unpackMessage does not unpack version or descendent count. - //if (category_map.has("version")) - //{ - // mCatVersionsUpdated[category_id] = category_map["version"].asInteger(); - //} if (rv) { - if (curr_cat) + if (mFetch) + { + // set version only if previous one was already known + // or if we are sure this update has full data and embeded items (depth 0+) + // since bulk fetch uses this to decide what still needs fetching + if (version > LLViewerInventoryCategory::VERSION_UNKNOWN + && (depth >= 0 || (curr_cat && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN))) + { + // Set version/descendents for newly fetched categories. + LL_DEBUGS("Inventory") << "Setting version to " << version + << " for category " << category_id << LL_ENDL; + new_cat->setVersion(version); + } + uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id); + if (mCatDescendentsKnown.end() != lookup_it) + { + S32 descendent_count = lookup_it->second; + LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count + << " for category " << category_id << LL_ENDL; + new_cat->setDescendentCount(descendent_count); + } + mCategoriesCreated[category_id] = new_cat; + mCatDescendentDeltas[new_cat->getParentUUID()]; + } + else if (curr_cat) { mCategoriesUpdated[category_id] = new_cat; // This statement is here to cause a new entry with 0 @@ -839,7 +1088,7 @@ void AISUpdate::parseCategory(const LLSD& category_map) // Check for more embedded content. if (category_map.has("_embedded")) { - parseEmbedded(category_map["_embedded"]); + parseEmbedded(category_map["_embedded"], depth - 1); } } @@ -856,7 +1105,7 @@ void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embe } } -void AISUpdate::parseEmbedded(const LLSD& embedded) +void AISUpdate::parseEmbedded(const LLSD& embedded, S32 depth) { if (embedded.has("links")) // _embedded in a category { @@ -872,11 +1121,11 @@ void AISUpdate::parseEmbedded(const LLSD& embedded) } if (embedded.has("categories")) // _embedded in a category { - parseEmbeddedCategories(embedded["categories"]); + parseEmbeddedCategories(embedded["categories"], depth); } if (embedded.has("category")) // _embedded in a link { - parseEmbeddedCategory(embedded["category"]); + parseEmbeddedCategory(embedded["category"], depth); } } @@ -901,7 +1150,7 @@ void AISUpdate::parseEmbeddedLinks(const LLSD& links) { const LLUUID link_id((*linkit).first); const LLSD& link_map = (*linkit).second; - if (mItemIds.end() == mItemIds.find(link_id)) + if (!mFetch && mItemIds.end() == mItemIds.find(link_id)) { LL_DEBUGS("Inventory") << "Ignoring link not in items list " << link_id << LL_ENDL; } @@ -917,7 +1166,7 @@ void AISUpdate::parseEmbeddedItem(const LLSD& item) // a single item (_embedded in a link) if (item.has("item_id")) { - if (mItemIds.end() != mItemIds.find(item["item_id"].asUUID())) + if (mFetch || mItemIds.end() != mItemIds.find(item["item_id"].asUUID())) { parseItem(item); } @@ -933,7 +1182,7 @@ void AISUpdate::parseEmbeddedItems(const LLSD& items) { const LLUUID item_id((*itemit).first); const LLSD& item_map = (*itemit).second; - if (mItemIds.end() == mItemIds.find(item_id)) + if (!mFetch && mItemIds.end() == mItemIds.find(item_id)) { LL_DEBUGS("Inventory") << "Ignoring item not in items list " << item_id << LL_ENDL; } @@ -944,19 +1193,19 @@ void AISUpdate::parseEmbeddedItems(const LLSD& items) } } -void AISUpdate::parseEmbeddedCategory(const LLSD& category) +void AISUpdate::parseEmbeddedCategory(const LLSD& category, S32 depth) { // a single category (_embedded in a link) if (category.has("category_id")) { - if (mCategoryIds.end() != mCategoryIds.find(category["category_id"].asUUID())) + if (mFetch || mCategoryIds.end() != mCategoryIds.find(category["category_id"].asUUID())) { - parseCategory(category); + parseCategory(category, depth); } } } -void AISUpdate::parseEmbeddedCategories(const LLSD& categories) +void AISUpdate::parseEmbeddedCategories(const LLSD& categories, S32 depth) { // a map of categories (_embedded in a category) for(LLSD::map_const_iterator categoryit = categories.beginMap(), @@ -965,13 +1214,13 @@ void AISUpdate::parseEmbeddedCategories(const LLSD& categories) { const LLUUID category_id((*categoryit).first); const LLSD& category_map = (*categoryit).second; - if (mCategoryIds.end() == mCategoryIds.find(category_id)) + if (!mFetch && mCategoryIds.end() == mCategoryIds.find(category_id)) { LL_DEBUGS("Inventory") << "Ignoring category not in categories list " << category_id << LL_ENDL; } else { - parseCategory(category_map); + parseCategory(category_map, depth); } } } diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h index 50352e4959..4f495681ff 100644 --- a/indra/newview/llaisapi.h +++ b/indra/newview/llaisapi.h @@ -38,6 +38,11 @@ class AISAPI { public: + typedef enum { + INVENTORY, + LIBRARY + } ITEM_TYPE; + typedef boost::function completion_t; static bool isAvailable(); @@ -50,6 +55,9 @@ public: static void PurgeDescendents(const LLUUID &categoryId, completion_t callback = completion_t()); static void UpdateCategory(const LLUUID &categoryId, const LLSD &updates, completion_t callback = completion_t()); static void UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t callback = completion_t()); + static void FetchItem(const LLUUID &itemId, ITEM_TYPE type, completion_t callback = completion_t()); + static void FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0); + static void FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0); static void CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, bool copySubfolders, completion_t callback = completion_t()); private: @@ -62,7 +70,10 @@ private: UPDATECATEGORY, UPDATEITEM, COPYLIBRARYCATEGORY, - CREATEINVENTORY + CREATEINVENTORY, + FETCHITEM, + FETCHCATEGORYCHILDREN, + FETCHCATEGORYCATEGORIES, } COMMAND_TYPE; static const std::string INVENTORY_CAP_NAME; @@ -73,6 +84,7 @@ private: static void EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc); static void onIdle(void *userdata); // launches postponed AIS commands + static void onUpdateReceived(const std::string& context, const LLSD& update, COMMAND_TYPE type, const LLSD& request_body); static std::string getInvCap(); static std::string getLibCap(); @@ -88,7 +100,7 @@ private: class AISUpdate { public: - AISUpdate(const LLSD& update); + AISUpdate(const LLSD& update, bool fetch, S32 depth); void parseUpdate(const LLSD& update); void parseMeta(const LLSD& update); void parseContent(const LLSD& update); @@ -98,14 +110,14 @@ public: // void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids); void parseLink(const LLSD& link_map); void parseItem(const LLSD& link_map); - void parseCategory(const LLSD& link_map); + void parseCategory(const LLSD& link_map, S32 depth); void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded); - void parseEmbedded(const LLSD& embedded); + void parseEmbedded(const LLSD& embedded, S32 depth); void parseEmbeddedLinks(const LLSD& links); void parseEmbeddedItems(const LLSD& items); - void parseEmbeddedCategories(const LLSD& categories); + void parseEmbeddedCategories(const LLSD& categories, S32 depth); void parseEmbeddedItem(const LLSD& item); - void parseEmbeddedCategory(const LLSD& category); + void parseEmbeddedCategory(const LLSD& category, S32 depth); void doUpdate(); private: void clearParseResults(); @@ -127,6 +139,8 @@ private: uuid_list_t mObjectsDeletedIds; uuid_list_t mItemIds; uuid_list_t mCategoryIds; + bool mFetch; + S32 mFetchDepth; }; #endif diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index e193899d47..58dd01b3b1 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1777,12 +1777,17 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds } // USES UDP PATH // D567 needs to carry over thumbnail info - LLUUID subfolder_id = gInventory.createNewCategory( parent_id, - LLFolderType::FT_NONE, - src_cat->getName()); - shallowCopyCategoryContents(src_id, subfolder_id, cb); + gInventory.createNewCategory( + parent_id, + LLFolderType::FT_NONE, + src_cat->getName(), + [src_id, cb](const LLUUID &new_id) + { + LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_id, new_id, cb); - gInventory.notifyObservers(); + gInventory.notifyObservers(); + } + ); } void LLAppearanceMgr::slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id, @@ -3103,21 +3108,27 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap // UDP PATH // D567 needs to carry over thumbnail info if present - LLUUID new_cat_id = gInventory.createNewCategory( + gInventory.createNewCategory( pid, LLFolderType::FT_NONE, - name); + name, + [cat_id, append](const LLUUID& new_cat_id) + { + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat_id, cats, items); + // Create a CopyMgr that will copy items, manage its own destruction + new LLCallAfterInventoryCopyMgr( + *items, new_cat_id, std::string("wear_inventory_category_callback"), + boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar, + LLAppearanceMgr::getInstance(), + gInventory.getCategory(new_cat_id), + append)); - // Create a CopyMgr that will copy items, manage its own destruction - new LLCallAfterInventoryCopyMgr( - *items, new_cat_id, std::string("wear_inventory_category_callback"), - boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar, - LLAppearanceMgr::getInstance(), - gInventory.getCategory(new_cat_id), - append)); - - // BAP fixes a lag in display of created dir. - gInventory.notifyObservers(); + // BAP fixes a lag in display of created dir. + gInventory.notifyObservers(); + } + ); } else { @@ -3607,7 +3618,7 @@ void LLAppearanceMgr::copyLibraryGestures() // Copy gestures LLUUID lib_gesture_cat_id = - gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false); + gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE); if (lib_gesture_cat_id.isNull()) { LL_WARNS() << "Unable to copy gestures, source category not found" << LL_ENDL; @@ -4502,31 +4513,17 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo // First, make a folder in the My Outfits directory. const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - // D567 replace with coros - if (AISAPI::isAvailable()) - { - // cap-based category creation was buggy until recently. use - // existence of AIS as an indicator the fix is present. Does - // not actually use AIS to create the category. - inventory_func_type func = boost::bind(&LLAppearanceMgr::onOutfitFolderCreated,this,_1,show_panel); - // D567 copy thumbnail info from source folder - gInventory.createNewCategory( - parent_id, - LLFolderType::FT_OUTFIT, - new_folder_name, - func); - } - else - { - // UDP PATH, should remove - // D567 copy thumbnail info from source folder - LLUUID folder_id = gInventory.createNewCategory( - parent_id, - LLFolderType::FT_OUTFIT, - new_folder_name); - onOutfitFolderCreated(folder_id, show_panel); - } + // UDP PATH, should remove + // D567 copy thumbnail info from source folder + gInventory.createNewCategory( + parent_id, + LLFolderType::FT_OUTFIT, + new_folder_name, + [show_panel](const LLUUID &new_cat_id) + { + LLAppearanceMgr::getInstance()->onOutfitFolderCreated(new_cat_id, show_panel); + }); } void LLAppearanceMgr::wearBaseOutfit() diff --git a/indra/newview/llfloatereditenvironmentbase.cpp b/indra/newview/llfloatereditenvironmentbase.cpp index 2850951668..cd24d79b7f 100644 --- a/indra/newview/llfloatereditenvironmentbase.cpp +++ b/indra/newview/llfloatereditenvironmentbase.cpp @@ -260,7 +260,7 @@ void LLFloaterEditEnvironmentBase::onSaveAsCommit(const LLSD& notification, cons } else if (mInventoryItem) { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); LLUUID parent_id = mInventoryItem->getParentUUID(); if (marketplacelistings_id == parent_id || gInventory.isObjectDescendentOf(mInventoryItem->getUUID(), gInventory.getLibraryRootFolderID())) { diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index 114b937a44..0377912208 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -214,7 +214,7 @@ BOOL LLFloaterGesture::postBuild() getChildView("play_btn")->setVisible( true); getChildView("stop_btn")->setVisible( false); setDefaultBtn("play_btn"); - mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE, false); + mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); uuid_vec_t folders; folders.push_back(mGestureFolderID); diff --git a/indra/newview/llfloaterlinkreplace.cpp b/indra/newview/llfloaterlinkreplace.cpp index 16c91370f0..c0243ece37 100644 --- a/indra/newview/llfloaterlinkreplace.cpp +++ b/indra/newview/llfloaterlinkreplace.cpp @@ -381,8 +381,8 @@ BOOL LLFloaterLinkReplace::tick() void LLFloaterLinkReplace::processBatch(LLInventoryModel::item_array_t items) { const LLViewerInventoryItem* target_item = gInventory.getItem(mTargetUUID); - const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) { diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp index 6002112257..17fa7c4f85 100644 --- a/indra/newview/llfloatermarketplacelistings.cpp +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -231,18 +231,31 @@ void LLPanelMarketplaceListings::onTabChange() void LLPanelMarketplaceListings::onAddButtonClicked() { - // Find active panel - LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); - if (panel) - { - LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - llassert(marketplacelistings_id.notNull()); - LLFolderType::EType preferred_type = LLFolderType::lookup("category"); - LLUUID category = gInventory.createNewCategory(marketplacelistings_id, preferred_type, LLStringUtil::null); - gInventory.notifyObservers(); - panel->setSelectionByID(category, TRUE); - panel->getRootFolder()->setNeedsAutoRename(TRUE); + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + llassert(marketplacelistings_id.notNull()); + LLFolderType::EType preferred_type = LLFolderType::lookup("category"); + LLHandle handle = getHandle(); + gInventory.createNewCategory( + marketplacelistings_id, + preferred_type, + LLStringUtil::null, + [handle](const LLUUID &new_cat_id) + { + // Find active panel + LLPanel *marketplace_panel = handle.get(); + if (!marketplace_panel) + { + return; + } + LLInventoryPanel* panel = (LLInventoryPanel*)marketplace_panel->getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + gInventory.notifyObservers(); + panel->setSelectionByID(new_cat_id, TRUE); + panel->getRootFolder()->setNeedsAutoRename(TRUE); + } } + ); } void LLPanelMarketplaceListings::onAuditButtonClicked() @@ -363,6 +376,7 @@ LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key) , mInventoryTitle(NULL) , mPanelListings(NULL) , mPanelListingsSet(false) +, mRootFolderCreating(false) { } @@ -448,15 +462,39 @@ void LLFloaterMarketplaceListings::setRootFolder() // If we are *not* a merchant or we have no market place connection established yet, do nothing return; } + if (!gInventory.isInventoryUsable()) + { + return; + } + LLFolderType::EType preferred_type = LLFolderType::FT_MARKETPLACE_LISTINGS; // We are a merchant. Get the Marketplace listings folder, create it if needs be. - LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true); - if (marketplacelistings_id.isNull()) - { - // We should never get there unless the inventory fails badly - LL_ERRS("SLM") << "Inventory problem: failure to create the marketplace listings folder for a merchant!" << LL_ENDL; - return; - } + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(preferred_type); + + if (marketplacelistings_id.isNull()) + { + if (!mRootFolderCreating) + { + mRootFolderCreating = true; + gInventory.createNewCategory( + gInventory.getRootFolderID(), + preferred_type, + LLStringUtil::null, + [](const LLUUID &new_cat_id) + { + LLFloaterMarketplaceListings *marketplace = LLFloaterReg::findTypedInstance("marketplace_listings"); + if (marketplace) + { + // will call setRootFolder again + marketplace->updateView(); + } + } + ); + } + return; + } + + mRootFolderCreating = false; // No longer need to observe new category creation if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) @@ -474,6 +512,7 @@ void LLFloaterMarketplaceListings::setRootFolder() } mRootFolderId = marketplacelistings_id; + mRootFolderCreating = true; } void LLFloaterMarketplaceListings::setPanels() @@ -544,6 +583,11 @@ void LLFloaterMarketplaceListings::updateView() { setRootFolder(); } + if (mRootFolderCreating) + { + // waiting for callback + return; + } // Update the bottom initializing status and progress dial if we are initializing or if we're a merchant and still loading if ((mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) || (is_merchant && (data_fetched <= MarketplaceFetchCodes::MARKET_FETCH_LOADING)) ) @@ -860,14 +904,17 @@ void LLFloaterMarketplaceValidation::onOpen(const LLSD& key) LLUUID cat_id(key.asUUID()); if (cat_id.isNull()) { - cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); } // Validates the folder if (cat_id.notNull()) { - LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); - validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), false); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + cat_id, + NULL, + boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), + false); } // Handle the listing folder being processed diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h index 33be7cf06b..15a1d13743 100644 --- a/indra/newview/llfloatermarketplacelistings.h +++ b/indra/newview/llfloatermarketplacelistings.h @@ -144,6 +144,7 @@ private: LLTextBox * mInventoryTitle; LLUUID mRootFolderId; + bool mRootFolderCreating; LLPanelMarketplaceListings * mPanelListings; bool mPanelListingsSet; }; diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp index 56ecebe7a8..420c79ca77 100644 --- a/indra/newview/llfloateropenobject.cpp +++ b/indra/newview/llfloateropenobject.cpp @@ -182,35 +182,11 @@ void LLFloaterOpenObject::moveToInventory(bool wear, bool replace) inventory_func_type func = boost::bind(LLFloaterOpenObject::callbackCreateInventoryCategory,_1,object_id,wear,replace); // D567 copy thumbnail info - LLUUID category_id = gInventory.createNewCategory(parent_category_id, - LLFolderType::FT_NONE, - name, - func); - - //If we get a null category ID, we are using a capability in createNewCategory and we will - //handle the following in the callbackCreateInventoryCategory routine. - // D567 review - if ( category_id.notNull() ) - { - LLCatAndWear* data = new LLCatAndWear; - data->mCatID = category_id; - data->mWear = wear; - data->mFolderResponded = false; - data->mReplace = replace; - - // Copy and/or move the items into the newly created folder. - // Ignore any "you're going to break this item" messages. - BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE, - callbackMoveInventory, - (void*)data); - if (!success) - { - delete data; - data = NULL; - - LLNotificationsUtil::add("OpenObjectCannotCopy"); - } - } + gInventory.createNewCategory( + parent_category_id, + LLFolderType::FT_NONE, + name, + func); } // static diff --git a/indra/newview/llfloatersimplesnapshot.cpp b/indra/newview/llfloatersimplesnapshot.cpp index 83f4f1ace9..cf596fd82f 100644 --- a/indra/newview/llfloatersimplesnapshot.cpp +++ b/indra/newview/llfloatersimplesnapshot.cpp @@ -31,6 +31,7 @@ #include "llfloaterreg.h" #include "llimagefiltersmanager.h" #include "llinventorymodel.h" +#include "llinventoryobserver.h" #include "llstatusbar.h" // can_afford_transaction() #include "llnotificationsutil.h" #include "llagent.h" @@ -47,8 +48,7 @@ const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN = 64; // Thumbnail posting coro -static const std::string THUMBNAIL_ITEM_UPLOAD_CAP = "InventoryItemThumbnailUpload"; -static const std::string THUMBNAIL_CATEGORY_UPLOAD_CAP = "InventoryCategoryThumbnailUpload"; +static const std::string THUMBNAIL_UPLOAD_CAP = "InventoryThumbnailUpload"; void post_thumbnail_image_coro(std::string cap_url, std::string path_to_image, LLSD first_data) { @@ -132,8 +132,27 @@ void post_thumbnail_image_coro(std::string cap_url, std::string path_to_image, L return; } - // todo: issue an inventory udpate? - //return result["new_asset"].asUUID(); + if (first_data.has("category_id")) + { + LLUUID cat_id = first_data["category_id"].asUUID(); + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (cat) + { + cat->setThumbnailUUID(result["new_asset"].asUUID()); + } + gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat_id); + } + if (first_data.has("item_id")) + { + LLUUID item_id = first_data["item_id"].asUUID(); + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if (item) + { + item->setThumbnailUUID(result["new_asset"].asUUID()); + } + // Are we supposed to get BulkUpdateInventory? + gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id); + } } ///---------------------------------------------------------------------------- @@ -346,6 +365,7 @@ void LLFloaterSimpleSnapshot::onSend() if (previewp->createUploadFile(temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN)) { uploadImageUploadFile(temp_file, mInventoryId, mTaskId); + closeFloater(); } else { @@ -380,31 +400,27 @@ void LLFloaterSimpleSnapshot::uploadThumbnail(const std::string &file_path, cons // static void LLFloaterSimpleSnapshot::uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id) { - std::string cap_name; LLSD data; if (task_id.notNull()) { - cap_name = THUMBNAIL_ITEM_UPLOAD_CAP; data["item_id"] = inventory_id; data["task_id"] = task_id; } else if (gInventory.getCategory(inventory_id)) { - cap_name = THUMBNAIL_CATEGORY_UPLOAD_CAP; data["category_id"] = inventory_id; } else { - cap_name = THUMBNAIL_ITEM_UPLOAD_CAP; data["item_id"] = inventory_id; } - std::string cap_url = gAgent.getRegionCapability(cap_name); + std::string cap_url = gAgent.getRegionCapability(THUMBNAIL_UPLOAD_CAP); if (cap_url.empty()) { LLSD args; - args["CAPABILITY"] = cap_url; + args["CAPABILITY"] = THUMBNAIL_UPLOAD_CAP; LLNotificationsUtil::add("RegionCapabilityRequestError", args); LL_WARNS("Thumbnail") << "Failed to upload profile image for item " << inventory_id << " " << task_id << ", no cap found" << LL_ENDL; return; diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index 38811ab250..fc899f67e5 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -478,14 +478,24 @@ void LLFriendCardsManager::ensureFriendsFolderExists() LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL; } - friends_folder_ID = gInventory.createNewCategory(calling_cards_folder_ID, - LLFolderType::FT_CALLINGCARD, get_friend_folder_name()); - - gInventory.createNewCategory(friends_folder_ID, - LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name()); - - // Now when we have all needed folders we can sync their contents with buddies list. - syncFriendsFolder(); + gInventory.createNewCategory( + calling_cards_folder_ID, + LLFolderType::FT_CALLINGCARD, + get_friend_folder_name(), + [](const LLUUID &new_category_id) + { + gInventory.createNewCategory( + new_category_id, + LLFolderType::FT_CALLINGCARD, + get_friend_all_subfolder_name(), + [](const LLUUID &new_category_id) + { + // Now when we have all needed folders we can sync their contents with buddies list. + LLFriendCardsManager::getInstance()->syncFriendsFolder(); + } + ); + } + ); } } @@ -510,11 +520,16 @@ void LLFriendCardsManager::ensureFriendsAllFolderExists() LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL; } - friends_all_folder_ID = gInventory.createNewCategory(friends_folder_ID, - LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name()); - - // Now when we have all needed folders we can sync their contents with buddies list. - syncFriendsFolder(); + gInventory.createNewCategory( + friends_folder_ID, + LLFolderType::FT_CALLINGCARD, + get_friend_all_subfolder_name(), + [](const LLUUID &new_cat_id) + { + // Now when we have all needed folders we can sync their contents with buddies list. + LLFriendCardsManager::getInstance()->syncFriendsFolder(); + } + ); } } diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 496d0097fd..46863162a8 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -378,7 +378,7 @@ BOOL LLInvFVBridge::cutToClipboard() const LLInventoryObject* obj = gInventory.getObject(mUUID); if (obj && isItemMovable() && isItemRemovable()) { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); const BOOL cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id); if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) || @@ -1404,7 +1404,7 @@ bool LLInvFVBridge::isItemInOutfits() const const LLInventoryModel* model = getInventoryModel(); if(!model) return false; - const LLUUID my_outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID my_outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); return isCOFFolder() || (my_outfits_cat == mUUID) || model->isObjectDescendentOf(mUUID, my_outfits_cat); } @@ -1489,7 +1489,7 @@ BOOL LLInvFVBridge::isLockedFolder(bool ignore_setting /*= false*/) const // *TODO : Suppress isInboxFolder() once Merchant Outbox is fully deprecated BOOL LLInvFVBridge::isInboxFolder() const { - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); if (inbox_id.isNull()) { @@ -1501,7 +1501,7 @@ BOOL LLInvFVBridge::isInboxFolder() const BOOL LLInvFVBridge::isMarketplaceListingsFolder() const { - const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (folder_id.isNull()) { @@ -1801,7 +1801,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const { std::string error_msg; LLInventoryModel* model = getInventoryModel(); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.notNull()) { LLViewerInventoryCategory * master_folder = model->getCategory(marketplacelistings_id); @@ -1959,7 +1959,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) { LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); // Note: For a single item, if it's not a copy, then it's a move move_item_to_marketplacelistings(itemp, marketplacelistings_id, ("copy_to_marketplace_listings" == action)); } @@ -2621,6 +2621,16 @@ LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const return LLFontGL::NORMAL; } +const LLUUID& LLFolderBridge::getThumbnailUUID() const +{ + LLViewerInventoryCategory* cat = getCategory(); + if (cat) + { + return cat->getThumbnailUUID(); + } + return LLUUID::null; +} + void LLFolderBridge::update() { // we know we have children but haven't fetched them (doesn't obey filter) @@ -2866,8 +2876,8 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (!filter) return false; const LLUUID &cat_id = inv_cat->getUUID(); - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); const LLUUID from_folder_uuid = inv_cat->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); @@ -2885,11 +2895,11 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (is_agent_inventory) { - const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); // FIRE-1392: Allow dragging all asset types into Landmarks folder - //const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); - const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND, false); + //const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); @@ -3184,7 +3194,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else { - if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX))) { set_dad_inbox_object(cat_id); } @@ -3214,11 +3224,16 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); if (version_folder_id.notNull()) { - LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); - if (!validate_marketplacelistings(cat,NULL)) + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) { - LLMarketplaceData::instance().activateListing(version_folder_id,false); + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } } + ); } // In all cases, update the listing we moved from so suffix are updated update_marketplace_category(from_folder_uuid); @@ -3696,18 +3711,26 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) if (depth_nesting_in_marketplace(mUUID) == 1) { LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID); - LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); mMessage = ""; - if (!validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3))) + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [this](bool result) { - LLSD subs; - subs["[ERROR_CODE]"] = mMessage; - LLNotificationsUtil::add("MerchantListingFailed", subs); - } - else - { - LLMarketplaceData::instance().activateListing(mUUID,true); - } + // todo: might need to ensure bridge/mUUID exists or this will cause crashes + if (!result) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + else + { + LLMarketplaceData::instance().activateListing(mUUID, true); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3) + ); } return; } @@ -3715,18 +3738,27 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if (depth_nesting_in_marketplace(mUUID) == 2) { - LLInventoryCategory* category = gInventory.getCategory(mUUID); mMessage = ""; - if (!validate_marketplacelistings(category,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false,2)) + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) { - LLSD subs; - subs["[ERROR_CODE]"] = mMessage; - LLNotificationsUtil::add("MerchantFolderActivationFailed", subs); - } - else - { - LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID); - } + if (!result) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantFolderActivationFailed", subs); + } + else + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + false, + 2); } return; } @@ -3749,29 +3781,44 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) } else if ("marketplace_create_listing" == action) { - LLViewerInventoryCategory* cat = gInventory.getCategory(mUUID); mMessage = ""; - bool validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false); - if (!validates) + + // first run vithout fix_hierarchy, second run with fix_hierarchy + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) { - mMessage = ""; - validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),true); - if (validates) + if (!result) { - LLNotificationsUtil::add("MerchantForceValidateListing"); + mMessage = ""; + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) + { + if (result) + { + LLNotificationsUtil::add("MerchantForceValidateListing"); + LLMarketplaceData::instance().createListing(mUUID); + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + true); } - } + else + { + LLMarketplaceData::instance().createListing(mUUID); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + false); - if (!validates) - { - LLSD subs; - subs["[ERROR_CODE]"] = mMessage; - LLNotificationsUtil::add("MerchantListingFailed", subs); - } - else - { - LLMarketplaceData::instance().createListing(mUUID); - } return; } else if ("marketplace_disassociate_listing" == action) @@ -3830,7 +3877,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { LLInventoryCategory * cat = gInventory.getCategory(mUUID); if (!cat) return; - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("move_to_marketplace_listings" != action), (("copy_or_move_to_marketplace_listings" == action))); } // FIRE-29342: Protect folder option @@ -4134,7 +4181,7 @@ void LLFolderBridge::pasteFromClipboard() LLInventoryModel* model = getInventoryModel(); if (model && isClipboardPasteable()) { - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); const BOOL paste_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); BOOL cut_from_marketplacelistings = FALSE; @@ -4195,11 +4242,11 @@ void LLFolderBridge::perform_pasteFromClipboard() LLInventoryModel* model = getInventoryModel(); if (model && isClipboardPasteable()) { - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); - const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); @@ -4479,9 +4526,9 @@ void LLFolderBridge::pasteLinkFromClipboard() LLInventoryModel* model = getInventoryModel(); if(model) { - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); @@ -4551,8 +4598,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); const LLUUID &favorites = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); // FIRE-11628: Option to delete broken links from AO folder if (mUUID == AOEngine::instance().getAOFolder()) @@ -5535,7 +5582,7 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c { if((inv_item->getInventoryType() == LLInventoryType::IT_TEXTURE) || (inv_item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)) { - const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); if(mUUID != my_outifts_id) { LLFloaterOutfitPhotoPreview* photo_preview = LLFloaterReg::showTypedInstance("outfit_photo_preview", inv_item->getUUID()); @@ -5654,12 +5701,12 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); // FIRE-1392: Allow dragging all asset types into Landmarks folder - //const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + //const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); const LLUUID from_folder_uuid = inv_item->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); @@ -5680,7 +5727,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLViewerObject* object = NULL; if(LLToolDragAndDrop::SOURCE_AGENT == source) { - const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID()); @@ -5864,7 +5911,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, else { // set up observer to select item once drag and drop from inbox is complete - if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX))) { set_dad_inbox_object(inv_item->getUUID()); } @@ -5882,11 +5929,15 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); if (version_folder_id.notNull()) { - LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); - if (!validate_marketplacelistings(cat,NULL)) + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) { - LLMarketplaceData::instance().activateListing(version_folder_id,false); - } + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + }); } } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 5a95e9fb59..35e30c685c 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -317,6 +317,7 @@ public: static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; + virtual const LLUUID& getThumbnailUUID() const; void setShowDescendantsCount(bool show_count) {mShowDescendantsCount = show_count;} diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index a50c955038..11818c50fe 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -221,7 +221,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { // At the moment background fetch only cares about VERSION_UNKNOWN, // so do not check isCategoryComplete that compares descendant count - LLInventoryModelBackgroundFetch::instance().start(folder_id); + LLInventoryModelBackgroundFetch::instance().start(folder_id, false); } } diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 846d549a55..125acea802 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -384,7 +384,7 @@ void update_all_marketplace_count(const LLUUID& cat_id) void update_all_marketplace_count() { // Get the marketplace root and launch the recursive exploration - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (!marketplace_listings_uuid.isNull()) { update_all_marketplace_count(marketplace_listings_uuid); @@ -933,7 +933,7 @@ void show_item_original(const LLUUID& item_uuid) { if(main_inventory->isSingleFolderMode()) { - main_inventory->onViewModeClick(); + main_inventory->toggleViewMode(); } // FIRE-31037: "Recent" inventory filter gets reset when using "Show Original" //main_inventory->resetFilters(); @@ -992,22 +992,6 @@ void open_marketplace_listings() LLFloaterReg::showInstance("marketplace_listings"); } -// Create a new folder in destFolderId with the same name as the item name and return the uuid of the new folder -// Note: this is used locally in various situation where we need to wrap an item into a special folder -LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId) -{ - llassert(item); - llassert(destFolderId.notNull()); - - LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName()); - gInventory.notifyObservers(); - - // *TODO : Create different notifications for the various cases - LLNotificationsUtil::add("OutboxFolderCreated"); - - return created_folder_id; -} - ///---------------------------------------------------------------------------- // Marketplace functions // @@ -1022,7 +1006,7 @@ S32 depth_nesting_in_marketplace(LLUUID cur_uuid) // Todo: findCategoryUUIDForType is somewhat expensive with large // flat root folders yet we use depth_nesting_in_marketplace at // every turn, find a way to correctly cache this id. - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplace_listings_uuid.isNull()) { return -1; @@ -1494,6 +1478,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn return accept; } +// Can happen asynhroneously!!! bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy) { // Get the marketplace listings depth of the destination folder, exit with error if not under marketplace @@ -1533,53 +1518,64 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol if (can_move_to_marketplace(inv_item, error_msg, true)) { // When moving an isolated item, we might need to create the folder structure to support it + + LLUUID item_id = inv_item->getUUID(); + std::function callback_create_stock = [copy, item_id](const LLUUID& new_cat_id) + { + // Verify we can have this item in that destination category + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); + LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); + if (!dest_cat->acceptItem(viewer_inv_item)) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + } + + if (copy) + { + // Copy the item + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_id)); + copy_inventory_item( + gAgent.getID(), + viewer_inv_item->getPermissions().getOwner(), + viewer_inv_item->getUUID(), + new_cat_id, + std::string(), + cb); + } + else + { + // Reparent the item + gInventory.changeItemParent(viewer_inv_item, new_cat_id, true); + } + }; + + std::function callback_dest_create = [item_id, callback_create_stock](const LLUUID& new_cat_id) + { + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); + LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && + (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // We need to create a stock folder to move a no copy item + gInventory.createNewCategory(new_cat_id, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName(), callback_create_stock); + } + else + { + callback_create_stock(new_cat_id); + } + }; + if (depth == 0) { // We need a listing folder - dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); - depth++; + gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create); } if (depth == 1) { // We need a version folder - dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); - depth++; - } - LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); - if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && - (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) - { - // We need to create a stock folder to move a no copy item - dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName()); - dest_cat = gInventory.getCategory(dest_folder); - depth++; - } - - // Verify we can have this item in that destination category - if (!dest_cat->acceptItem(viewer_inv_item)) - { - LLSD subs; - subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return false; - } - - if (copy) - { - // Copy the item - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); - copy_inventory_item( - gAgent.getID(), - viewer_inv_item->getPermissions().getOwner(), - viewer_inv_item->getUUID(), - dest_folder, - std::string(), - cb); - } - else - { - // Reparent the item - gInventory.changeItemParent(viewer_inv_item, dest_folder, true); + gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create); } } else @@ -1627,7 +1623,7 @@ bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUU // Reparent the folder gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false); // Check the destination folder recursively for no copy items and promote the including folders if any - validate_marketplacelistings(dest_cat); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings(dest_folder); } // Update the modified folders @@ -1652,32 +1648,23 @@ bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCa return cat1->getName().compare(cat2->getName()) < 0; } -void dump_trace(std::string& message, S32 depth, LLError::ELevel log_level) -{ - LL_INFOS() << "validate_marketplacelistings : error = "<< log_level << ", depth = " << depth << ", message = " << message << LL_ENDL; -} - // Make all relevant business logic checks on the marketplace listings starting with the folder as argument. // This function does no deletion of listings but a mere audit and raises issues to the user (through the -// optional callback cb). It also returns a boolean, true if things validate, false if issues are raised. +// optional callback cb). // The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders. -bool validate_marketplacelistings( +// @pending_callbacks - how many callbacks we are waiting for, must be inited before use +// @result - true if things validate, false if issues are raised, must be inited before use +typedef boost::function validation_result_callback_t; +void validate_marketplacelistings( LLInventoryCategory* cat, - validation_callback_t cb, + validation_result_callback_t cb_result, + LLMarketplaceValidator::validation_msg_callback_t cb_msg, bool fix_hierarchy, S32 depth, - bool notify_observers) + bool notify_observers, + S32 &pending_callbacks, + bool &result) { -#if 0 - // Used only for debug - if (!cb) - { - cb = boost::bind(&dump_trace, _1, _2, _3); - } -#endif - // Folder is valid unless issue is raised - bool result = true; - // Get the type and the depth of the folder LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (cat); const LLFolderType::EType folder_type = cat->getPreferredType(); @@ -1709,10 +1696,10 @@ bool validate_marketplacelistings( if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy)) { result = false; - if (cb) + if (cb_msg) { message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } } } @@ -1722,26 +1709,42 @@ bool validate_marketplacelistings( { if (fix_hierarchy) { - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } + // Nest the stock folder one level deeper in a normal folder and restart from there + pending_callbacks++; LLUUID parent_uuid = cat->getParentUUID(); - LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName()); - LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid); - gInventory.changeCategoryParent(viewer_cat, folder_uuid, false); - result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy, depth + 1, notify_observers); - return result; + LLUUID cat_uuid = cat->getUUID(); + gInventory.createNewCategory(parent_uuid, + LLFolderType::FT_NONE, + cat->getName(), + [cat_uuid, cb_result, cb_msg, fix_hierarchy, depth, notify_observers](const LLUUID &new_cat_id) + { + LLInventoryCategory * move_cat = gInventory.getCategory(cat_uuid); + LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *)(move_cat); + LLInventoryCategory * new_cat = gInventory.getCategory(new_cat_id); + gInventory.changeCategoryParent(viewer_cat, new_cat_id, false); + S32 pending = 0; + bool result = true; + // notify_observers probably should be true in such case? + validate_marketplacelistings(new_cat, cb_result, cb_msg, fix_hierarchy, depth + 1, notify_observers, pending, result); + cb_result(pending, result); + } + ); + result = false; + return; } else { result = false; - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } } } @@ -1772,10 +1775,10 @@ bool validate_marketplacelistings( if (!can_move_to_marketplace(item, error_msg, false)) { has_bad_items = true; - if (cb && fix_hierarchy) + if (cb_msg && fix_hierarchy) { std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } continue; } @@ -1806,35 +1809,35 @@ bool validate_marketplacelistings( if (depth == 2) { // If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist) - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } } else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) { // If this is a legit but empty stock folder, warn only (listing must stay searchable when out of stock) - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } } - else if (cb) + else if (cb_msg) { // We warn if there's nothing in a regular folder (may be it's an under construction listing) std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } } else { // Done with that folder : Print out the folder name unless we already found an error here - if (cb && result && (depth >= 1)) + if (cb_msg && result && (depth >= 1)) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message,depth,LLError::LEVEL_INFO); + cb_msg(message,depth,LLError::LEVEL_INFO); } } } @@ -1842,10 +1845,10 @@ bool validate_marketplacelistings( else if ((count == 1) && !has_bad_items && (((unique_key == default_key) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) { // Done with that folder : Print out the folder name unless we already found an error here - if (cb && result && (depth >= 1)) + if (cb_msg && result && (depth >= 1)) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message,depth,LLError::LEVEL_INFO); + cb_msg(message,depth,LLError::LEVEL_INFO); } } else @@ -1867,11 +1870,11 @@ bool validate_marketplacelistings( while (items_vector_it != items_vector.end()) { // Create a new folder - LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); + const LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName()); LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); - if (cb) + if (cb_msg) { std::string message = ""; if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK) @@ -1882,30 +1885,46 @@ bool validate_marketplacelistings( { message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); } - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } - LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, folder_name); - - // Move each item to the new folder - while (!items_vector_it->second.empty()) + + pending_callbacks++; + std::vector uuid_vector = items_vector_it->second; // needs to be a copy for lambda + gInventory.createNewCategory( + parent_uuid, + new_folder_type, + folder_name, + [uuid_vector, cb_result, cb_msg, depth, parent_uuid, notify_observers](const LLUUID &new_category_id) { - LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); - if (cb) + // Move each item to the new folder + std::vector::const_reverse_iterator iter = uuid_vector.rbegin(); + while (iter != uuid_vector.rend()) { - std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); - cb(message,depth,LLError::LEVEL_WARN); + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(*iter); + if (cb_msg) + { + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); + cb_msg(message, depth, LLError::LEVEL_WARN); + } + gInventory.changeItemParent(viewer_inv_item, new_category_id, true); + iter++; } - gInventory.changeItemParent(viewer_inv_item, folder_uuid, true); - items_vector_it->second.pop_back(); - } - - // Next type - update_marketplace_category(parent_uuid); - update_marketplace_category(folder_uuid); - if (notify_observers) - { - gInventory.notifyObservers(); + + // Next type + update_marketplace_category(parent_uuid); + update_marketplace_category(new_category_id); + if (notify_observers) + { + gInventory.notifyObservers(); + } + cb_result(0, true); } + ); items_vector_it++; } } @@ -1919,11 +1938,11 @@ bool validate_marketplacelistings( { LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (*iter); gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); - result &= validate_marketplacelistings(viewer_cat, cb, fix_hierarchy, depth, false); + validate_marketplacelistings(viewer_cat, cb_result, cb_msg, fix_hierarchy, depth, false, pending_callbacks, result); } } } - else if (cb) + else if (cb_msg) { // We are not fixing the hierarchy but reporting problems, report everything we can find // Print the folder name @@ -1934,20 +1953,20 @@ bool validate_marketplacelistings( // Report if a stock folder contains a mix of items result = false; std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0)) { // Report if a stock folder contains subfolders result = false; std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else { // Simply print the folder name std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message,depth,LLError::LEVEL_INFO); + cb_msg(message,depth,LLError::LEVEL_INFO); } } // Scan each item and report if there's a problem @@ -1962,21 +1981,21 @@ bool validate_marketplacelistings( // Report items that shouldn't be there to start with result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) { // Report stock items that are misplaced result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else if (depth == 1) { // Report items not wrapped in version folder result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } } } @@ -1985,17 +2004,18 @@ bool validate_marketplacelistings( if (viewer_cat->getDescendentCount() == 0) { // Remove the current folder if it ends up empty - if (cb) + if (cb_msg) { std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } gInventory.removeCategory(cat->getUUID()); if (notify_observers) { gInventory.notifyObservers(); } - return result && !has_bad_items; + result &=!has_bad_items; + return; } } @@ -2008,15 +2028,15 @@ bool validate_marketplacelistings( for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { LLInventoryCategory* category = *iter; - result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1, false); + validate_marketplacelistings(category, cb_result, cb_msg, fix_hierarchy, depth + 1, false, pending_callbacks, result); } - + update_marketplace_category(cat->getUUID(), true, true); if (notify_observers) { gInventory.notifyObservers(); } - return result && !has_bad_items; + result &= !has_bad_items; } void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) @@ -2116,9 +2136,92 @@ void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::st inventory_func_type func = boost::bind(&move_items_to_folder, _1, selected_uuids); gInventory.createNewCategory(first_item->getParentUUID(), LLFolderType::FT_NONE, folder_name, func); - } +///---------------------------------------------------------------------------- +/// LLMarketplaceValidator implementations +///---------------------------------------------------------------------------- + + +LLMarketplaceValidator::LLMarketplaceValidator() +{ +} +LLMarketplaceValidator::~LLMarketplaceValidator() +{ +} + +void LLMarketplaceValidator::validateMarketplaceListings( + const LLUUID &category_id, + LLMarketplaceValidator::validation_done_callback_t cb_done, + LLMarketplaceValidator::validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth) +{ + + mValidationQueue.emplace(category_id, cb_done, cb_msg, fix_hierarchy, depth); + if (!mValidationInProgress) + { + start(); + } +} + +void LLMarketplaceValidator::start() +{ + if (mValidationQueue.empty()) + { + mValidationInProgress = false; + return; + } + mValidationInProgress = true; + mPendingCallbacks = 1; // do '1' in case something decides ro callback immediately + const ValidationRequest &first = mValidationQueue.front(); + LLViewerInventoryCategory* cat = gInventory.getCategory(first.mCategoryId); + + validation_result_callback_t result_callback = [](S32 pending, bool result) + { + LLMarketplaceValidator* validator = LLMarketplaceValidator::getInstance(); + validator->mPendingCallbacks--; // we just got a callback + validator->mPendingCallbacks += pending; + validator->mPendingResult &= result; + if (validator->mPendingCallbacks <= 0) + { + llassert(validator->mPendingCallbacks == 0); // shouldn't be below 0 + const ValidationRequest &first = validator->mValidationQueue.front(); + if (first.mCbDone) + { + first.mCbDone(validator->mPendingResult); + } + validator->mValidationQueue.pop(); // done; + validator->start(); + } + }; + + validate_marketplacelistings( + cat, + result_callback, + first.mCbMsg, + first.mFixHierarchy, + first.mDepth, + true, + mPendingCallbacks, + mPendingResult); + + result_callback(mPendingCallbacks, mPendingResult); +} + +LLMarketplaceValidator::ValidationRequest::ValidationRequest( + LLUUID category_id, + validation_done_callback_t cb_done, + validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth) +: mCategoryId(category_id) +, mCbDone(cb_done) +, mCbMsg(cb_msg) +, mFixHierarchy(fix_hierarchy) +, mDepth(depth) +{} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -2665,7 +2768,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { // Undo delete item confirmation per-session annoyance //static bool sDisplayedAtSession = false; - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); bool marketplacelistings_item = false; LLAllDescendentsPassedFilter f; for (std::set::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it) @@ -2923,7 +3026,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root if (action == "wear" || action == "wear_add") { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); std::copy_if(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids), @@ -3203,7 +3306,7 @@ void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) // target listing *and* the original listing. So we need to keep track of both. // Note: do not however put the marketplace listings root itself in this list or the whole marketplace data will be rebuilt. sMarketplaceFolders.clear(); - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.isNull()) { return; diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index a99886385b..391b012d95 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -88,13 +88,12 @@ void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryMode // Generates a string containing the path to the item specified by item_id. void append_path(const LLUUID& id, std::string& path); -typedef boost::function validation_callback_t; bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false); bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false); bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false); bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false, bool move_no_copy_items = false); -bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1, bool notify_observers = true); + S32 depth_nesting_in_marketplace(LLUUID cur_uuid); LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false); @@ -109,6 +108,50 @@ bool is_only_items_selected(const uuid_vec_t& selected_uuids); ** ** *******************************************************************************/ +class LLMarketplaceValidator: public LLSingleton +{ + LLSINGLETON(LLMarketplaceValidator); + ~LLMarketplaceValidator(); + LOG_CLASS(LLMarketplaceValidator); +public: + + typedef boost::function validation_msg_callback_t; + typedef boost::function validation_done_callback_t; + + void validateMarketplaceListings( + const LLUUID &category_id, + validation_done_callback_t cb_done = NULL, + validation_msg_callback_t cb_msg = NULL, + bool fix_hierarchy = true, + S32 depth = -1); + +private: + void start(); + + class ValidationRequest + { + public: + ValidationRequest( + LLUUID category_id, + validation_done_callback_t cb_done, + validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth); + LLUUID mCategoryId; + validation_done_callback_t mCbDone; + validation_msg_callback_t mCbMsg; + bool mFixHierarchy; + S32 mDepth; + }; + + bool mValidationInProgress; + S32 mPendingCallbacks; + bool mPendingResult; + // todo: might be a good idea to memorize requests by id and + // filter out ones that got multiple validation requests + std::queue mValidationQueue; +}; + /******************************************************************************** ** ** ** INVENTORY COLLECTOR FUNCTIONS diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 4dbcd050df..cacae126ca 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -41,6 +41,7 @@ #include "llinventorypanel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventoryobserver.h" #include "llinventorypanel.h" #include "llfloaterpreviewtrash.h" @@ -209,6 +210,7 @@ public: titem->unpackMessage(item); LL_DEBUGS("Inventory") << "unpacked item '" << titem->getName() << "' in " << titem->getParentUUID() << LL_ENDL; + // callback id might be no longer supported U32 callback_id = item["callback_id"].asInteger(); if (titem->getUUID().notNull()) @@ -454,6 +456,7 @@ LLInventoryModel::LLInventoryModel() mIsNotifyObservers(FALSE), mModifyMask(LLInventoryObserver::ALL), mChangedItemIDs(), + mBulckFecthCallbackSlot(), mObservers(), mProtectedCategoriesChangedCallbackConnection(), // FIRE-29342: Protect folder option mHttpRequestFG(NULL), @@ -493,6 +496,11 @@ void LLInventoryModel::cleanupInventory() mObservers.erase(iter); delete observer; } + + if (mBulckFecthCallbackSlot.connected()) + { + mBulckFecthCallbackSlot.disconnect(); + } mObservers.clear(); // Run down HTTP transport @@ -872,10 +880,63 @@ void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::E } } +void LLInventoryModel::ensureCategoryForTypeExists(LLFolderType::EType preferred_type) +{ + LLUUID rv = LLUUID::null; + LLUUID root_id = gInventory.getRootFolderID(); + if (LLFolderType::FT_ROOT_INVENTORY == preferred_type) + { + rv = root_id; + } + else if (root_id.notNull()) + { + cat_array_t* cats = NULL; + cats = get_ptr_in_map(mParentChildCategoryTree, root_id); + if (cats) + { + S32 count = cats->size(); + for (S32 i = 0; i < count; ++i) + { + LLViewerInventoryCategory* p_cat = cats->at(i); + if (p_cat && p_cat->getPreferredType() == preferred_type) + { + const LLUUID& folder_id = cats->at(i)->getUUID(); + if (rv.isNull() || folder_id < rv) + { + rv = folder_id; + } + } + } + } + } + + if (rv.isNull() && root_id.notNull()) + { + + if (isInventoryUsable()) + { + createNewCategory( + root_id, + preferred_type, + LLStringUtil::null, + [preferred_type](const LLUUID &new_cat_id) + { + LL_DEBUGS("Inventory") << "Created category: " << new_cat_id + << " for type: " << preferred_type << LL_ENDL; + } + ); + } + else + { + LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type + << " because inventory is not usable" << LL_ENDL; + } + } +} + const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( LLFolderType::EType preferred_type, - bool create_folder, - const LLUUID& root_id) + const LLUUID& root_id) const { LLUUID rv = LLUUID::null; if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) @@ -904,18 +965,14 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( } } - if(rv.isNull() && create_folder && root_id.notNull()) + if(rv.isNull() && root_id.notNull() && preferred_type != LLFolderType::FT_MARKETPLACE_LISTINGS) { - - if (isInventoryUsable()) - { - return createNewCategory(root_id, preferred_type, LLStringUtil::null); - } - else - { - LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type - << " because inventory is not usable" << LL_ENDL; - } + // if it does not exists, it should either be added + // to createCommonSystemCategories or server should + // have set it + llassert(false); + LL_WARNS("Inventory") << "Tried to find folder, type " << preferred_type + << " but category does not exist" << LL_ENDL; } return rv; } @@ -924,12 +981,12 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( // specifies 'type' as what it defaults to containing. The category is // not necessarily only for that type. *NOTE: This will create a new // inventory category on the fly if one does not exist. -const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type) const { - return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getRootFolderID()); + return findCategoryUUIDForTypeInRoot(preferred_type, gInventory.getRootFolderID()); } -const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) +const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const { LLUUID cat_id; switch (preferred_type) @@ -960,14 +1017,14 @@ const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType:: if (cat_id.isNull() || !getCategory(cat_id)) { - cat_id = findCategoryUUIDForTypeInRoot(preferred_type, true, getRootFolderID()); + cat_id = findCategoryUUIDForTypeInRoot(preferred_type, getRootFolderID()); } return cat_id; } -const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const { - return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getLibraryRootFolderID()); + return findCategoryUUIDForTypeInRoot(preferred_type, gInventory.getLibraryRootFolderID()); } LLUUID LLInventoryModel::findCategoryByName(std::string name) @@ -995,27 +1052,31 @@ LLUUID LLInventoryModel::findCategoryByName(std::string name) // Convenience function to create a new category. You could call // updateCategory() with a newly generated UUID category, but this // version will take care of details like what the name should be -// based on preferred type. Returns the UUID of the new category. -// -// On failure, returns a null UUID. -// FIXME: callers do not check for or handle a null results currently. -LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, +// based on preferred type. +void LLInventoryModel::createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& pname, inventory_func_type callback) { - LLUUID id; // Initially null. if (!isInventoryUsable()) { LL_WARNS(LOG_INV) << "Inventory is not usable; can't create requested category of type " << preferred_type << LL_ENDL; - return id; + if (callback) + { + callback(LLUUID::null); + } + return; } if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup()) { LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL; - return id; + if (callback) + { + callback(LLUUID::null); + } + return; } if (preferred_type != LLFolderType::FT_NONE) @@ -1031,33 +1092,64 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, { name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type)); } - -#ifdef USE_AIS_FOR_NC - // D567 currently this doesn't really work due to limitations in - // AIS3, also violates the common caller assumption that we can - // assign the id and return immediately. - if (callback) + + if (AISAPI::isAvailable()) { - // D567 note that we no longer assign the UUID in the viewer, so various workflows need to change. LLSD new_inventory = LLSD::emptyMap(); new_inventory["categories"] = LLSD::emptyArray(); LLViewerInventoryCategory cat(LLUUID::null, parent_id, preferred_type, name, gAgent.getID()); LLSD cat_sd = cat.asLLSD(); new_inventory["categories"].append(cat_sd); - AISAPI::CreateInventory(parent_id, new_inventory, callback); + AISAPI::CreateInventory( + parent_id, + new_inventory, + [this, callback, parent_id, preferred_type, name] (const LLUUID& new_category) + { + if (new_category.isNull()) + { + if (callback) + { + callback(new_category); + } + return; + } - return LLUUID::null; + LLViewerInventoryCategory* folderp = gInventory.getCategory(new_category); + if (!folderp) + { + // Add the category to the internal representation + LLPointer cat = new LLViewerInventoryCategory( + new_category, + parent_id, + preferred_type, + name, + gAgent.getID()); + + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + accountForUpdate(update); + + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 + cat->setDescendentCount(0); + updateCategory(cat); + } + + if (callback) + { + callback(new_category); + } + }); + return; } -#else + LLViewerRegion* viewer_region = gAgent.getRegion(); std::string url; if ( viewer_region ) url = viewer_region->getCapability("CreateInventoryCategory"); - if (!url.empty() && callback) + if (!url.empty()) { //Let's use the new capability. - + LLUUID id; id.generate(); LLSD request, body; body["folder_id"] = id; @@ -1071,48 +1163,13 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, LL_DEBUGS(LOG_INV) << "Creating category via request: " << ll_pretty_print_sd(request) << LL_ENDL; LLCoros::instance().launch("LLInventoryModel::createNewCategoryCoro", boost::bind(&LLInventoryModel::createNewCategoryCoro, this, url, body, callback)); - - return LLUUID::null; - } -#endif - - - if (!gMessageSystem) - { - return LLUUID::null; + return; } - // D567 FIXME this UDP code path needs to be removed. Requires - // reworking many of the callers to use callbacks rather than - // assuming instant success. - - // Add the category to the internal representation - LL_WARNS() << "D567 need to remove this usage" << LL_ENDL; - - id.generate(); - LLPointer cat = - new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID()); - cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 - cat->setDescendentCount(0); - LLCategoryUpdate update(cat->getParentUUID(), 1); - accountForUpdate(update); - updateCategory(cat); - - LL_DEBUGS(LOG_INV) << "Creating category via UDP message CreateInventoryFolder, type " << preferred_type << LL_ENDL; - - // Create the category on the server. We do this to prevent people - // from munging their protected folders. - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("CreateInventoryFolder"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlock("FolderData"); - cat->packMessage(msg); - gAgent.sendReliableMessage(); - - // return the folder id of the newly created folder - return id; + if (callback) + { + callback(LLUUID::null); // Notify about failure + } } void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inventory_func_type callback) @@ -1136,12 +1193,20 @@ void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inv if (!status) { LL_WARNS() << "HTTP failure attempting to create category." << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } if (!result.has("folder_id")) { LL_WARNS() << "Malformed response contents" << ll_pretty_print_sd(result) << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -1707,7 +1772,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 mask |= LLInventoryObserver::LABEL; } // Under marketplace, category labels are quite complex and need extra upate - const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplace_id.notNull() && isObjectDescendentOf(cat->getUUID(), marketplace_id)) { mask |= LLInventoryObserver::LABEL; @@ -1870,17 +1935,13 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat, notifyObservers(); } -void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update) +void LLInventoryModel::rebuildBrockenLinks() { - LLTimer timer; - if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) - { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); - } - - AISUpdate ais_update(update); // parse update llsd into stuff to do. - ais_update.doUpdate(); // execute the updates in the appropriate order. - LL_INFOS(LOG_INV) << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL; + for (LLUUID link_id : mPossiblyBrockenLinks) + { + addChangedMask(LLInventoryObserver::REBUILD, link_id); + } + mPossiblyBrockenLinks.clear(); } // Does not appear to be used currently. @@ -2516,9 +2577,34 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) // The item will show up as a broken link. if (item->getIsBrokenLink()) { - LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName() - << " itemID: " << item->getUUID() - << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + if (!LLInventoryModelBackgroundFetch::getInstance()->isEverythingFetched()) + { + // isEverythingFetched is actually 'initial' fetch only. + // Schedule this link for a recheck once inventory gets loaded + mPossiblyBrockenLinks.insert(item->getUUID()); + if (!mBulckFecthCallbackSlot.connected()) + { + // Links might take a while to update this way, and there + // might be a lot of them. A better option might be to check + // links periodically with final check on fetch completion. + mBulckFecthCallbackSlot = + LLInventoryModelBackgroundFetch::getInstance()->setAllFoldersFetchedCallback( + []() + { + gInventory.rebuildBrockenLinks(); + }); + } + LL_DEBUGS(LOG_INV) << "Scheduling a link to be rebuilt later [ name: " << item->getName() + << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + + } + else + { + LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName() + << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + } } if (item->getIsLinkType()) { @@ -3127,7 +3213,7 @@ void LLInventoryModel::buildParentChildMap() } } - const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null); + const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) != LLUUID::null); sFirstTimeInViewer2 = !COF_exists || gAgent.isFirstLogin(); @@ -3383,14 +3469,14 @@ LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground, void LLInventoryModel::createCommonSystemCategories() { - gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK, true); // folder should exist before user tries to 'landmark this' - gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS, true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, true); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_TRASH); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_FAVORITE); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CALLINGCARD); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_MY_OUTFITS); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CURRENT_OUTFIT); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_LANDMARK); // folder should exist before user tries to 'landmark this' + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_SETTINGS); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_INBOX); } struct LLUUIDAndName @@ -3614,9 +3700,6 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg) msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem, processRemoveInventoryItem, NULL); - msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder, - processUpdateInventoryFolder, - NULL); msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder, processRemoveInventoryFolder, NULL); @@ -3764,66 +3847,6 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) gInventory.notifyObservers(); } -// static -void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, - void**) -{ - LL_DEBUGS(LOG_INV) << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL; - LLUUID agent_id, folder_id, parent_id; - //char name[DB_INV_ITEM_NAME_BUF_SIZE]; - msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id); - if(agent_id != gAgent.getID()) - { - LL_WARNS(LOG_INV) << "Got an UpdateInventoryFolder for the wrong agent." - << LL_ENDL; - return; - } - LLPointer lastfolder; // hack - cat_array_t folders; - update_map_t update; - S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); - for(S32 i = 0; i < count; ++i) - { - LLPointer tfolder = new LLViewerInventoryCategory(gAgent.getID()); - lastfolder = tfolder; - tfolder->unpackMessage(msg, _PREHASH_FolderData, i); - // make sure it's not a protected folder - tfolder->setPreferredType(LLFolderType::FT_NONE); - folders.push_back(tfolder); - // examine update for changes. - LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); - if(folderp) - { - if(tfolder->getParentUUID() == folderp->getParentUUID()) - { - update[tfolder->getParentUUID()]; - } - else - { - ++update[tfolder->getParentUUID()]; - --update[folderp->getParentUUID()]; - } - } - else - { - ++update[tfolder->getParentUUID()]; - } - } - gInventory.accountForUpdate(update); - for (cat_array_t::iterator it = folders.begin(); it != folders.end(); ++it) - { - gInventory.updateCategory(*it); - } - gInventory.notifyObservers(); - - // *HACK: Do the 'show' logic for a new item in the inventory. - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); - if (active_panel) - { - active_panel->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO); - } -} - // static void LLInventoryModel::removeInventoryFolder(LLUUID agent_id, LLMessageSystem* msg) @@ -4071,10 +4094,20 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit) { gInventory.updateCategory(*cit); + + // Temporary workaround: just fetch the item using AIS to get missing fields. + // If this works fine we might want to extract ids only from the message + // then use AIS as a primary fetcher + AISAPI::FetchCategoryChildren((*cit)->getUUID(), AISAPI::INVENTORY); } for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit) { gInventory.updateItem(*iit); + + // Temporary workaround: just fetch the item using AIS to get missing fields. + // If this works fine we might want to extract ids only from the message + // then use AIS as a primary fetcher + AISAPI::FetchItem((*iit)->getUUID(), AISAPI::INVENTORY); } // [SL:KB] - Patch: UI-Notifications | Checked: Catznip-6.5 gInventory.notifyObservers(tid); diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 06386c1765..497aefd63f 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -333,24 +333,25 @@ public: // Find //-------------------------------------------------------------------- public: + + // Checks if category exists ("My Inventory" only), if it does not, creates it + void ensureCategoryForTypeExists(LLFolderType::EType preferred_type); + const LLUUID findCategoryUUIDForTypeInRoot( LLFolderType::EType preferred_type, - bool create_folder, - const LLUUID& root_id); + const LLUUID& root_id) const; // Returns the uuid of the category that specifies 'type' as what it // defaults to containing. The category is not necessarily only for that type. // NOTE: If create_folder is true, this will create a new inventory category // on the fly if one does not exist. *NOTE: if find_in_library is true it // will search in the user's library folder instead of "My Inventory" - const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder = true); + const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type) const; // will search in the user's library folder instead of "My Inventory" - const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder = true); + const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const; // Returns user specified category for uploads, returns default id if there are no // user specified one or it does not exist, creates default category if it is missing. - const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type); + const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const; // Get whatever special folder this object is a child of, if any. const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; @@ -456,13 +457,14 @@ public: const LLUUID& new_parent_id, BOOL restamp); + // Marks links from a "possibly" broken list for a rebuild + // clears the list + void rebuildBrockenLinks(); + //-------------------------------------------------------------------- // Delete //-------------------------------------------------------------------- public: - - // Update model after an AISv3 update received for any operation. - void onAISUpdateReceived(const std::string& context, const LLSD& update); // Update model after an item is confirmed as removed from // server. Works for categories or items. @@ -527,7 +529,7 @@ public: LLUUID findCategoryByName(std::string name); // Returns the UUID of the new category. If you want to use the default // name based on type, pass in a NULL to the 'name' parameter. - LLUUID createNewCategory(const LLUUID& parent_id, + void createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& name, inventory_func_type callback = NULL); @@ -633,6 +635,8 @@ private: U32 mModifyMaskBacklog; changed_items_t mChangedItemIDsBacklog; changed_items_t mAddedItemIDsBacklog; + changed_items_t mPossiblyBrockenLinks; + boost::signals2::connection mBulckFecthCallbackSlot; // [SL:KB] - Patch: UI-Notifications | Checked: Catznip-6.5 LLUUID mTransactionId; @@ -725,7 +729,6 @@ public: static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**); static void removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg, const char* msg_label); static void processRemoveInventoryItem(LLMessageSystem* msg, void**); - static void processUpdateInventoryFolder(LLMessageSystem* msg, void**); static void removeInventoryFolder(LLUUID agent_id, LLMessageSystem* msg); static void processRemoveInventoryFolder(LLMessageSystem* msg, void**); static void processRemoveInventoryObjects(LLMessageSystem* msg, void**); diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 9b5172c7ba..5c86dce610 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llinventorymodelbackgroundfetch.h" +#include "llaisapi.h" #include "llagent.h" #include "llappviewer.h" #include "llcallbacklist.h" @@ -188,12 +189,12 @@ const char * const LOG_INV("Inventory"); ///---------------------------------------------------------------------------- LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch(): - mBackgroundFetchActive(FALSE), + mBackgroundFetchActive(false), mFolderFetchActive(false), mFetchCount(0), - mAllFoldersFetched(FALSE), - mRecursiveInventoryFetchStarted(FALSE), - mRecursiveLibraryFetchStarted(FALSE), + mAllFoldersFetched(false), + mRecursiveInventoryFetchStarted(false), + mRecursiveLibraryFetchStarted(false), mMinTimeBetweenFetches(0.3f) {} @@ -245,17 +246,19 @@ BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const return mFolderFetchActive; } -void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category) +void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, bool recursive, bool is_category) { - mFetchQueue.push_front(FetchQueueInfo(id, recursive, is_category)); + ERecursionType recursion_type = recursive ? RT_RECURSIVE : RT_NONE; + mFetchQueue.push_front(FetchQueueInfo(id, recursion_type, is_category)); } -void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category) +void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, bool recursive, bool is_category) { - mFetchQueue.push_back(FetchQueueInfo(id, recursive, is_category)); + ERecursionType recursion_type = recursive ? RT_RECURSIVE : RT_NONE; + mFetchQueue.push_back(FetchQueueInfo(id, recursion_type, is_category)); } -void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) +void LLInventoryModelBackgroundFetch::start(const LLUUID& id, bool recursive) { LLViewerInventoryCategory * cat(gInventory.getCategory(id)); @@ -264,31 +267,52 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) // it's a folder, do a bulk fetch LL_DEBUGS(LOG_INV) << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL; - mBackgroundFetchActive = TRUE; + mBackgroundFetchActive = true; mFolderFetchActive = true; + ERecursionType recursion_type = recursive ? RT_RECURSIVE : RT_NONE; if (id.isNull()) { if (! mRecursiveInventoryFetchStarted) { mRecursiveInventoryFetchStarted |= recursive; - mFetchQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursive)); + if (recursive && AISAPI::isAvailable()) + { + // Not only root folder can be massive, but + // most system folders will be requested independently + // so request root folder and content separately + mFetchQueue.push_front(FetchQueueInfo(gInventory.getRootFolderID(), RT_CONTENT)); + } + else + { + mFetchQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursion_type)); + } gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); } if (! mRecursiveLibraryFetchStarted) { mRecursiveLibraryFetchStarted |= recursive; - mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursive)); + mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursion_type)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); } } else { - // Specific folder requests go to front of queue. - if (mFetchQueue.empty() || mFetchQueue.front().mUUID != id) - { - mFetchQueue.push_front(FetchQueueInfo(id, recursive)); - gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); - } + if (mFetchQueue.empty() || mFetchQueue.front().mUUID != id) + { + if (AISAPI::isAvailable()) + { + // On AIS make sure root goes to the top and follow up recursive + // fetches, not individual requests + mFetchQueue.push_back(FetchQueueInfo(id, recursion_type)); + } + else + { + // Specific folder requests go to front of queue. + mFetchQueue.push_front(FetchQueueInfo(id, recursion_type)); + } + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + if (id == gInventory.getLibraryRootFolderID()) { mRecursiveLibraryFetchStarted |= recursive; @@ -303,9 +327,9 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) { if (! itemp->mIsComplete && (mFetchQueue.empty() || mFetchQueue.front().mUUID != id)) { - mBackgroundFetchActive = TRUE; + mBackgroundFetchActive = true; - mFetchQueue.push_front(FetchQueueInfo(id, false, false)); + mFetchQueue.push_front(FetchQueueInfo(id, RT_NONE, false)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); } } @@ -313,9 +337,9 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) void LLInventoryModelBackgroundFetch::findLostItems() { - mBackgroundFetchActive = TRUE; + mBackgroundFetchActive = true; mFolderFetchActive = true; - mFetchQueue.push_back(FetchQueueInfo(LLUUID::null, TRUE)); + mFetchQueue.push_back(FetchQueueInfo(LLUUID::null, RT_RECURSIVE)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); } @@ -324,15 +348,23 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched() if (mRecursiveInventoryFetchStarted && mRecursiveLibraryFetchStarted) { - mAllFoldersFetched = TRUE; + mAllFoldersFetched = true; //LL_INFOS(LOG_INV) << "All folders fetched, validating" << LL_ENDL; //gInventory.validate(); + + // For now only informs about initial fetch being done + mAllFoldersFetchedSignal(); } mFolderFetchActive = false; mBackgroundFetchActive = false; LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL; } +boost::signals2::connection LLInventoryModelBackgroundFetch::setAllFoldersFetchedCallback(folders_fetched_callback_t cb) +{ + return mAllFoldersFetchedSignal.connect(cb); +} + void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) { LLInventoryModelBackgroundFetch::instance().backgroundFetch(); @@ -348,7 +380,15 @@ void LLInventoryModelBackgroundFetch::backgroundFetch() if (use_http_inventory()) // { - bulkFetch(); + if (AISAPI::isAvailable()) + { + bulkFetchViaAis(); + } + else + { + // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. + bulkFetch(); + } return; } @@ -517,8 +557,163 @@ void LLInventoryModelBackgroundFetch::incrFetchCount(S32 fetching) } } +void ais_simple_callback(const LLUUID& inv_id) +{ + LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); +} + +void LLInventoryModelBackgroundFetch::onAISCalback(const LLUUID &request_id, const LLUUID &response_id, ERecursionType recursion) +{ + incrFetchCount(-1); + if (response_id.isNull()) // Failure + { + if (recursion == RT_RECURSIVE) + { + // A full recursive request failed. + // Try requesting folder and nested content separately + mBackgroundFetchActive = true; + mFolderFetchActive = true; + mFetchQueue.push_front(FetchQueueInfo(request_id, RT_CONTENT)); + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + } + else + { + if (recursion == RT_CONTENT) + { + // Got the folder, now recursively request content + LLInventoryModel::cat_array_t * categories(NULL); + LLInventoryModel::item_array_t * items(NULL); + gInventory.getDirectDescendentsOf(request_id, categories, items); + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + mFetchQueue.push_front(FetchQueueInfo((*it)->getUUID(), RT_RECURSIVE)); + } + if (!mFetchQueue.empty()) + { + mBackgroundFetchActive = true; + mFolderFetchActive = true; + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + } + } +} + static LLTrace::BlockTimerStatHandle FTM_BULK_FETCH("Bulk Fetch"); +void LLInventoryModelBackgroundFetch::bulkFetchViaAis() +{ + LL_RECORD_BLOCK_TIME(FTM_BULK_FETCH); + //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. + if (gDisconnected) + { + return; + } + + static const S32 max_concurrent_fetches(50); + + if (mFetchCount >= max_concurrent_fetches) + { + return; + } + + // Don't loop for too long (in case of large, fully loaded inventory) + F64 curent_time = LLTimer::getTotalSeconds(); + const F64 max_time = 0.006f; // 6 ms + const F64 end_time = curent_time + max_time; + + while (!mFetchQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time) + { + const FetchQueueInfo & fetch_info(mFetchQueue.front()); + bulkFetchViaAis(fetch_info); + mFetchQueue.pop_front(); + curent_time = LLTimer::getTotalSeconds(); + } + + if (isBulkFetchProcessingComplete()) + { + setAllFoldersFetched(); + } +} + +void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetch_info) +{ + if (fetch_info.mIsCategory) + { + const LLUUID & cat_id(fetch_info.mUUID); + if (cat_id.isNull()) // Lost and found + { + AISAPI::FetchCategoryChildren(LLUUID::null, AISAPI::INVENTORY, false, ais_simple_callback); + mFetchCount++; + } + else + { + + const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); + if (cat) + { + if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) + { + if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) + { + AISAPI::FetchCategoryChildren(cat->getUUID(), AISAPI::LIBRARY, fetch_info.mRecursive == RT_RECURSIVE, ais_simple_callback); + } + else + { + LLUUID cat_id = cat->getUUID(); + ERecursionType type = fetch_info.mRecursive; + AISAPI::FetchCategoryChildren( + cat_id, + AISAPI::INVENTORY, + type == RT_RECURSIVE, + [cat_id, type](const LLUUID &response_id) + { + LLInventoryModelBackgroundFetch::instance().onAISCalback(cat_id, response_id, type); + }); + } + mFetchCount++; + } + else + { + // Already fetched, check if anything inside needs fetching + if (fetch_info.mRecursive) + { + LLInventoryModel::cat_array_t * categories(NULL); + LLInventoryModel::item_array_t * items(NULL); + gInventory.getDirectDescendentsOf(cat_id, categories, items); + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + // not push_front to not cause an infinite loop + mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive)); + } + } + } + } // else? + } + } + else + { + LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); + + if (itemp) + { + if (itemp->getPermissions().getOwner() == gAgent.getID()) + { + AISAPI::FetchItem(itemp->getUUID(), AISAPI::INVENTORY, ais_simple_callback); + } + else + { + AISAPI::FetchItem(itemp->getUUID(), AISAPI::LIBRARY, ais_simple_callback); + } + mFetchCount++; + } + } +} + // Bundle up a bunch of requests to send all at once. void LLInventoryModelBackgroundFetch::bulkFetch() { @@ -538,13 +733,6 @@ void LLInventoryModelBackgroundFetch::bulkFetch() // inventory more quickly. static const U32 max_batch_size(10); static const S32 max_concurrent_fetches(12); // Outstanding requests, not connections - static const F32 new_min_time(0.05f); // *HACK: Clean this up when old code goes away entirely. - - mMinTimeBetweenFetches = new_min_time; - if (mMinTimeBetweenFetches < new_min_time) - { - mMinTimeBetweenFetches = new_min_time; // *HACK: See above. - } if (mFetchCount) { @@ -564,8 +752,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch() // } - if ((mFetchCount > max_concurrent_fetches) || - (mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches)) + if (mFetchCount > max_concurrent_fetches) { return; } @@ -591,60 +778,61 @@ void LLInventoryModelBackgroundFetch::bulkFetch() if (fetch_info.mIsCategory) { const LLUUID & cat_id(fetch_info.mUUID); - if (cat_id.isNull()) //DEV-17797 + if (cat_id.isNull()) //DEV-17797 Lost and found { LLSD folder_sd; folder_sd["folder_id"] = LLUUID::null.asString(); folder_sd["owner_id"] = gAgent.getID(); folder_sd["sort_order"] = LLSD::Integer(sort_order); - folder_sd["fetch_folders"] = LLSD::Boolean(FALSE); - folder_sd["fetch_items"] = LLSD::Boolean(TRUE); + folder_sd["fetch_folders"] = LLSD::Boolean(false); + folder_sd["fetch_items"] = LLSD::Boolean(true); folder_request_body["folders"].append(folder_sd); folder_count++; } else { - const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); - - if (cat) - { - if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) - { - LLSD folder_sd; - folder_sd["folder_id"] = cat->getUUID(); - folder_sd["owner_id"] = cat->getOwnerID(); - folder_sd["sort_order"] = LLSD::Integer(sort_order); - folder_sd["fetch_folders"] = LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted; - folder_sd["fetch_items"] = LLSD::Boolean(TRUE); + const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); + if (cat) + { + if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) + { + LLSD folder_sd; + folder_sd["folder_id"] = cat->getUUID(); + folder_sd["owner_id"] = cat->getOwnerID(); + folder_sd["sort_order"] = LLSD::Integer(sort_order); + folder_sd["fetch_folders"] = LLSD::Boolean(true); //(LLSD::Boolean)sFullFetchStarted; + folder_sd["fetch_items"] = LLSD::Boolean(true); - // correct library owner for OpenSim (Rye) - // if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) - if (gInventory.getLibraryOwnerID() == cat->getOwnerID()) - // - { - folder_request_body_lib["folders"].append(folder_sd); - } - else - { - folder_request_body["folders"].append(folder_sd); - } - folder_count++; - } - - // May already have this folder, but append child folders to list. - if (fetch_info.mRecursive) - { - LLInventoryModel::cat_array_t * categories(NULL); - LLInventoryModel::item_array_t * items(NULL); - gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items); - for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); - it != categories->end(); - ++it) - { - mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive)); - } - } - } + // correct library owner for OpenSim (Rye) + // if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) + if (gInventory.getLibraryOwnerID() == cat->getOwnerID()) + // + { + folder_request_body_lib["folders"].append(folder_sd); + } + else + { + folder_request_body["folders"].append(folder_sd); + } + folder_count++; + } + else + { + // May already have this folder, but append child folders to list. + if (fetch_info.mRecursive) + { + LLInventoryModel::cat_array_t * categories(NULL); + LLInventoryModel::item_array_t * items(NULL); + gInventory.getDirectDescendentsOf(cat_id, categories, items); + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive)); + } + } + } + } } if (fetch_info.mRecursive) { diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h index efb6dc767d..b26e971831 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.h +++ b/indra/newview/llinventorymodelbackgroundfetch.h @@ -49,7 +49,7 @@ public: // Start and stop background breadth-first fetching of inventory contents. // This gets triggered when performing a filter-search. - void start(const LLUUID& cat_id = LLUUID::null, BOOL recursive = TRUE); + void start(const LLUUID& cat_id = LLUUID::null, bool recursive = true); BOOL folderFetchActive() const; bool isEverythingFetched() const; // completing the fetch once per session should be sufficient @@ -68,10 +68,36 @@ public: bool isBulkFetchProcessingComplete() const; void setAllFoldersFetched(); - void addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category); - void addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category); + typedef boost::function folders_fetched_callback_t; + boost::signals2::connection setAllFoldersFetchedCallback(folders_fetched_callback_t cb); + + void addRequestAtFront(const LLUUID & id, bool recursive, bool is_category); + void addRequestAtBack(const LLUUID & id, bool recursive, bool is_category); protected: + + typedef enum { + RT_NONE = 0, + RT_CONTENT, // request content recursively + RT_RECURSIVE, // request everything recursively + } ERecursionType; + struct FetchQueueInfo + { + FetchQueueInfo(const LLUUID& id, ERecursionType recursive, bool is_category = true) + : mUUID(id), + mIsCategory(is_category), + mRecursive(recursive) + {} + + LLUUID mUUID; + bool mIsCategory; + ERecursionType mRecursive; + }; + typedef std::deque fetch_queue_t; + + void onAISCalback(const LLUUID &request_id, const LLUUID &response_id, ERecursionType recursion); + void bulkFetchViaAis(); + void bulkFetchViaAis(const FetchQueueInfo& fetch_info); void bulkFetch(); void backgroundFetch(); @@ -80,37 +106,26 @@ protected: bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const; private: - BOOL mRecursiveInventoryFetchStarted; - BOOL mRecursiveLibraryFetchStarted; - BOOL mAllFoldersFetched; + bool mRecursiveInventoryFetchStarted; + bool mRecursiveLibraryFetchStarted; + bool mAllFoldersFetched; + typedef boost::signals2::signal folders_fetched_signal_t; + folders_fetched_signal_t mAllFoldersFetchedSignal; - BOOL mBackgroundFetchActive; + bool mBackgroundFetchActive; bool mFolderFetchActive; S32 mFetchCount; LLFrameTimer mFetchTimer; F32 mMinTimeBetweenFetches; - + fetch_queue_t mFetchQueue; + // For legacy inventory BOOL mTimelyFetchPending; S32 mNumFetchRetries; F32 mMaxTimeBetweenFetches; // - struct FetchQueueInfo - { - FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true) - : mUUID(id), - mIsCategory(is_category), - mRecursive(recursive) - {} - - LLUUID mUUID; - bool mIsCategory; - BOOL mRecursive; - }; - typedef std::deque fetch_queue_t; - fetch_queue_t mFetchQueue; }; #endif // LL_LLINVENTORYMODELBACKGROUNDFETCH_H diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index c75f01ab10..a8bd4f996c 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -817,7 +817,7 @@ LLUUID LLInventoryPanel::getRootFolderID() LLStringExplicit label(mParams.start_folder.name()); setLabel(label); - root_id = gInventory.findCategoryUUIDForType(preferred_type, false); + root_id = gInventory.findCategoryUUIDForType(preferred_type); if (root_id.isNull()) { LL_WARNS() << "Could not find folder of type " << preferred_type << LL_ENDL; @@ -2081,7 +2081,7 @@ void LLInventoryPanel::setSFViewAndOpenFolder(const LLInventoryPanel* panel, con LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); if (main_inventory && panel->hasAncestor(main_inventory) && !main_inventory->isSingleFolderMode()) { - main_inventory->onViewModeClick(); + main_inventory->toggleViewMode(); main_inventory->setSingleFolderViewRoot(folder_id, false); } } diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 09fa993fc6..18a3423ae9 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -701,10 +701,9 @@ void LLMarketplaceInventoryObserver::onIdleProcessQueue(void *userdata) // If it's a folder known to the marketplace, let's check it's in proper shape if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it)) { - LLInventoryCategory* cat = (LLInventoryCategory*)(obj); // can trigger notifyObservers // can cause more structural changes - validate_marketplacelistings(cat); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings(obj->getUUID()); } } else @@ -898,7 +897,7 @@ void LLMarketplaceData::setDataFetchedSignal(const status_updated_signal_t::slot // Get/Post/Put requests to the SLM Server using the SLM API void LLMarketplaceData::getSLMListings() { - const LLUUID marketplaceFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplaceFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); setUpdating(marketplaceFolderId, true); LLCoros::instance().launch("getSLMListings", @@ -1805,7 +1804,7 @@ bool LLMarketplaceData::isUpdating(const LLUUID& folder_id, S32 depth) } else { - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); std::set::iterator it = mPendingUpdateSet.find(marketplace_listings_uuid); if (it != mPendingUpdateSet.end()) { @@ -1849,8 +1848,7 @@ void LLMarketplaceData::decrementValidationWaiting(const LLUUID& folder_id, S32 if (found->second <= 0) { mValidationWaitingList.erase(found); - LLInventoryCategory *cat = gInventory.getCategory(folder_id); - validate_marketplacelistings(cat); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings(folder_id); update_marketplace_category(folder_id); gInventory.notifyObservers(); } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 0dc0f91f3e..55e9d12a23 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -550,8 +550,33 @@ void LLPanelMainInventory::newWindow() } } +//static void LLPanelMainInventory::newFolderWindow(LLUUID folder_id, LLUUID item_to_select) { + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end();) + { + LLFloaterSidePanelContainer* inventory_container = dynamic_cast(*iter++); + if (inventory_container) + { + LLSidepanelInventory* sidepanel_inventory = dynamic_cast(inventory_container->findChild("main_panel", true)); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory && main_inventory->isSingleFolderMode() + && (main_inventory->getSingleFolderViewRoot() == folder_id)) + { + main_inventory->setFocus(true); + if(item_to_select.notNull()) + { + sidepanel_inventory->getActivePanel()->setSelection(item_to_select, TAKE_FOCUS_YES); + } + return; + } + } + } + } + S32 instance_num = get_instance_num(); LLFloaterSidePanelContainer* inventory_container = LLFloaterReg::showTypedInstance("inventory", LLSD(instance_num)); @@ -563,7 +588,7 @@ void LLPanelMainInventory::newFolderWindow(LLUUID folder_id, LLUUID item_to_sele LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); if (main_inventory) { - main_inventory->onViewModeClick(); + main_inventory->toggleViewMode(); if(folder_id.notNull()) { main_inventory->setSingleFolderViewRoot(folder_id); @@ -1031,7 +1056,7 @@ void LLPanelMainInventory::updateFilterDropdown(const LLInventoryFilter* filter) void LLPanelMainInventory::onFilterSelected() { // Find my index - mActivePanel = (LLInventoryPanel*)getChild("inventory filter tabs")->getCurrentPanel(); + mActivePanel = mSingleFolderMode ? getChild("single_folder_inv") : (LLInventoryPanel*)getChild("inventory filter tabs")->getCurrentPanel(); if (!mActivePanel) { @@ -1057,6 +1082,14 @@ void LLPanelMainInventory::onFilterSelected() if (finder) { finder->changeFilter(&filter); + if (mSingleFolderMode) + { + const LLViewerInventoryCategory* cat = gInventory.getCategory(mSingleFolderPanelInventory->getSingleFolderRoot()); + if (cat) + { + finder->setTitle(cat->getName()); + } + } } if (filter.isActive()) { @@ -1262,6 +1295,15 @@ void LLPanelMainInventory::toggleFindOptions() parent_floater->addDependentFloater(mFinderHandle); // start background fetch of folders LLInventoryModelBackgroundFetch::instance().start(); + + if (mSingleFolderMode) + { + const LLViewerInventoryCategory* cat = gInventory.getCategory(mSingleFolderPanelInventory->getSingleFolderRoot()); + if (cat) + { + finder->setTitle(cat->getName()); + } + } } else { @@ -1766,7 +1808,7 @@ void LLPanelMainInventory::onAddButtonClick() } } -void LLPanelMainInventory::onViewModeClick() +void LLPanelMainInventory::toggleViewMode() { mSingleFolderMode = !mSingleFolderMode; @@ -1780,6 +1822,7 @@ void LLPanelMainInventory::onViewModeClick() mActivePanel = mSingleFolderMode ? getChild("single_folder_inv") : (LLInventoryPanel*)getChild("inventory filter tabs")->getCurrentPanel(); updateTitle(); + onFilterSelected(); LLSidepanelInventory* sidepanel_inventory = getParentSidepanelInventory(); if (sidepanel_inventory) @@ -1793,7 +1836,61 @@ void LLPanelMainInventory::onViewModeClick() sidepanel_inventory->toggleInbox(); } } +} +void LLPanelMainInventory::onViewModeClick() +{ + LLUUID selected_folder; + LLUUID new_root_folder; + if(mSingleFolderMode) + { + selected_folder = mSingleFolderPanelInventory->getSingleFolderRoot(); + } + else + { + LLFolderView* root = getActivePanel()->getRootFolder(); + std::set selection_set = root->getSelectionList(); + if (selection_set.size() == 1) + { + LLFolderViewItem* current_item = *selection_set.begin(); + if (current_item) + { + const LLUUID& id = static_cast(current_item->getViewModelItem())->getUUID(); + if(gInventory.getCategory(id) != NULL) + { + new_root_folder = id; + } + else + { + const LLViewerInventoryItem* selected_item = gInventory.getItem(id); + if (selected_item && selected_item->getParentUUID().notNull()) + { + new_root_folder = selected_item->getParentUUID(); + selected_folder = id; + } + } + } + } + } + + toggleViewMode(); + + if (mSingleFolderMode && new_root_folder.notNull()) + { + setSingleFolderViewRoot(new_root_folder, true); + if(selected_folder.notNull()) + { + getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES); + } + } + else + { + if(selected_folder.notNull()) + { + selectAllItemsPanel(); + getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES); + } + } } void LLPanelMainInventory::onUpFolderClicked() @@ -1828,6 +1925,11 @@ void LLPanelMainInventory::setSingleFolderViewRoot(const LLUUID& folder_id, bool } } +LLUUID LLPanelMainInventory::getSingleFolderViewRoot() +{ + return mSingleFolderPanelInventory->getSingleFolderRoot(); +} + void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name) { if (menu) @@ -1837,8 +1939,7 @@ void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_v LLView* spawning_view = getChild (spawning_view_name); S32 menu_x, menu_y; //show menu in co-ordinates of panel - spawning_view->localPointToOtherView(0, spawning_view->getRect().getHeight(), &menu_x, &menu_y, this); - menu_y += menu->getRect().getHeight(); + spawning_view->localPointToOtherView(0, 0, &menu_x, &menu_y, this); LLMenuGL::showPopup(this, menu, menu_x, menu_y); } } @@ -2468,6 +2569,11 @@ void LLPanelMainInventory::updateTitle() if (cat) { inventory_floater->setTitle(cat->getName()); + LLFloaterInventoryFinder *finder = getFinder(); + if (finder) + { + finder->setTitle(cat->getName()); + } } } else diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index 03fbef3373..e0c8ee1f06 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -107,10 +107,12 @@ public: void resetFilters(); void onViewModeClick(); + void toggleViewMode(); void onUpFolderClicked(); void onBackFolderClicked(); void onForwardFolderClicked(); void setSingleFolderViewRoot(const LLUUID& folder_id, bool clear_nav_history = true); + LLUUID getSingleFolderViewRoot(); bool isSingleFolderMode() { return mSingleFolderMode; } // Filter dropdown diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 798cbc9923..a7d9a91676 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -98,7 +98,7 @@ BOOL LLPanelOutfitsInventory::postBuild() // ( This is only necessary if we want to show a warning if a user deletes an item that has a // a link in an outfit, see "ConfirmItemDeleteHasLinks". ) - const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); if (outfits_cat.notNull()) { LLInventoryModelBackgroundFetch::instance().start(outfits_cat); diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 19cc4190bf..b3b0df7520 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -79,6 +79,8 @@ static const char * const INBOX_LAYOUT_PANEL_NAME = "inbox_layout_panel"; static const char * const INVENTORY_LAYOUT_STACK_NAME = "inventory_layout_stack"; static const char * const MARKETPLACE_INBOX_PANEL = "marketplace_inbox"; +static bool sLoginCompleted = false; + bool LLSidepanelInventory::sInboxInitalized = false; // Inbox panel randomly shown on secondary inventory windows // @@ -204,17 +206,31 @@ BOOL LLSidepanelInventory::postBuild() inbox_button->setCommitCallback(boost::bind(&LLSidepanelInventory::onToggleInboxBtn, this)); - // Get the previous inbox state from "InventoryInboxToggleState" setting. - bool is_inbox_collapsed = !inbox_button->getToggleState(); + // For main Inventory floater: Get the previous inbox state from "InventoryInboxToggleState" setting. + // For additional Inventory floaters: Collapsed state is default. + bool is_inbox_collapsed = !inbox_button->getToggleState();// || sLoginCompleted; // Show inbox on main inventory window only // Restore the collapsed inbox panel state mInboxLayoutPanel = getChild(INBOX_LAYOUT_PANEL_NAME); - inv_stack->collapsePanel(mInboxLayoutPanel, is_inbox_collapsed); - if (!is_inbox_collapsed) - { + inv_stack->collapsePanel(mInboxLayoutPanel, is_inbox_collapsed); + if (!is_inbox_collapsed) + { mInboxLayoutPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); - } + } + // Show inbox on main inventory window only + //if (sLoginCompleted) + //{ + // //save the state of Inbox panel only for main Inventory floater + // inbox_button->removeControlVariable(); + // inbox_button->setToggleState(false); + // updateInbox(); + //} + //else + //{ + // // Trigger callback for after login so we can setup to track inbox changes after initial inventory load + // LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSidepanelInventory::updateInbox, this)); + //} // Set the inbox visible based on debug settings (final setting comes from http request below) // FIRE-17603: Received Items button sometimes vanishing //enableInbox(gSavedSettings.getBOOL("InventoryDisplayInbox")); @@ -224,6 +240,7 @@ BOOL LLSidepanelInventory::postBuild() // Trigger callback for after login so we can setup to track inbox changes after initial inventory load LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSidepanelInventory::updateInbox, this)); + // // Optional hiding of Received Items folder aka Inbox gSavedSettings.getControl("FSShowInboxFolder")->getSignal()->connect(boost::bind(&LLSidepanelInventory::refreshInboxVisibility, this)); @@ -240,10 +257,11 @@ BOOL LLSidepanelInventory::postBuild() void LLSidepanelInventory::updateInbox() { + sLoginCompleted = true; // // Track inbox folder changes // - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, true); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); // Set up observer to listen for creation of inbox if it doesn't exist if (inbox_id.isNull()) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index dbaa2840d9..0510ab3dce 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2653,6 +2653,7 @@ bool idle_startup() LLNotificationsUtil::add("InventoryUnusable"); } + LLInventoryModelBackgroundFetch::instance().start(); gInventory.createCommonSystemCategories(); // It's debatable whether this flag is a good idea - sets all @@ -3696,7 +3697,7 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name, // Not going through the processAgentInitialWearables path, so need to set this here. LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true); // Initiate creation of COF, since we're also bypassing that. - gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CURRENT_OUTFIT); ESex gender; if (gender_name == "male") @@ -3873,6 +3874,11 @@ void reset_login() LLFloaterReg::hideVisibleInstances(); LLStartUp::setStartupState( STATE_BROWSER_INIT ); + if (LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->terminate(); + } + // Clear any verified certs and verify them again on next login // to ensure cert matches server instead of just getting reused LLPointer store = gSecAPIHandler->getCertificateStore(""); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 72a217c1dd..05a87c1d18 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -773,7 +773,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if (!handled) { // Disallow drag and drop to 3D from the marketplace - const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.notNull()) { for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) @@ -1780,7 +1780,7 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( return ACCEPT_NO; } - const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX); if(gInventory.isObjectDescendentOf(item->getUUID(), outbox_id)) { // Legacy @@ -2254,7 +2254,7 @@ EAcceptance LLToolDragAndDrop::dad3dWearCategory( return ACCEPT_NO; } - const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); + const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX); if(gInventory.isObjectDescendentOf(category->getUUID(), outbox_id)) { // Legacy diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 20955f787a..9d6488a0eb 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -74,6 +74,7 @@ // [RLVa:KB] - Checked: 2014-11-02 (RLVa-1.4.11) #include "rlvcommon.h" // [/RLVa:KB] +#include "llviewernetwork.h" // do-nothing ops for use in callbacks. void no_op_inventory_func(const LLUUID&) {} @@ -457,48 +458,66 @@ void LLViewerInventoryItem::fetchFromServer(void) const { if(!mIsComplete) { - std::string url; + if (AISAPI::isAvailable()) // AIS v 3 + { + if (gAgent.getID() != mPermissions.getOwner()) + { + AISAPI::FetchItem(mUUID, AISAPI::LIBRARY); + } + else + { + AISAPI::FetchItem(mUUID, AISAPI::INVENTORY); + } + } + else + { + std::string url; - LLViewerRegion* region = gAgent.getRegion(); - // we have to check region. It can be null after region was destroyed. See EXT-245 - if (region) - { - if (gAgent.getID() != mPermissions.getOwner()) - { - url = region->getCapability("FetchLib2"); - } - else - { - url = region->getCapability("FetchInventory2"); - } - } - else - { - LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL; - } + LLViewerRegion* region = gAgent.getRegion(); + // we have to check region. It can be null after region was destroyed. See EXT-245 + if (region) + { + if (gAgent.getID() != mPermissions.getOwner()) + { + url = region->getCapability("FetchLib2"); + } + else + { + url = region->getCapability("FetchInventory2"); + } + } + else + { + LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL; + } - if (!url.empty()) - { - LLSD body; - body["agent_id"] = gAgent.getID(); - body["items"][0]["owner_id"] = mPermissions.getOwner(); - body["items"][0]["item_id"] = mUUID; + if (!url.empty()) + { + LLSD body; + body["agent_id"] = gAgent.getID(); + body["items"][0]["owner_id"] = mPermissions.getOwner(); + body["items"][0]["item_id"] = mUUID; - LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body)); - gInventory.requestPost(true, url, body, handler, "Inventory Item"); - } - else - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("FetchInventory"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("OwnerID", mPermissions.getOwner()); - msg->addUUID("ItemID", mUUID); - gAgent.sendReliableMessage(); - } + LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body)); + gInventory.requestPost(true, url, body, handler, "Inventory Item"); + } + // OpenSim compatibility +#ifdef OPENSIM + else if (LLGridManager::instance().isInOpenSim()) // no cap + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("FetchInventory"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("InventoryData"); + msg->addUUID("OwnerID", mPermissions.getOwner()); + msg->addUUID("ItemID", mUUID); + gAgent.sendReliableMessage(); + } +#endif + // + } } } @@ -715,7 +734,7 @@ bool LLViewerInventoryCategory::fetch() { LL_WARNS(LOG_INV) << "agent region is null" << LL_ENDL; } - if (!url.empty()) //Capability found. Build up LLSD and use it. + if (!url.empty() || AISAPI::isAvailable()) { LLInventoryModelBackgroundFetch::instance().start(mUUID, false); } @@ -1100,13 +1119,18 @@ void create_notecard_cb(const LLUUID& inv_item) LLInventoryCallbackManager gInventoryCallbacks; -void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id, - const LLUUID& parent, const LLTransactionID& transaction_id, - const std::string& name, - const std::string& desc, LLAssetType::EType asset_type, - LLInventoryType::EType inv_type, U8 subtype, - U32 next_owner_perm, - LLPointer cb) +void create_inventory_item( + const LLUUID& agent_id, + const LLUUID& session_id, + const LLUUID& parent_id, + const LLTransactionID& transaction_id, + const std::string& name, + const std::string& desc, + LLAssetType::EType asset_type, + LLInventoryType::EType inv_type, + U8 subtype, + U32 next_owner_perm, + LLPointer cb) { //check if name is equal to one of special inventory items names //EXT-5839 @@ -1127,6 +1151,54 @@ void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id, } } +#ifdef USE_AIS_FOR_NC + // D567 18.03.2023 not yet implemented within AIS3 + if (AISAPI::isAvailable()) + { + LLSD new_inventory = LLSD::emptyMap(); + new_inventory["items"] = LLSD::emptyArray(); + + LLPermissions perms; + perms.init( + gAgentID, + gAgentID, + LLUUID::null, + LLUUID::null); + perms.initMasks( + PERM_ALL, + PERM_ALL, + PERM_NONE, + PERM_NONE, + next_owner_perm); + + LLUUID null_id; + LLPointer item = new LLViewerInventoryItem( + null_id, /*don't know yet*/ + parent_id, + perms, + null_id, /*don't know yet*/ + asset_type, + inv_type, + server_name, + desc, + LLSaleInfo(), + 0, + 0 /*don't know yet, whenever server creates it*/); + LLSD item_sd = item->asLLSD(); + new_inventory["items"].append(item_sd); + AISAPI::completion_t cr = boost::bind(&doInventoryCb, cb, _1); + AISAPI::CreateInventory( + parent_id, + new_inventory, + cr); + return; + } + else + { + LL_WARNS() << "AIS v3 not available" << LL_ENDL; + } +#endif + LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_CreateInventoryItem); msg->nextBlock(_PREHASH_AgentData); @@ -1134,7 +1206,7 @@ void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id, msg->addUUIDFast(_PREHASH_SessionID, session_id); msg->nextBlock(_PREHASH_InventoryBlock); msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb)); - msg->addUUIDFast(_PREHASH_FolderID, parent); + msg->addUUIDFast(_PREHASH_FolderID, parent_id); msg->addUUIDFast(_PREHASH_TransactionID, transaction_id); msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_perm); msg->addS8Fast(_PREHASH_Type, (S8)asset_type); @@ -1584,7 +1656,9 @@ void remove_inventory_item( gInventory.onObjectDeletedFromServer(item_id); } } - else // no cap + // OpenSim compatibility +#ifdef OPENSIM + else if (LLGridManager::instance().isInOpenSim()) // no cap { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_RemoveInventoryItem); @@ -1603,6 +1677,12 @@ void remove_inventory_item( cb->fire(item_id); } } +#endif + // + else + { + LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL; + } } else { @@ -1769,7 +1849,9 @@ void purge_descendents_of(const LLUUID& id, LLPointer cb) AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); AISAPI::PurgeDescendents(id, cr); } - else // no cap + // OpenSim compatibility +#ifdef OPENSIM + else if (LLGridManager::instance().isInOpenSim()) // no cap { // Fast purge LL_DEBUGS(LOG_INV) << "purge_descendents_of fast case " << cat->getName() << LL_ENDL; @@ -1791,6 +1873,12 @@ void purge_descendents_of(const LLUUID& id, LLPointer cb) cb->fire(id); } } +#endif + // + else + { + LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL; + } } } @@ -2066,9 +2154,21 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, parent_id = gInventory.getRootFolderID(); } - LLUUID category = gInventory.createNewCategory(parent_id, preferred_type, LLStringUtil::null); - gInventory.notifyObservers(); - panel->setSelectionByID(category, TRUE); + LLHandle handle = panel->getHandle(); + gInventory.createNewCategory( + parent_id, + preferred_type, + LLStringUtil::null, + [handle](const LLUUID &new_category_id) + { + gInventory.notifyObservers(); + LLInventoryPanel* panel = static_cast(handle.get()); + if (panel) + { + panel->setSelectionByID(new_category_id, TRUE); + } + } + ); } else if ("lsl" == type_name) { @@ -2271,11 +2371,13 @@ const LLUUID& LLViewerInventoryItem::getThumbnailUUID() const } if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK) { - return gInventory.getItem(getLinkedUUID())->getThumbnailUUID(); + LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID); + return linked_item ? linked_item->getThumbnailUUID() : LLUUID::null; } if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK_FOLDER) { - return gInventory.getCategory(getLinkedUUID())->getThumbnailUUID(); + LLViewerInventoryCategory *linked_cat = gInventory.getCategory(mAssetUUID); + return linked_cat ? linked_cat->getThumbnailUUID() : LLUUID::null; } return mThumbnailUUID; } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index c72f6f14ad..7e6da77eb2 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -483,14 +483,14 @@ void set_merchant_SLM_menu() LLCommand* command = LLCommandManager::instance().getCommand("marketplacelistings"); gToolBarView->enableCommand(command->id(), true); - const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.isNull()) { U32 mkt_status = LLMarketplaceData::instance().getSLMStatus(); bool is_merchant = (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) || (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT); if (is_merchant) { - gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_MARKETPLACE_LISTINGS); LL_WARNS("SLM") << "Creating the marketplace listings folder for a merchant" << LL_ENDL; } } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index b339b463c4..baba229881 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -7561,42 +7561,47 @@ void container_inventory_arrived(LLViewerObject* object, { // create a new inventory category to put this in LLUUID cat_id; - cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(), - LLFolderType::FT_NONE, - LLTrans::getString("AcquiredItems")); + gInventory.createNewCategory( + gInventory.getRootFolderID(), + LLFolderType::FT_NONE, + LLTrans::getString("AcquiredItems"), + [inventory](const LLUUID &new_cat_id) + { + LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); + LLInventoryObject::object_list_t::const_iterator end = inventory->end(); + for (; it != end; ++it) + { + if ((*it)->getType() != LLAssetType::AT_CATEGORY) + { + LLInventoryObject* obj = (LLInventoryObject*)(*it); + LLInventoryItem* item = (LLInventoryItem*)(obj); + LLUUID item_id; + item_id.generate(); + time_t creation_date_utc = time_corrected(); + LLPointer new_item + = new LLViewerInventoryItem(item_id, + new_cat_id, + item->getPermissions(), + item->getAssetUUID(), + item->getType(), + item->getInventoryType(), + item->getName(), + item->getDescription(), + LLSaleInfo::DEFAULT, + item->getFlags(), + creation_date_utc); + new_item->updateServer(TRUE); + gInventory.updateItem(new_item); + } + } + gInventory.notifyObservers(); - LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); - LLInventoryObject::object_list_t::const_iterator end = inventory->end(); - for ( ; it != end; ++it) - { - if ((*it)->getType() != LLAssetType::AT_CATEGORY) - { - LLInventoryObject* obj = (LLInventoryObject*)(*it); - LLInventoryItem* item = (LLInventoryItem*)(obj); - LLUUID item_id; - item_id.generate(); - time_t creation_date_utc = time_corrected(); - LLPointer new_item - = new LLViewerInventoryItem(item_id, - cat_id, - item->getPermissions(), - item->getAssetUUID(), - item->getType(), - item->getInventoryType(), - item->getName(), - item->getDescription(), - LLSaleInfo::DEFAULT, - item->getFlags(), - creation_date_utc); - new_item->updateServer(TRUE); - gInventory.updateItem(new_item); - } - } - gInventory.notifyObservers(); - if(active_panel) - { - active_panel->setSelection(cat_id, TAKE_FOCUS_NO); - } + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) + { + active_panel->setSelection(new_cat_id, TAKE_FOCUS_NO); + } + }); } else if (inventory->size() == 2) { diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b21e265e78..d43bb05eae 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3258,8 +3258,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("InterestList"); - capabilityNames.append("InventoryCategoryThumbnailUpload"); - capabilityNames.append("InventoryItemThumbnailUpload"); + capabilityNames.append("InventoryThumbnailUpload"); capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetExperiences"); capabilityNames.append("AgentExperiences"); diff --git a/indra/newview/particleeditor.cpp b/indra/newview/particleeditor.cpp index d3507a3f7b..84d59d5713 100644 --- a/indra/newview/particleeditor.cpp +++ b/indra/newview/particleeditor.cpp @@ -538,9 +538,19 @@ void ParticleEditor::onInjectButtonClicked() // if no #Firestorm folder was found, create one if (categoryID.isNull()) { - categoryID = gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER); + gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER, [this](const LLUUID& new_cat_id) + { + createScriptInventoryItem(new_cat_id); + }); } + else + { + createScriptInventoryItem(categoryID); + } +} +void ParticleEditor::createScriptInventoryItem(LLUUID categoryID) +{ // if still no #Firestorm folder was found, try to find the default "Scripts" folder if (categoryID.isNull()) { diff --git a/indra/newview/particleeditor.h b/indra/newview/particleeditor.h index ecf75ca801..c8f69e9710 100644 --- a/indra/newview/particleeditor.h +++ b/indra/newview/particleeditor.h @@ -58,6 +58,7 @@ class ParticleEditor : public LLFloater void updateUI(); std::string createScript(); + void createScriptInventoryItem(LLUUID categoryID); void onParameterChange(); void onCopyButtonClicked(); diff --git a/indra/newview/quickprefs.cpp b/indra/newview/quickprefs.cpp index 8cd211e4ee..301f7baf0a 100644 --- a/indra/newview/quickprefs.cpp +++ b/indra/newview/quickprefs.cpp @@ -63,7 +63,7 @@ class FSSettingsCollector : public LLInventoryCollectFunctor public: FSSettingsCollector() { - mMarketplaceFolderUUID = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + mMarketplaceFolderUUID = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); } virtual ~FSSettingsCollector() {} diff --git a/indra/newview/rlvinventory.cpp b/indra/newview/rlvinventory.cpp index 01915a7dd9..f8362411b9 100644 --- a/indra/newview/rlvinventory.cpp +++ b/indra/newview/rlvinventory.cpp @@ -479,12 +479,7 @@ void RlvRenameOnWearObserver::doneIdle() { // "No modify" item with a non-renameable parent: create a new folder named and move the item into it inventory_func_type f = boost::bind(RlvRenameOnWearObserver::onCategoryCreate, _1, pItem->getUUID()); - LLUUID idFolder = gInventory.createNewCategory(pFolder->getUUID(), LLFolderType::FT_NONE, strFolderName, f); - if (idFolder.notNull()) - { - // Not using the new 'CreateInventoryCategory' cap so manually invoke the callback - RlvRenameOnWearObserver::onCategoryCreate(idFolder, pItem->getUUID()); - } + gInventory.createNewCategory(pFolder->getUUID(), LLFolderType::FT_NONE, strFolderName, f); } } } @@ -532,9 +527,7 @@ bool RlvGiveToRLVOffer::createDestinationFolder(const std::string& strPath) else { inventory_func_type f = boost::bind(RlvGiveToRLVOffer::onCategoryCreateCallback, _1, this); - const LLUUID idTemp = gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, RLV_ROOT_FOLDER, f); - if (idTemp.notNull()) - onCategoryCreateCallback(idTemp, this); + gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, RLV_ROOT_FOLDER, f); } return true; } @@ -567,9 +560,7 @@ void RlvGiveToRLVOffer::onCategoryCreateCallback(LLUUID idFolder, RlvGiveToRLVOf { LLInventoryObject::correctInventoryName(strFolder); inventory_func_type f = boost::bind(RlvGiveToRLVOffer::onCategoryCreateCallback, _1, pInstance); - const LLUUID idTemp = gInventory.createNewCategory(idFolder, LLFolderType::FT_NONE, strFolder, f); - if (idTemp.notNull()) - onCategoryCreateCallback(idTemp, pInstance); + gInventory.createNewCategory(idFolder, LLFolderType::FT_NONE, strFolder, f); return; } } diff --git a/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml b/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml index 45ce8820e1..311d6b1ed1 100644 --- a/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml +++ b/indra/newview/skins/ansastorm/xui/en/panel_main_inventory.xml @@ -809,7 +809,9 @@ show_item_link_overlays="true" top="16" width="288" - scroll.reserve_scroll_corner="false"/> + scroll.reserve_scroll_corner="false"> + + + scroll.reserve_scroll_corner="false"> + + + scroll.reserve_scroll_corner="false"> + + + width="288" > + + + width="290" > + + + width="290" > + + + width="288" > + + + width="290" > + + + width="290" > + + + width="288" > + + + width="290" > + + + width="290" > + + + show_item_link_overlays="true" > + + + show_item_link_overlays="true" > + + + show_item_link_overlays="true" > + +