diff options
218 files changed, 18414 insertions, 6693 deletions
diff --git a/doc/contributions.txt b/doc/contributions.txt index 0ef3fee1a1..3de668c1f0 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -237,6 +237,7 @@ Ansariel Hiller  	SL-15227  	SL-15398  	SL-18432 +	SL-19140  	SL-4126  Aralara Rajal  Arare Chantilly @@ -894,6 +895,7 @@ Kitty Barnett  	STORM-2149  	MAINT-7581  	MAINT-7081 +    SL-18988  Kolor Fall  Komiko Okamoto  Korvel Noh diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt index e6ca2d753a..f3f822f61c 100644 --- a/indra/llappearance/CMakeLists.txt +++ b/indra/llappearance/CMakeLists.txt @@ -21,7 +21,6 @@ set(llappearance_SOURCE_FILES      lltexglobalcolor.cpp      lltexlayer.cpp      lltexlayerparams.cpp -    lltexturemanagerbridge.cpp      llwearable.cpp      llwearabledata.cpp      llwearabletype.cpp @@ -44,7 +43,6 @@ set(llappearance_HEADER_FILES      lltexglobalcolor.h      lltexlayer.h      lltexlayerparams.h -    lltexturemanagerbridge.h      llwearable.h      llwearabledata.h      llwearabletype.h diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp index 93d0a035da..9f23ce5317 100644 --- a/indra/llcommon/llcallbacklist.cpp +++ b/indra/llcommon/llcallbacklist.cpp @@ -55,7 +55,7 @@ void LLCallbackList::addFunction( callback_t func, void *data)  	// only add one callback per func/data pair  	// -	if (containsFunction(func)) +	if (containsFunction(func, data))  	{  		return;  	} diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 81261f0767..5adf1fa0e6 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -40,9 +40,12 @@  ///----------------------------------------------------------------------------  /// Exported functions  ///---------------------------------------------------------------------------- +// FIXME D567 - what's the point of these, especially if we don't even use them consistently?  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");  static const std::string INV_INVENTORY_TYPE_LABEL("inv_type"); @@ -99,6 +102,7 @@ void LLInventoryObject::copyObject(const LLInventoryObject* other)  	mParentUUID = other->mParentUUID;  	mType = other->mType;  	mName = other->mName; +	mThumbnailUUID = other->mThumbnailUUID;  }  const LLUUID& LLInventoryObject::getUUID() const @@ -111,6 +115,11 @@ const LLUUID& LLInventoryObject::getParentUUID() const  	return mParentUUID;  } +const LLUUID& LLInventoryObject::getThumbnailUUID() const +{ +	return mThumbnailUUID; +} +  const std::string& LLInventoryObject::getName() const  {  	return mName; @@ -160,6 +169,11 @@ void LLInventoryObject::setParent(const LLUUID& new_parent)  	mParentUUID = new_parent;  } +void LLInventoryObject::setThumbnailUUID(const LLUUID& thumbnail_uuid) +{ +	mThumbnailUUID = thumbnail_uuid; +} +  void LLInventoryObject::setType(LLAssetType::EType type)  {  	mType = type; @@ -201,6 +215,26 @@ BOOL LLInventoryObject::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("name", keyword))  		{  			//strcpy(valuestr, buffer + strlen(keyword) + 3); @@ -336,6 +370,7 @@ void LLInventoryItem::copyItem(const LLInventoryItem* other)  	copyObject(other);  	mPermissions = other->mPermissions;  	mAssetUUID = other->mAssetUUID; +    mThumbnailUUID = other->mThumbnailUUID;  	mDescription = other->mDescription;  	mSaleInfo = other->mSaleInfo;  	mInventoryType = other->mInventoryType; @@ -400,6 +435,7 @@ U32 LLInventoryItem::getCRC32() const  	//LL_DEBUGS() << "8 crc: " << std::hex << crc << std::dec << LL_ENDL;  	crc += (U32)mCreationDate;  	//LL_DEBUGS() << "9 crc: " << std::hex << crc << std::dec << LL_ENDL; +    crc += mThumbnailUUID.getCRC32();  	return crc;  } @@ -655,6 +691,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)); @@ -744,6 +800,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) @@ -797,6 +860,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())) @@ -848,6 +916,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: +            <key> asset_id </key> +            <uuid> acc0ec86 - 17f2 - 4b92 - ab41 - 6718b1f755f7 </uuid> +            <key> perms </key> +            <integer> 8 </integer> +            <key>service</key> +            <integer> 3 </integer> +            <key>version</key> +            <integer> 1 </key> +        */ +    } +    else +    { +        w = INV_THUMBNAIL_ID_LABEL; +        if (sd.has(w)) +        { +            mThumbnailUUID = sd[w].asUUID(); +        } +    }  	w = INV_PERMISSIONS_LABEL;  	if (sd.has(w))  	{ @@ -972,135 +1069,6 @@ fail:  } -// Deleted LLInventoryItem::exportFileXML() and LLInventoryItem::importXML() -// because I can't find any non-test code references to it. 2009-05-04 JC - -S32 LLInventoryItem::packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override) const -{ -	// Figure out which permissions to use. -	LLPermissions perm; -	if (perm_override) -	{ -		// Use the permissions override. -		perm = *perm_override; -	} -	else -	{ -		// Use the current permissions. -		perm = getPermissions(); -	} - -	// describe the inventory item -	char* buffer = (char*) bin_bucket; -	std::string creator_id_str; - -	perm.getCreator().toString(creator_id_str); -	std::string owner_id_str; -	perm.getOwner().toString(owner_id_str); -	std::string last_owner_id_str; -	perm.getLastOwner().toString(last_owner_id_str); -	std::string group_id_str; -	perm.getGroup().toString(group_id_str); -	std::string asset_id_str; -	getAssetUUID().toString(asset_id_str); -	S32 size = sprintf(buffer,	/* Flawfinder: ignore */ -					   "%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x", -					   getType(), -					   getInventoryType(), -					   getName().c_str(), -					   creator_id_str.c_str(), -					   owner_id_str.c_str(), -					   last_owner_id_str.c_str(), -					   group_id_str.c_str(), -					   perm.getMaskBase(), -					   perm.getMaskOwner(), -					   perm.getMaskGroup(), -					   perm.getMaskEveryone(), -					   perm.getMaskNextOwner(), -					   asset_id_str.c_str(), -					   getDescription().c_str(), -					   getSaleInfo().getSaleType(), -					   getSaleInfo().getSalePrice(), -					   getFlags()) + 1; - -	return size; -} - -void LLInventoryItem::unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size) -{	 -	// Early exit on an empty binary bucket. -	if (bin_bucket_size <= 1) return; - -	if (NULL == bin_bucket) -	{ -		LL_ERRS() << "unpackBinaryBucket failed.  bin_bucket is NULL." << LL_ENDL; -		return; -	} - -	// Convert the bin_bucket into a string. -	std::vector<char> item_buffer(bin_bucket_size+1); -	memcpy(&item_buffer[0], bin_bucket, bin_bucket_size);	/* Flawfinder: ignore */ -	item_buffer[bin_bucket_size] = '\0'; -	std::string str(&item_buffer[0]); - -	LL_DEBUGS() << "item buffer: " << str << LL_ENDL; - -	// Tokenize the string. -	typedef boost::tokenizer<boost::char_separator<char> > tokenizer; -	boost::char_separator<char> sep("|", "", boost::keep_empty_tokens); -	tokenizer tokens(str, sep); -	tokenizer::iterator iter = tokens.begin(); - -	// Extract all values. -	LLUUID item_id; -	item_id.generate(); -	setUUID(item_id); - -	LLAssetType::EType type; -	type = (LLAssetType::EType)(atoi((*(iter++)).c_str())); -	setType( type ); -	 -	LLInventoryType::EType inv_type; -	inv_type = (LLInventoryType::EType)(atoi((*(iter++)).c_str())); -	setInventoryType( inv_type ); - -	std::string name((*(iter++)).c_str()); -	rename( name ); -	 -	LLUUID creator_id((*(iter++)).c_str()); -	LLUUID owner_id((*(iter++)).c_str()); -	LLUUID last_owner_id((*(iter++)).c_str()); -	LLUUID group_id((*(iter++)).c_str()); -	PermissionMask mask_base = strtoul((*(iter++)).c_str(), NULL, 16); -	PermissionMask mask_owner = strtoul((*(iter++)).c_str(), NULL, 16); -	PermissionMask mask_group = strtoul((*(iter++)).c_str(), NULL, 16); -	PermissionMask mask_every = strtoul((*(iter++)).c_str(), NULL, 16); -	PermissionMask mask_next = strtoul((*(iter++)).c_str(), NULL, 16); -	LLPermissions perm; -	perm.init(creator_id, owner_id, last_owner_id, group_id); -	perm.initMasks(mask_base, mask_owner, mask_group, mask_every, mask_next); -	setPermissions(perm); -	//LL_DEBUGS() << "perm: " << perm << LL_ENDL; - -	LLUUID asset_id((*(iter++)).c_str()); -	setAssetUUID(asset_id); - -	std::string desc((*(iter++)).c_str()); -	setDescription(desc); -	 -	LLSaleInfo::EForSale sale_type; -	sale_type = (LLSaleInfo::EForSale)(atoi((*(iter++)).c_str())); -	S32 price = atoi((*(iter++)).c_str()); -	LLSaleInfo sale_info(sale_type, price); -	setSaleInfo(sale_info); -	 -	U32 flags = strtoul((*(iter++)).c_str(), NULL, 16); -	setFlags(flags); - -	time_t now = time(NULL); -	setCreationDate(now); -} -  ///----------------------------------------------------------------------------  /// Class LLInventoryCategory  ///---------------------------------------------------------------------------- @@ -1150,11 +1118,32 @@ void LLInventoryCategory::setPreferredType(LLFolderType::EType type)  LLSD LLInventoryCategory::asLLSD() const  {      LLSD sd = LLSD(); -    sd["item_id"] = mUUID; -    sd["parent_id"] = mParentUUID; +    sd[INV_ITEM_ID_LABEL]   = mUUID; +    sd[INV_PARENT_ID_LABEL] = mParentUUID;      S8 type = static_cast<S8>(mPreferredType); -    sd["type"]      = type; -    sd["name"] = mName; +    sd[INV_ASSET_TYPE_LABEL] = type; +    sd[INV_NAME_LABEL] = mName; + +    if (mThumbnailUUID.notNull()) +    { +        sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); +    } + +    return sd; +} + +LLSD LLInventoryCategory::asAISCreateCatLLSD() const +{ +    LLSD sd                 = LLSD(); +    sd[INV_FOLDER_ID_LABEL_WS]  = mUUID; +    sd[INV_PARENT_ID_LABEL] = mParentUUID; +    S8 type                 = static_cast<S8>(mPreferredType); +    sd[INV_ASSET_TYPE_LABEL_WS] = type; +    sd[INV_NAME_LABEL] = mName; +    if (mThumbnailUUID.notNull()) +    { +        sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); +    }      return sd;  } @@ -1184,6 +1173,25 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd)      {          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]; +        } +    } +    else +    { +        w = INV_THUMBNAIL_ID_LABEL; +        if (sd.has(w)) +        { +            mThumbnailUUID = sd[w]; +        } +    }      w = INV_ASSET_TYPE_LABEL;      if (sd.has(w))      { @@ -1275,6 +1283,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 @@ -1295,6 +1323,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;  } @@ -1308,6 +1342,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;  } @@ -1329,6 +1368,16 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data)  	{  		setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString()));  	} +    if (cat_data.has(INV_THUMBNAIL_LABEL)) +    { +        LLUUID thumbnail_uuid; +        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/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 7d9f9704f1..6d4535af27 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -70,6 +70,7 @@ public:  	virtual const LLUUID& getUUID() const; // inventoryID that this item points to  	virtual const LLUUID& getLinkedUUID() const; // inventoryID that this item points to, else this item's inventoryID  	const LLUUID& getParentUUID() const; +	virtual const LLUUID& getThumbnailUUID() const;  	virtual const std::string& getName() const;  	virtual LLAssetType::EType getType() const;  	LLAssetType::EType getActualType() const; // bypasses indirection for linked items @@ -84,6 +85,7 @@ public:  	void setUUID(const LLUUID& new_uuid);  	virtual void rename(const std::string& new_name);  	void setParent(const LLUUID& new_parent); +	virtual void setThumbnailUUID(const LLUUID& thumbnail_uuid);  	void setType(LLAssetType::EType type);  	virtual void setCreationDate(time_t creation_date_utc); // only stored for items @@ -108,6 +110,7 @@ public:  protected:  	LLUUID mUUID;  	LLUUID mParentUUID; // Parent category.  Root categories have LLUUID::NULL. +	LLUUID mThumbnailUUID;  	LLAssetType::EType mType;  	std::string mName;  	time_t mCreationDate; // seconds from 1/1/1970, UTC @@ -203,9 +206,6 @@ public:  	// Helper Functions  	//--------------------------------------------------------------------  public: -	// Pack all information needed to reconstruct this item into the given binary bucket. -	S32 packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override = NULL) const; -	void unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size);  	LLSD asLLSD() const;  	void asLLSD( LLSD& sd ) const;  	bool fromLLSD(const LLSD& sd, bool is_new = true); @@ -253,6 +253,7 @@ public:  	LLFolderType::EType getPreferredType() const;  	void setPreferredType(LLFolderType::EType type);  	LLSD asLLSD() const; +    LLSD asAISCreateCatLLSD() const;  	bool fromLLSD(const LLSD& sd);  	//-------------------------------------------------------------------- diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index e8b063bffe..039fa938dd 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -400,27 +400,7 @@ namespace tut  		// Deleted LLInventoryItem::exportFileXML() and LLInventoryItem::importXML()  		// because I can't find any non-test code references to it. 2009-05-04 JC  	} -		 -	template<> template<> -	void inventory_object::test<10>() -	{ -		LLPointer<LLInventoryItem> src1 = create_random_inventory_item(); -		U8* bin_bucket = new U8[300]; -		S32 bin_bucket_size = src1->packBinaryBucket(bin_bucket, NULL); - -		LLPointer<LLInventoryItem> src2 = new LLInventoryItem(); -		src2->unpackBinaryBucket(bin_bucket, bin_bucket_size); -		ensure_equals("1.sale price::getSalePrice() failed price", src1->getSaleInfo().getSalePrice(), src2->getSaleInfo().getSalePrice()); -		ensure_equals("2.sale type::getSaleType() failed type", src1->getSaleInfo().getSaleType(), src2->getSaleInfo().getSaleType()); -		ensure_equals("3.type::getType() failed", src1->getType(), src2->getType()); -		ensure_equals("4.inventory type::getInventoryType() failed type", src1->getInventoryType(), src2->getInventoryType()); -		ensure_equals("5.name::getName() failed", src1->getName(), src2->getName()); -		ensure_equals("6.description::getDescription() failed", src1->getDescription(), src2->getDescription());				 -		ensure_equals("7.flags::getFlags() failed", src1->getFlags(), src2->getFlags()); -	 -	} -	  	template<> template<>  	void inventory_object::test<11>()  	{ diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index d310cefd1e..ebbaea9b12 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -364,7 +364,26 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced  {      LLUUID id(LLUUID::generateNewID()); -    LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " << mPending << LL_ENDL; +    if (mPoolName == "AIS") +    { +        // Fetch is going to be spammy. +        LL_DEBUGS("CoProcMgr", "Inventory") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName +                                   << "\" at " +                              << mPending << LL_ENDL; + +        if (mPending >= (LLCoprocedureManager::DEFAULT_QUEUE_SIZE - 1)) +        { +            // If it's all used up (not supposed to happen, +            // fetched should cap it), we are going to crash +            LL_WARNS("CoProcMgr", "Inventory") << "About to run out of queue space for Coprocedure(" << name +                                               << ") enqueuing with id=" << id.asString() << " Already pending:" << mPending << LL_ENDL; +        } +    } +    else +    { +        LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " +                              << mPending << LL_ENDL; +    }      auto pushed = mPendingCoprocs->try_push(boost::make_shared<QueuedCoproc>(name, id, proc));      if (pushed == boost::fibers::channel_op_status::success)      { diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 19146c64f4..31acc65642 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -3402,6 +3402,7 @@ typedef std::map<const char*, LLMessageBuilder*> BuilderMap;  void LLMessageSystem::newMessageFast(const char *name)  { +	//LL_DEBUGS("Messaging") << "creating new message: " << name << LL_ENDL;  	LLMessageConfig::Flavor message_flavor =  		LLMessageConfig::getMessageFlavor(name);  	LLMessageConfig::Flavor server_flavor = diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index ea8947155e..fb4376c837 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -29,6 +29,7 @@ set(llrender_SOURCE_FILES      llrendertarget.cpp      llshadermgr.cpp      lltexture.cpp +    lltexturemanagerbridge.cpp      lluiimage.cpp      llvertexbuffer.cpp      llglcommonfunc.cpp @@ -58,6 +59,7 @@ set(llrender_HEADER_FILES      llrendersphere.h      llshadermgr.h      lltexture.h +    lltexturemanagerbridge.h      lluiimage.h      lluiimage.inl      llvertexbuffer.h diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 6f4f2ec62c..15fddbc99f 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -1024,6 +1024,20 @@ LLFontGL* LLFontGL::getFontSansSerifSmall()  }  //static +LLFontGL* LLFontGL::getFontSansSerifSmallBold() +{ +    static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",BOLD)); +    return fontp; +} + +//static +LLFontGL* LLFontGL::getFontSansSerifSmallItalic() +{ +    static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",ITALIC)); +    return fontp; +} + +//static  LLFontGL* LLFontGL::getFontSansSerif()  {  	static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Medium",0)); diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 93c6b78ce8..9bbf9ce0c4 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -189,6 +189,8 @@ public:  	static LLFontGL* getFontMonospace();  	static LLFontGL* getFontSansSerifSmall(); +    static LLFontGL* getFontSansSerifSmallBold(); +    static LLFontGL* getFontSansSerifSmallItalic();  	static LLFontGL* getFontSansSerif();  	static LLFontGL* getFontSansSerifBig();  	static LLFontGL* getFontSansSerifHuge(); diff --git a/indra/llappearance/lltexturemanagerbridge.cpp b/indra/llrender/lltexturemanagerbridge.cpp index 33f2185e4f..33f2185e4f 100644 --- a/indra/llappearance/lltexturemanagerbridge.cpp +++ b/indra/llrender/lltexturemanagerbridge.cpp diff --git a/indra/llappearance/lltexturemanagerbridge.h b/indra/llrender/lltexturemanagerbridge.h index 101704b162..f61433ea4d 100644 --- a/indra/llappearance/lltexturemanagerbridge.h +++ b/indra/llrender/lltexturemanagerbridge.h @@ -27,7 +27,6 @@  #ifndef LL_TEXTUREMANAGERBRIDGE_H  #define LL_TEXTUREMANAGERBRIDGE_H -#include "llavatarappearancedefines.h"  #include "llpointer.h"  #include "llgltexture.h" diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 3354cb2db3..49d275997a 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -203,7 +203,8 @@ LLButton::LLButton(const LLButton::Params& p)  	}  	// Hack to make sure there is space for at least one character -	if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) +	if (getRect().mRight >= 0 && getRect().getWidth() > 0 && +		getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))  	{  		// Use old defaults  		mLeftHPad = llbutton_orig_h_pad; diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 62c311f522..650ae9ae75 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -189,7 +189,9 @@ LLFolderView::LLFolderView(const Params& p)  	mStatusTextBox(NULL),  	mShowItemLinkOverlays(p.show_item_link_overlays),  	mViewModel(p.view_model), -    mGroupedItemModel(p.grouped_item_model) +    mGroupedItemModel(p.grouped_item_model), +    mForceArrange(false), +    mSingleFolderMode(false)  {      LLPanel* panel = p.parent_panel;      mParentPanel = panel->getHandle(); @@ -609,6 +611,7 @@ void LLFolderView::clearSelection()  	}  	mSelectedItems.clear(); +    mNeedsScroll = false;  }  std::set<LLFolderViewItem*> LLFolderView::getSelectionList() const @@ -665,7 +668,7 @@ void LLFolderView::draw()  	}  	else if (mShowEmptyMessage)  	{ -		mStatusTextBox->setValue(getFolderViewModel()->getStatusText()); +		mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty()));  		mStatusTextBox->setVisible( TRUE );  		// firstly reshape message textbox with current size. This is necessary to @@ -693,12 +696,16 @@ void LLFolderView::draw()  		}  	} -	if (mRenameItem && mRenamer && mRenamer->getVisible() && !getVisibleRect().overlaps(mRenamer->getRect())) -	{ -		// renamer is not connected to the item we are renaming in any form so manage it manually -		// TODO: consider stopping on any scroll action instead of when out of visible area -		finishRenamingItem(); -	} +    if (mRenameItem +        && mRenamer +        && mRenamer->getVisible() +        && !getVisibleRect().overlaps(mRenamer->getRect())) +    { +        // renamer is not connected to the item we are renaming in any form so manage it manually +        // TODO: consider stopping on any scroll action instead of when out of visible area +        LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL; +        finishRenamingItem(); +    }  	// skip over LLFolderViewFolder::draw since we don't want the folder icon, label,   	// and arrow for the root folder @@ -832,9 +839,12 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item )  	mAutoOpenItems.push(item);  	item->setOpen(TRUE); +    if(!item->isSingleFolderMode()) +    {  	LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());  	LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0);  	scrollToShowItem(item, constraint_rect); +    }  }  void LLFolderView::closeAutoOpenedFolders() @@ -1038,6 +1048,8 @@ void LLFolderView::paste()  // public rename functionality - can only start the process  void LLFolderView::startRenamingSelectedItem( void )  { +    LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL; +  	// make sure selection is visible  	scrollToShowSelection(); @@ -1273,6 +1285,11 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask )  		if(mSelectedItems.size())  		{  			LLFolderViewItem* last_selected = getCurSelectedItem(); +            if(last_selected && last_selected->isSingleFolderMode()) +            { +                handled = FALSE; +                break; +            }  			LLFolderViewItem* parent_folder = last_selected->getParentFolder();  			if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())  			{ @@ -1458,9 +1475,19 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )  			mCallbackRegistrar->popScope();  		}  	} + +    BOOL item_clicked = FALSE; +    for (selected_items_t::iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) +    { +        item_clicked |= (*item_it)->getRect().pointInRect(x, y); +    } +    if(!item_clicked && mSingleFolderMode) +    { +        clearSelection(); +    }  	bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected(); -	if (menu && (handled -		&& ( count > 0 && (hasVisibleChildren()) )) && // show menu only if selected items are visible +	if (menu && (mSingleFolderMode || (handled +		&& ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible  		!hide_folder_menu)  	{  		if (mCallbackRegistrar) @@ -1532,6 +1559,22 @@ BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )  	return LLView::handleHover( x, y, mask );  } +LLFolderViewItem* LLFolderView::getHoveredItem() const +{ +	return dynamic_cast<LLFolderViewItem*>(mHoveredItem.get()); +} + +void LLFolderView::setHoveredItem(LLFolderViewItem* itemp) +{ +	if (mHoveredItem.get() != itemp) +	{ +		if (itemp) +			mHoveredItem = itemp->getHandle(); +		else +			mHoveredItem.markDead(); +	} +} +  BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,  									 EDragAndDropType cargo_type,  									 void* cargo_data,  @@ -1716,7 +1759,7 @@ void LLFolderView::update()  		mNeedsAutoSelect = FALSE;  	} -  BOOL is_visible = isInVisibleChain(); +  BOOL is_visible = isInVisibleChain() || mForceArrange;    //Puts folders/items in proper positions    // arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849) @@ -1817,13 +1860,28 @@ void LLFolderView::update()          }  	} -	if (mSignalSelectCallback) -	{ -		//RN: we use keyboard focus as a proxy for user-explicit actions -		BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); -		mSelectSignal(mSelectedItems, take_keyboard_focus); -	} -	mSignalSelectCallback = FALSE; +    if (mSelectedItems.size()) +    { +        LLFolderViewItem* item = mSelectedItems.back(); +        // If the goal is to show renamer, don't callback untill +        // item is visible or is no longer being scrolled to. +        // Otherwise renamer will be instantly closed +        // Todo: consider moving renamer out of selection callback +        if (!mNeedsAutoRename || !mNeedsScroll || item->getVisible()) +        { +            if (mSignalSelectCallback) +            { +                //RN: we use keyboard focus as a proxy for user-explicit actions +                BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); +                mSelectSignal(mSelectedItems, take_keyboard_focus); +            } +            mSignalSelectCallback = FALSE; +        } +    } +    else +    { +        mSignalSelectCallback = FALSE; +    }  }  void LLFolderView::dumpSelectionInformation() @@ -1886,6 +1944,11 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu)  		flags = multi_select_flag;  	} +    if(mSingleFolderMode && (mSelectedItems.size() == 0)) +    { +        buildContextMenu(*menu, flags); +    } +  	// This adds a check for restrictions based on the entire  	// selection set - for example, any one wearable may not push you  	// over the limit, but all wearables together still might. @@ -2042,7 +2105,7 @@ LLFolderViewItem* LLFolderView::getNextUnselectedItem()  	return new_selection;  } -S32 LLFolderView::getItemHeight() +S32 LLFolderView::getItemHeight() const  {  	if(!hasVisibleChildren())  { diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index 7dfa04828a..6de366044c 100644 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -127,6 +127,9 @@ public:  	bool getAllowMultiSelect() { return mAllowMultiSelect; }  	bool getAllowDrag() { return mAllowDrag; } +    void setSingleFolderMode(bool is_single_mode) { mSingleFolderMode = is_single_mode; } +    bool isSingleFolderMode() { return mSingleFolderMode; } +  	// Close all folders in the view  	void closeAllFolders();  	void openTopLevelFolders(); @@ -136,7 +139,7 @@ public:  	// Find width and height of this object and its children. Also  	// makes sure that this view and its children are the right size.  	virtual S32 arrange( S32* width, S32* height ); -	virtual S32 getItemHeight(); +	virtual S32 getItemHeight() const;  	void arrangeAll() { mArrangeGeneration++; }  	S32 getArrangeGeneration() { return mArrangeGeneration; } @@ -144,6 +147,10 @@ public:  	// applies filters to control visibility of items  	virtual void filter( LLFolderViewFilter& filter); +	void              clearHoveredItem() { setHoveredItem(nullptr); } +	LLFolderViewItem* getHoveredItem() const; +	void              setHoveredItem(LLFolderViewItem* itemp); +  	// Get the last selected item  	virtual LLFolderViewItem* getCurSelectedItem( void );      selected_items_t& getSelectedItems( void ); @@ -237,11 +244,15 @@ public:  	void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; }  	void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; } +    void setForceArrange(bool force) { mForceArrange = force; } +  	LLPanel* getParentPanel() { return mParentPanel.get(); }  	// DEBUG only  	void dumpSelectionInformation();  	virtual S32	notify(const LLSD& info) ; + +	void setShowEmptyMessage(bool show_msg) { mShowEmptyMessage = show_msg; }  	bool useLabelSuffix() { return mUseLabelSuffix; }  	virtual void updateMenu(); @@ -275,6 +286,7 @@ protected:  	LLHandle<LLView>					mPopupMenuHandle;  	std::string						mMenuFileName; +	LLHandle<LLView>				mHoveredItem;  	selected_items_t				mSelectedItems;  	bool							mKeyboardSelection,  									mAllowMultiSelect, @@ -291,7 +303,8 @@ protected:  									mShowItemLinkOverlays,  									mShowSelectionContext,  									mShowSingleSelection, -									mSuppressFolderMenu; +									mSuppressFolderMenu, +                                    mSingleFolderMode;  	// Renaming variables and methods  	LLFolderViewItem*				mRenameItem;  // The item currently being renamed @@ -330,6 +343,8 @@ protected:  	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar;  	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar; + +    bool mForceArrange;  public:  	static F32 sAutoOpenTime; diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 5319af1b60..0dc66bf37a 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -32,6 +32,7 @@  #include "llfolderview.h"  #include "llfolderviewmodel.h"  #include "llpanel.h" +#include "llcallbacklist.h"  #include "llcriticaldamp.h"  #include "llclipboard.h"  #include "llfocusmgr.h"		// gFocusMgr @@ -113,6 +114,8 @@ LLFolderViewItem::Params::Params()      icon_width("icon_width", 0),      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)  { } @@ -151,7 +154,9 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)      mTextPad(p.text_pad),      mTextPadRight(p.text_pad_right),      mArrowSize(p.arrow_size), -    mMaxFolderItemOverlap(p.max_folder_item_overlap) +    mSingleFolderMode(p.single_folder_mode), +    mMaxFolderItemOverlap(p.max_folder_item_overlap), +    mDoubleClickOverride(p.double_click_override)  {  	if (!sColorSetInitialized)  	{ @@ -162,7 +167,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)  		sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);  		sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);  		sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); -		sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); +		sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE);  		sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);  		sColorSetInitialized = true;  	} @@ -396,7 +401,7 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height )              // it is purely visual, so it is fine to do at our laisure              refreshSuffix();          } -		mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight; +		mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(LLFontGL::NORMAL)->getWidth(mLabelSuffix) + mLabelPaddingRight;  		mLabelWidthDirty = false;  	} @@ -413,7 +418,7 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height )  	return *height;  } -S32 LLFolderViewItem::getItemHeight() +S32 LLFolderViewItem::getItemHeight() const  {  	return mItemHeight;  } @@ -625,11 +630,14 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )  			getWindow()->setCursor(UI_CURSOR_NOLOCKED);  		} +		root->clearHoveredItem();  		return TRUE;  	}  	else  	{ -		getRoot()->setShowSelectionContext(FALSE); +		LLFolderView* pRoot = getRoot(); +		pRoot->setHoveredItem(this); +		pRoot->setShowSelectionContext(FALSE);  		getWindow()->setCursor(UI_CURSOR_ARROW);  		// let parent handle this then...  		return FALSE; @@ -684,6 +692,13 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )  void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask)  {  	mIsMouseOverTitle = false; + +	// NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it +	LLFolderView* pRoot = getRoot(); +	if (this == pRoot->getHoveredItem()) +	{ +		pRoot->clearHoveredItem(); +	}  }  BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, @@ -890,7 +905,10 @@ void LLFolderViewItem::draw()      getViewModelItem()->update(); -    drawOpenFolderArrow(default_params, sFgColor); +    if(!mSingleFolderMode) +    { +        drawOpenFolderArrow(default_params, sFgColor); +    }      drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); @@ -926,16 +944,43 @@ void LLFolderViewItem::draw()  	F32 text_left = (F32)getLabelXPos();  	std::string combined_string = mLabel + mLabelSuffix; +    const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL); +    S32 filter_offset = mViewModelItem->getFilterStringOffset();  	if (filter_string_length > 0)  	{ -		S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2; +        S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); +        S32 top = getRect().getHeight() - TOP_PAD; +        if(mLabelSuffix.empty() || (font == suffix_font)) +        { +        S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2;  		S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2; -		S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); -		S32 top = getRect().getHeight() - TOP_PAD;  		LLUIImage* box_image = default_params.selection_image;  		LLRect box_rect(left, top, right, bottom);  		box_image->draw(box_rect, sFilterBGColor); +        } +        else +        { +            S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); +            if(label_filter_length > 0) +            { +                S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, llmin(filter_offset, (S32)mLabel.size())) - 2; +                S32 right = left + font->getWidthF32(mLabel, filter_offset, label_filter_length) + 2; +                LLUIImage* box_image = default_params.selection_image; +                LLRect box_rect(left, top, right, bottom); +                box_image->draw(box_rect, sFilterBGColor); +            } +            S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; +            if(suffix_filter_length > 0) +            { +                S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); +                S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2; +                S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2; +                LLUIImage* box_image = default_params.selection_image; +                LLRect box_rect(left, top, right, bottom); +                box_image->draw(box_rect, sFilterBGColor); +            } +        }      }      LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor; @@ -952,7 +997,7 @@ void LLFolderViewItem::draw()  	//  	if (!mLabelSuffix.empty())  	{ -		font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor, +        suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,  						  LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,  						  S32_MAX, S32_MAX, &right_x, FALSE );  	} @@ -962,12 +1007,35 @@ void LLFolderViewItem::draw()  	//      if (filter_string_length > 0)      { -        S32 filter_offset = mViewModelItem->getFilterStringOffset(); -        F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length); -        F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; -        font->renderUTF8( combined_string, filter_offset, match_string_left, yy, +        if(mLabelSuffix.empty() || (font == suffix_font)) +        { +            F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length); +            F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; +            font->renderUTF8( combined_string, filter_offset, match_string_left, yy,              sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,              filter_string_length, S32_MAX, &right_x, FALSE ); +        } +        else +        { +            S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); +            if(label_filter_length > 0) +            { +                F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length); +                F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; +                font->renderUTF8( mLabel, filter_offset, match_string_left, yy, + sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x, FALSE ); +            } +             +            S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; +            if(suffix_filter_length > 0) +            { +                S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); +                F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length); +                F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; +                suffix_font->renderUTF8( mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x, FALSE ); +            } +        } +      }      //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to @@ -1278,7 +1346,7 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem  			child_selected = TRUE;  		}  	} -	if(openitem && child_selected) +	if(openitem && child_selected && !mSingleFolderMode)  	{  		setOpenArrangeRecursively(TRUE);  	} @@ -1703,6 +1771,11 @@ BOOL LLFolderViewFolder::isRemovable()  	return TRUE;  } +void LLFolderViewFolder::destroyRoot() +{ +    delete this; +} +  // this is an internal method used for adding items to folders.   void LLFolderViewFolder::addItem(LLFolderViewItem* item)  { @@ -1771,7 +1844,19 @@ void LLFolderViewFolder::toggleOpen()  // Force a folder open or closed  void LLFolderViewFolder::setOpen(BOOL openitem)  { -	setOpenArrangeRecursively(openitem); +    if(mSingleFolderMode) +    { +        // navigateToFolder can destroy this view +        // delay it in case setOpen was called from click or key processing +        doOnIdleOneTime([this]() +                        { +                            getViewModelItem()->navigateToFolder(); +                        }); +    } +    else +    { +        setOpenArrangeRecursively(openitem); +    }  }  void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) @@ -1974,7 +2059,8 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )  	}  	if( !handled )  	{ -		if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) +		if((mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) +           && !mSingleFolderMode)  		{  			toggleOpen();  			handled = TRUE; @@ -1992,12 +2078,45 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )  BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )  {  	BOOL handled = FALSE; +    if(mSingleFolderMode) +    { +        static LLUICachedControl<bool> double_click_new_window("SingleModeDoubleClickOpenWindow", false); +        if (double_click_new_window) +        { +            getViewModelItem()->navigateToFolder(true); +        } +        else +        { +            // navigating is going to destroy views and change children +            // delay it untill handleDoubleClick processing is complete +            doOnIdleOneTime([this]() +                            { +                                getViewModelItem()->navigateToFolder(false); +                            }); +        } +        return TRUE; +    } +  	if( isOpen() )  	{  		handled = childrenHandleDoubleClick( x, y, mask ) != NULL;  	}  	if( !handled )  	{ +        if(mDoubleClickOverride) +        { +            static LLUICachedControl<U32> 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)  		{  			// don't select when user double-clicks plus sign diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index a5157266c5..5c2a1ecff0 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -72,6 +72,8 @@ public:                                                      text_pad_right,                                                      arrow_size,                                                      max_folder_item_overlap; +        Optional<bool>                              single_folder_mode, +                                                    double_click_override;  		Params();  	}; @@ -121,6 +123,8 @@ protected:  								mIsMouseOverTitle,  								mAllowWear,                                  mAllowDrop, +                                mSingleFolderMode, +                                mDoubleClickOverride,  								mSelectPending,  								mIsItemCut; @@ -174,7 +178,7 @@ public:  	// Finds width and height of this object and it's children.  Also  	// makes sure that this view and it's children are the right size.  	virtual S32 arrange( S32* width, S32* height ); -	virtual S32 getItemHeight(); +	virtual S32 getItemHeight() const;      virtual S32 getLabelXPos();      S32 getIconPad();      S32 getTextPad(); @@ -213,9 +217,9 @@ public:  	void setIsCurSelection(BOOL select) { mIsCurSelection = select; } -	BOOL getIsCurSelection() { return mIsCurSelection; } +	BOOL getIsCurSelection() const { return mIsCurSelection; } -	BOOL hasVisibleChildren() { return mHasVisibleChildren; } +	BOOL hasVisibleChildren() const { return mHasVisibleChildren; }  	// true if object can't have children  	virtual bool isFolderComplete() { return true; } @@ -264,7 +268,7 @@ public:  	virtual LLFolderView*	getRoot();  	virtual const LLFolderView*	getRoot() const;  	BOOL			isDescendantOf( const LLFolderViewFolder* potential_ancestor ); -	S32				getIndentation() { return mIndentation; } +	S32				getIndentation() const { return mIndentation; }  	virtual BOOL	passedFilter(S32 filter_generation = -1);  	virtual BOOL	isPotentiallyVisible(S32 filter_generation = -1); @@ -277,6 +281,8 @@ public:      // Does not need filter update  	virtual void refreshSuffix(); +    bool isSingleFolderMode() { return mSingleFolderMode; } +  	// LLView functionality  	virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );  	virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); @@ -387,6 +393,7 @@ public:  	// destroys this folder, and all children  	virtual void destroyView(); +    void destroyRoot();      // whether known children are fully loaded (arrange sets to true)      virtual bool isFolderComplete() { return mIsFolderComplete; } diff --git a/indra/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp index 93122503d1..f217b743a0 100644 --- a/indra/llui/llfolderviewmodel.cpp +++ b/indra/llui/llfolderviewmodel.cpp @@ -34,7 +34,7 @@ bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item)  	return item->getSortVersion() < mTargetSortVersion;  } -std::string LLFolderViewModelCommon::getStatusText() +std::string LLFolderViewModelCommon::getStatusText(bool is_empty_folder)  {  	if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration())  	{ @@ -42,7 +42,7 @@ std::string LLFolderViewModelCommon::getStatusText()  	}  	else  	{ -		return getFilter().getEmptyLookupMessage(); +		return getFilter().getEmptyLookupMessage(is_empty_folder);  	}  } diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index c5e027d314..551a60e097 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -69,7 +69,7 @@ public:  	virtual bool				checkFolder(const LLFolderViewModelItem* folder) const = 0;  	virtual void 				setEmptyLookupMessage(const std::string& message) = 0; -	virtual std::string			getEmptyLookupMessage() const = 0; +    virtual std::string			getEmptyLookupMessage(bool is_empty_folder = false) const = 0;  	virtual bool				showAllResults() const = 0; @@ -125,7 +125,7 @@ public:  	virtual void setFolderView(LLFolderView* folder_view) = 0;  	virtual LLFolderViewFilter& getFilter() = 0;  	virtual const LLFolderViewFilter& getFilter() const = 0; -	virtual std::string getStatusText() = 0; +	virtual std::string getStatusText(bool is_empty_folder = false) = 0;  	virtual bool startDrag(std::vector<LLFolderViewModelItem*>& items) = 0;  }; @@ -159,6 +159,8 @@ public:  	virtual void openItem( void ) = 0;  	virtual void closeItem( void ) = 0;  	virtual void selectItem(void) = 0; + +    virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0;      virtual BOOL isItemWearable() const { return FALSE; } @@ -392,7 +394,7 @@ public:  		// sort everything  		mTargetSortVersion++;  	} -	virtual std::string getStatusText(); +	virtual std::string getStatusText(bool is_empty_folder = false);  	virtual void filter();  	void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index 5d6c544571..e983d63a01 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -39,7 +39,9 @@ class LLUICtrlFactory;  // Classes  // -//  +// Class for diplaying named UI textures +// Do not use for displaying textures from network, +// UI textures are stored permanently!  class LLIconCtrl  : public LLUICtrl  { diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 7b22f9dbdc..7e4e828a88 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -216,7 +216,8 @@ LLLayoutStack::Params::Params()  	drag_handle_first_indent("drag_handle_first_indent", 0),  	drag_handle_second_indent("drag_handle_second_indent", 0),  	drag_handle_thickness("drag_handle_thickness", 5), -	drag_handle_shift("drag_handle_shift", 2) +	drag_handle_shift("drag_handle_shift", 2), +    drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody"))  {  	addSynonym(border_size, "drag_handle_gap");  } @@ -236,7 +237,8 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)  	mDragHandleFirstIndent(p.drag_handle_first_indent),  	mDragHandleSecondIndent(p.drag_handle_second_indent),  	mDragHandleThickness(p.drag_handle_thickness), -	mDragHandleShift(p.drag_handle_shift) +	mDragHandleShift(p.drag_handle_shift), +    mDragHandleColor(p.drag_handle_color())  {  } @@ -523,6 +525,15 @@ void LLLayoutStack::updateLayout()  	mNeedsLayout = continue_animating;  } // end LLLayoutStack::updateLayout +void LLLayoutStack::setPanelSpacing(S32 val) +{ +    if (mPanelSpacing != val) +    { +        mPanelSpacing = val; +        mNeedsLayout = true; +    } +} +  LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const  {  	if (!panelp) return NULL; @@ -576,7 +587,7 @@ void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp)  				resize_bar_bg_panel_p.follows.flags = FOLLOWS_ALL;  				resize_bar_bg_panel_p.tab_stop = false;  				resize_bar_bg_panel_p.background_visible = true; -				resize_bar_bg_panel_p.bg_alpha_color = LLUIColorTable::instance().getColor("ResizebarBody"); +				resize_bar_bg_panel_p.bg_alpha_color = mDragHandleColor;  				resize_bar_bg_panel_p.has_border = true;  				resize_bar_bg_panel_p.border.border_thickness = 1;  				resize_bar_bg_panel_p.border.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 22f11eb20f..000b919ae7 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -59,6 +59,8 @@ public:  		Optional<S32>			drag_handle_thickness;  		Optional<S32>			drag_handle_shift; +        Optional<LLUIColor>     drag_handle_color; +  		Params();  	}; @@ -89,6 +91,7 @@ public:  	void updateLayout();  	S32 getPanelSpacing() const { return mPanelSpacing; } +    void setPanelSpacing(S32 val);  	static void updateClass(); @@ -128,6 +131,7 @@ private:  	S32  mDragHandleSecondIndent;  	S32  mDragHandleThickness;  	S32  mDragHandleShift; +    LLUIColor mDragHandleColor;  }; // end class LLLayoutStack diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 16c27da56a..cebca70b59 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -573,13 +573,13 @@ void LLMenuItemGL::onVisibilityChange(BOOL new_visibility)  //  // This class represents a separator.  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLMenuItemSeparatorGL::Params::Params() -{ -} -  LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :  	LLMenuItemGL( p )  { +    if (p.on_visible.isProvided()) +    { +        mVisibleSignal.connect(initEnableCallback(p.on_visible)); +    }  }  //virtual @@ -596,6 +596,15 @@ void LLMenuItemSeparatorGL::draw( void )  	gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );  } +void LLMenuItemSeparatorGL::buildDrawLabel( void ) +{ +    if (mVisibleSignal.num_slots() > 0) +    { +        bool visible = mVisibleSignal(this, LLSD()); +        setVisible(visible); +    } +} +  BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)  {  	LLMenuGL* parent_menu = getMenu(); diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 9d3be8d94f..87e3f18ebc 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -234,7 +234,9 @@ class LLMenuItemSeparatorGL : public LLMenuItemGL  public:  	struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>  	{ -		Params(); +        Optional<EnableCallbackParam > on_visible; +        Params() : on_visible("on_visible") +        {}  	};  	LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params()); @@ -243,7 +245,12 @@ public:  	/*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);  	/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); +    virtual void buildDrawLabel(); +  	/*virtual*/ U32 getNominalHeight( void ) const; + +private: +    enable_signal_t mVisibleSignal;  };  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index fde6de4921..735e2d529e 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -188,12 +188,12 @@ void LLScrollbar::setPageSize( S32 page_size )  	}  } -BOOL LLScrollbar::isAtBeginning() +bool LLScrollbar::isAtBeginning() const  {  	return mDocPos == 0;  } -BOOL LLScrollbar::isAtEnd() +bool LLScrollbar::isAtEnd() const  {  	return mDocPos == getDocPosMax();  } @@ -591,7 +591,12 @@ void LLScrollbar::setValue(const LLSD& value)  BOOL LLScrollbar::handleKeyHere(KEY key, MASK mask)  { -	BOOL handled = FALSE; +    if (getDocPosMax() == 0 && !getVisible()) +    { +        return FALSE; +    } + +    BOOL handled = FALSE;  	switch( key )  	{ diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 5f2f490d81..9be9d22db8 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -105,8 +105,8 @@ public:  	bool				setDocPos( S32 pos, BOOL update_thumb = TRUE );  	S32					getDocPos() const		{ return mDocPos; } -	BOOL				isAtBeginning(); -	BOOL				isAtEnd(); +	bool				isAtBeginning() const; +	bool				isAtEnd() const;  	// Setting both at once.  	void				setDocParams( S32 size, S32 pos ); diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 3db38bbfac..ad32f7186c 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -105,8 +105,8 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)  	mBorder = LLUICtrlFactory::create<LLViewBorder> (params);  	LLView::addChild( mBorder ); -	mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 ); -	mInnerRect.stretch( -getBorderWidth()  ); +	mInnerRect = getLocalRect(); +	mInnerRect.stretch( -getBorderWidth() );  	LLRect vertical_scroll_rect = mInnerRect;  	vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size; @@ -124,8 +124,9 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)  	mScrollbar[VERTICAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);  	LLView::addChild( mScrollbar[VERTICAL] ); -	LLRect horizontal_scroll_rect = mInnerRect; -	horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size; +	LLRect horizontal_scroll_rect; +	horizontal_scroll_rect.mTop = scrollbar_size; +	horizontal_scroll_rect.mRight = mInnerRect.getWidth();  	sbparams.name("scrollable horizontal");  	sbparams.rect(horizontal_scroll_rect);  	sbparams.orientation(LLScrollbar::HORIZONTAL); @@ -134,7 +135,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)  	sbparams.page_size(mInnerRect.getWidth());  	sbparams.step_size(VERTICAL_MULTIPLE);  	sbparams.visible(false); -	sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT); +	sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);  	sbparams.change_callback(p.scroll_callback);  	mScrollbar[HORIZONTAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);  	LLView::addChild( mScrollbar[HORIZONTAL] ); diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index c14099dbd5..dacea2a987 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -98,8 +98,10 @@ public:  	void			pageDown(S32 overlap = 0);  	void			goToTop();  	void			goToBottom(); -	bool			isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); } -	bool			isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); } +	bool			isAtTop() const { return mScrollbar[VERTICAL]->isAtBeginning(); } +	bool			isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); } +    S32             getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); } +    S32             getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); }  	S32				getBorderWidth() const;  	// LLView functionality diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 8c841540a5..76b9e448a1 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -605,6 +605,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )  			LLButton* tab_button = getTab(index)->mButton;  			gFocusMgr.setMouseCapture(this);  			tab_button->setFocus(TRUE); +            mMouseDownTimer.start();  		}  	}  	if (handled) { @@ -653,7 +654,11 @@ BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )  		handled = LLPanel::handleHover(x, y, mask);  	} -	commitHoveredButton(x, y); +    F32 drag_delay = 0.25f; // filter out clicks from dragging +    if (mMouseDownTimer.getElapsedTimeF32() > drag_delay) +    { +        commitHoveredButton(x, y); +    }  	return handled;  } @@ -699,6 +704,7 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )  	}  	commitHoveredButton(x, y); +    mMouseDownTimer.stop();  	LLPanel* cur_panel = getCurrentPanel();  	if (hasMouseCapture())  	{ @@ -1002,7 +1008,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)  	}  	else  	{ -		//Scip tab button space if they are invisible(EXT - 576) +		// Skip tab button space if tabs are invisible (EXT-576)  		tab_panel_top = getRect().getHeight();  		tab_panel_bottom = LLPANEL_BORDER_WIDTH;  	} @@ -1017,9 +1023,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)  	}  	else  	{ -		tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH,  +		tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH * 3,  								tab_panel_top, -								getRect().getWidth()-LLPANEL_BORDER_WIDTH, +								getRect().getWidth() - LLPANEL_BORDER_WIDTH * 2,  								tab_panel_bottom );  	}  	child->setFollowsAll(); @@ -1106,7 +1112,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)  		  p.follows.flags = p.follows.flags() | FOLLOWS_TOP;  		}  		else -		  {  +		{   		    p.name("htab_"+std::string(child->getName()));  		    p.visible(false);  		    p.image_unselected(tab_img); diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 8f8cedb1b9..aa4a08c4ff 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -320,6 +320,7 @@ private:  	LLUIColor						mTabsFlashingColor;  	S32								mTabIconCtrlPad;  	bool							mUseTabEllipses; +    LLFrameTimer					mMouseDownTimer;  };  #endif  // LL_TABCONTAINER_H diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 2f56a8b1d0..a6552d4ff1 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -163,6 +163,7 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)  :	LLPanel(p),  	mHasClickCallback(p.click_callback.isProvided()),  	mPadding(p.padding), +	mMaxWidth(p.max_width),  	mTextBox(NULL),  	mInfoButton(NULL),  	mPlayMediaButton(NULL), @@ -272,7 +273,7 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)  	// do this *after* we've had our size set in LLPanel::initFromParams();  	const S32 REALLY_LARGE_HEIGHT = 10000; -	mTextBox->reshape(p.max_width, REALLY_LARGE_HEIGHT); +	mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT);  	if (p.styled_message.isProvided())  	{ @@ -288,16 +289,19 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)  		mTextBox->setText(p.message());  	} -	S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth() + 1); +	updateTextBox(); +	snapToChildren(); +} + +void LLToolTip::updateTextBox() +{ +	S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1);  	S32 text_height = mTextBox->getTextPixelHeight();  	mTextBox->reshape(text_width, text_height); -	if (mInfoButton) -	{ -		LLRect text_rect = mTextBox->getRect(); -		LLRect icon_rect = mInfoButton->getRect(); -		mTextBox->translate(0, icon_rect.getCenterY() - text_rect.getCenterY()); -	} - +} +  +void LLToolTip::snapToChildren() +{  	// reshape tooltip panel to fit text box  	LLRect tooltip_rect = calcBoundingRect();  	tooltip_rect.mTop += mPadding; @@ -305,7 +309,14 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)  	tooltip_rect.mBottom = 0;  	tooltip_rect.mLeft = 0; -	mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding)); +	if (mInfoButton) +	{ +		mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding)); + +		LLRect text_rect = mTextBox->getRect(); +		LLRect icon_rect = mInfoButton->getRect(); +		mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY()); +	}  	setShape(tooltip_rect);  } @@ -428,7 +439,10 @@ void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)  	}  	tooltip_params.rect = LLRect (0, 1, 1, 0); -	mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params); +	if (tooltip_params.create_callback.isProvided()) +		mToolTip = tooltip_params.create_callback()(tooltip_params); +	else +		mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);  	gToolTipView->addChild(mToolTip); diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h index 0b1fbe5367..86943625ff 100644 --- a/indra/llui/lltooltip.h +++ b/indra/llui/lltooltip.h @@ -68,6 +68,7 @@ public:  	struct Params : public LLInitParam::Block<Params, LLPanel::Params>   	{  		typedef boost::function<void(void)> click_callback_t; +		typedef boost::function<LLToolTip*(LLToolTip::Params)> create_callback_t;  		Optional<std::string>		message;  		Multiple<StyledText>		styled_message; @@ -84,6 +85,8 @@ public:  		Optional<bool>				time_based_media,  									web_based_media,  									media_playing; +		Optional<create_callback_t>	create_callback; +		Optional<LLSD>				create_params;  		Optional<click_callback_t>	click_callback,  									click_playmedia_callback,  									click_homepage_callback; @@ -103,11 +106,15 @@ public:  	bool hasClickCallback();  	LLToolTip(const Params& p); -	void initFromParams(const LLToolTip::Params& params); +	virtual void initFromParams(const LLToolTip::Params& params);  	void getToolTipMessage(std::string & message); -private: +protected: +	void updateTextBox(); +	void snapToChildren(); + +protected:  	class LLTextBox*	mTextBox;  	class LLButton*     mInfoButton;  	class LLButton*     mPlayMediaButton; @@ -117,6 +124,7 @@ private:  	LLFrameTimer	mVisibleTimer;  	bool			mHasClickCallback;  	S32				mPadding;	// pixels +	S32				mMaxWidth;  };  // used for the inspector tooltips which need different background images etc. diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 2196ba201b..21afcae7c3 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -531,6 +531,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 67dd24341c..be1c7dd0b6 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -175,6 +175,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; }  diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 3344300635..da7868d804 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -311,7 +311,13 @@ bool LLView::addChild(LLView* child, S32 tab_group)  	}  	child->mParentView = this; -	updateBoundingRect(); +    if (getVisible() && child->getVisible()) +    { +        // if child isn't visible it won't affect bounding rect +        // if current view is not visible it will be recalculated +        // on visibility change +        updateBoundingRect(); +    }  	mLastTabGroup = tab_group;  	return true;  } @@ -581,6 +587,7 @@ void LLView::deleteAllChildren()          delete viewp;          mChildList.pop_front();  	} +    updateBoundingRect();  }  void LLView::setAllChildrenEnabled(BOOL b) @@ -879,6 +886,17 @@ LLView*	LLView::childFromPoint(S32 x, S32 y, bool recur)  	return 0;  } +F32 LLView::getTooltipTimeout() +{ +    static LLCachedControl<F32> tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f); +    static LLCachedControl<F32> tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f); +    // allow "scrubbing" over ui by showing next tooltip immediately +    // if previous one was still visible +    return (F32)(LLToolTipMgr::instance().toolTipVisible() +    ? tooltip_fast_delay +    : tooltip_delay); +} +  BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)  {  	BOOL handled = FALSE; @@ -888,14 +906,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)  	std::string tooltip = getToolTip();  	if (!tooltip.empty())  	{ -        static LLCachedControl<F32> tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f); -        static LLCachedControl<F32> tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f);          static LLCachedControl<bool> allow_ui_tooltips(*LLUI::getInstance()->mSettingGroups["config"], "BasicUITooltips", true); -		// allow "scrubbing" over ui by showing next tooltip immediately -		// if previous one was still visible -		F32 timeout = LLToolTipMgr::instance().toolTipVisible()  -		              ? tooltip_fast_delay -		              : tooltip_delay;  		// Even if we don't show tooltips, consume the event, nothing below should show tooltip  		if (allow_ui_tooltips) @@ -903,7 +914,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)  			LLToolTipMgr::instance().show(LLToolTip::Params()  			                              .message(tooltip)  			                              .sticky_rect(calcScreenRect()) -			                              .delay_time(timeout)); +			                              .delay_time(getTooltipTimeout()));  		}  		handled = TRUE;  	} diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 8aa97aac39..7360fd0fb9 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -243,6 +243,7 @@ public:  	ECursorType	getHoverCursor() { return mHoverCursor; } +    static F32 getTooltipTimeout();  	virtual const std::string getToolTip() const			{ return mToolTipMsg.getString(); }  	void		sendChildToFront(LLView* child); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 1e2fcaa3ad..40a858cfeb 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -207,6 +207,7 @@ set(viewer_SOURCE_FILES      llfloaterbuyland.cpp      llfloatercamera.cpp      llfloatercamerapresets.cpp +    llfloaterchangeitemthumbnail.cpp      llfloaterchatvoicevolume.cpp      llfloaterclassified.cpp      llfloatercolorpicker.cpp @@ -243,6 +244,7 @@ set(viewer_SOURCE_FILES      llfloaterimsession.cpp      llfloaterimcontainer.cpp      llfloaterinspect.cpp +    llfloaterinventorysettings.cpp      llfloaterjoystick.cpp      llfloaterlagmeter.cpp      llfloaterland.cpp @@ -258,12 +260,12 @@ set(viewer_SOURCE_FILES      llfloatermyscripts.cpp      llfloatermyenvironment.cpp      llfloaternamedesc.cpp +    llfloaternewfeaturenotification.cpp      llfloaternotificationsconsole.cpp      llfloaternotificationstabbed.cpp -    llfloateroutfitphotopreview.cpp       llfloaterobjectweights.cpp      llfloateropenobject.cpp -    llfloatersimpleoutfitsnapshot.cpp +    llfloatersimplesnapshot.cpp      llfloaterpathfindingcharacters.cpp      llfloaterpathfindingconsole.cpp      llfloaterpathfindinglinksets.cpp @@ -278,7 +280,6 @@ set(viewer_SOURCE_FILES      llfloaterpreferenceviewadvanced.cpp      llfloaterpreviewtrash.cpp      llfloaterprofiletexture.cpp -    llfloaterproperties.cpp      llfloaterregiondebugconsole.cpp      llfloaterregioninfo.cpp      llfloaterreporter.cpp @@ -350,10 +351,13 @@ set(viewer_SOURCE_FILES      llinspectgroup.cpp      llinspectobject.cpp      llinspectremoteobject.cpp +    llinspecttexture.cpp      llinspecttoast.cpp      llinventorybridge.cpp      llinventoryfilter.cpp      llinventoryfunctions.cpp +    llinventorygallery.cpp +    llinventorygallerymenu.cpp      llinventoryicon.cpp      llinventoryitemslist.cpp      llinventorylistitem.cpp @@ -579,6 +583,7 @@ set(viewer_SOURCE_FILES      lltextureinfodetails.cpp      lltexturestats.cpp      lltextureview.cpp +    llthumbnailctrl.cpp      lltoast.cpp      lltoastalertpanel.cpp      lltoastgroupnotifypanel.cpp @@ -847,6 +852,7 @@ set(viewer_HEADER_FILES      llfloaterbuycurrencyhtml.h      llfloaterbuyland.h      llfloatercamerapresets.h +    llfloaterchangeitemthumbnail.h      llfloatercamera.h      llfloaterchatvoicevolume.h      llfloaterclassified.h @@ -887,6 +893,7 @@ set(viewer_HEADER_FILES      llfloaterimsession.h      llfloaterimcontainer.h      llfloaterinspect.h +    llfloaterinventorysettings.h      llfloaterjoystick.h      llfloaterlagmeter.h      llfloaterland.h @@ -902,12 +909,12 @@ set(viewer_HEADER_FILES      llfloatermyscripts.h      llfloatermyenvironment.h      llfloaternamedesc.h +    llfloaternewfeaturenotification.h      llfloaternotificationsconsole.h      llfloaternotificationstabbed.h -    llfloateroutfitphotopreview.h      llfloaterobjectweights.h      llfloateropenobject.h -    llfloatersimpleoutfitsnapshot.h +    llfloatersimplesnapshot.h      llfloaterpathfindingcharacters.h      llfloaterpathfindingconsole.h      llfloaterpathfindinglinksets.h @@ -922,7 +929,6 @@ set(viewer_HEADER_FILES      llfloaterpreferenceviewadvanced.h      llfloaterpreviewtrash.h      llfloaterprofiletexture.h -    llfloaterproperties.h      llfloaterregiondebugconsole.h      llfloaterregioninfo.h      llfloaterreporter.h @@ -992,10 +998,13 @@ set(viewer_HEADER_FILES      llinspectgroup.h      llinspectobject.h      llinspectremoteobject.h +    llinspecttexture.h      llinspecttoast.h      llinventorybridge.h      llinventoryfilter.h      llinventoryfunctions.h +    llinventorygallery.h +    llinventorygallerymenu.h      llinventoryicon.h      llinventoryitemslist.h      llinventorylistitem.h @@ -1212,6 +1221,7 @@ set(viewer_HEADER_FILES      lltextureinfodetails.h      lltexturestats.h      lltextureview.h +    llthumbnailctrl.h      lltoast.h      lltoastalertpanel.h      lltoastgroupnotifypanel.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index e9d5f8c17f..b69c12176f 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.6.14 +6.6.15 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 816b7b7c92..28d84aba21 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5002,17 +5002,6 @@          <key>Value</key>          <integer>0</integer>      </map> -    <key>InventoryInboxToggleState</key> -    <map> -        <key>Comment</key> -        <string>Stores the open/closed state of inventory Received items panel</string> -        <key>Persist</key> -        <integer>1</integer> -        <key>Type</key> -        <string>Boolean</string> -        <key>Value</key> -        <integer>0</integer> -    </map>  	<key>InventoryLinking</key>  	<map>  		<key>Comment</key> @@ -5508,6 +5497,17 @@        <key>Value</key>        <integer>0</integer>      </map> +    <key>LastUIFeatureVersion</key> +    <map> +        <key>Comment</key> +        <string>UI Feature Version number for tracking feature notification between viewer builds</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>LLSD</string> +        <key>Value</key> +        <string></string> +    </map>      <key>LastFindPanel</key>      <map>        <key>Comment</key> @@ -15371,6 +15371,17 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>BatchSizeAIS3</key> +    <map> +        <key>Comment</key> +        <string>Amount of folder ais packs into category subset request</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>S32</string> +        <key>Value</key> +        <integer>20</integer> +    </map>      <key>PoolSizeAIS</key>          <map>          <key>Comment</key> @@ -15378,7 +15389,7 @@          <key>Type</key>              <string>U32</string>          <key>Value</key> -            <integer>1</integer> +            <integer>20</integer>          </map>      <key>PoolSizeUpload</key>          <map> @@ -17107,6 +17118,17 @@      <key>Value</key>      <integer>0</integer>    </map> +  <key>FindOriginalOpenWindow</key> +  <map> +    <key>Comment</key> +    <string>Sets the action for 'Find original' and 'Show in Inventory' (0 - shows item in main Inventory, 1 - opens a new single-folder window)</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <integer>0</integer> +  </map>    <key>StatsReportMaxDuration</key>    <map>      <key>Comment</key> @@ -17140,5 +17162,27 @@      <key>Value</key>      <integer>0</integer>    </map> +<key>MultiModeDoubleClickFolder</key> +<map> +  <key>Comment</key> +  <string>Sets the action for Double-click on folder in multi-folder view (0 - expands and collapses folder, 1 - opens a new window, 2 – stays in current floater but switches to SFV)</string> +  <key>Persist</key> +  <integer>1</integer> +  <key>Type</key> +  <string>U32</string> +  <key>Value</key> +  <integer>0</integer> +</map> +<key>SingleModeDoubleClickOpenWindow</key> +<map> +  <key>Comment</key> +  <string>Sets the action for Double-click on folder in single-folder view (0 - stays in current window, 1 - opens a new window)</string> +  <key>Persist</key> +  <integer>1</integer> +  <key>Type</key> +  <string>Boolean</string> +  <key>Value</key> +  <integer>0</integer> +</map>  </map>  </llsd> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index f87fa5b281..77fe601c1e 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -120,6 +120,11 @@ const F64 CHAT_AGE_FAST_RATE = 3.0;  const F32 MIN_FIDGET_TIME = 8.f; // seconds  const F32 MAX_FIDGET_TIME = 20.f; // seconds +const S32 UI_FEATURE_VERSION = 1; +// For version 1: 1 - inventory, 2 - gltf +// Will need to change to 3 once either inventory or gltf releases and cause a conflict +const S32 UI_FEATURE_FLAGS = 1; +  // The agent instance.  LLAgent gAgent; @@ -372,7 +377,7 @@ LLAgent::LLAgent() :  	mHideGroupTitle(FALSE),  	mGroupID(), -	mInitialized(FALSE), +	mInitialized(false),  	mListener(),  	mDoubleTapRunTimer(), @@ -448,7 +453,7 @@ LLAgent::LLAgent() :  	mNextFidgetTime(0.f),  	mCurrentFidget(0), -	mFirstLogin(FALSE), +	mFirstLogin(false),  	mOutfitChosen(FALSE),  	mVoiceConnected(false), @@ -505,7 +510,7 @@ void LLAgent::init()  	mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT); -	mInitialized = TRUE; +	mInitialized = true;  }  //----------------------------------------------------------------------------- @@ -560,6 +565,93 @@ void LLAgent::onAppFocusGained()  	}  } +void LLAgent::setFirstLogin(bool b) +{ +    mFirstLogin = b; + +    if (mFirstLogin) +    { +        // Don't notify new users about new features +        if (getFeatureVersion() <= UI_FEATURE_VERSION) +        { +            setFeatureVersion(UI_FEATURE_VERSION, UI_FEATURE_FLAGS); +        } +    } +} + +void LLAgent::setFeatureVersion(S32 version, S32 flags) +{ +    LLSD updated_version; +    updated_version["version"] = version; +    updated_version["flags"] = flags; +    gSavedSettings.setLLSD("LastUIFeatureVersion", updated_version); +} + +S32 LLAgent::getFeatureVersion() +{ +    S32 version; +    S32 flags; +    getFeatureVersionAndFlags(version, flags); +    return version; +} + +void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags) +{ +    version = 0; +    flags = 0; +    LLSD feature_version = gSavedSettings.getLLSD("LastUIFeatureVersion"); +    if (feature_version.isInteger()) +    { +        version = feature_version.asInteger(); +        flags = 1; // inventory flag +    } +    else if (feature_version.isMap()) +    { +        version = feature_version["version"]; +        flags = feature_version["flags"]; +    } +    else if (!feature_version.isString() && !feature_version.isUndefined()) +    { +        // is something newer inside? +        version = UI_FEATURE_VERSION; +        flags = UI_FEATURE_FLAGS; +    } +} + +void LLAgent::showLatestFeatureNotification(const std::string key) +{ +    S32 version; +    S32 flags; // a single release can have multiple new features +    getFeatureVersionAndFlags(version, flags); +    if (version <= UI_FEATURE_VERSION && (flags & UI_FEATURE_FLAGS) != UI_FEATURE_FLAGS) +    { +        S32 flag = 0; + +        if (key == "inventory") +        { +            // Notify user about new thumbnail support +            flag = 1; +        } + +        if (key == "gltf") +        { +            flag = 2; +        } + +        if ((flags & flag) == 0) +        { +            // Need to open on top even if called from onOpen, +            // do on idle to make sure it's on top +            LLSD floater_key(key); +            doOnIdleOneTime([floater_key]() +                            { +                                LLFloaterReg::showInstance("new_feature_notification", floater_key); +                            }); + +            setFeatureVersion(UI_FEATURE_VERSION, flags | flag); +        } +    } +}  void LLAgent::ageChat()  { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index ea91d2b720..fd3a9b1d7b 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -117,15 +117,20 @@ private:  	//--------------------------------------------------------------------  public:  	void			onAppFocusGained(); -	void			setFirstLogin(BOOL b) 	{ mFirstLogin = b; } +	void			setFirstLogin(bool b);  	// Return TRUE if the database reported this login as the first for this particular user. -	BOOL 			isFirstLogin() const 	{ return mFirstLogin; } -	BOOL 			isInitialized() const 	{ return mInitialized; } +	bool 			isFirstLogin() const 	{ return mFirstLogin; } +	bool 			isInitialized() const 	{ return mInitialized; } + +    void            setFeatureVersion(S32 version, S32 flags); +    S32             getFeatureVersion(); +    void            getFeatureVersionAndFlags(S32 &version, S32 &flags); +    void            showLatestFeatureNotification(const std::string key);  public:  	std::string		mMOTD; 					// Message of the day  private: -	BOOL			mInitialized; -	BOOL			mFirstLogin; +	bool			mInitialized; +	bool			mFirstLogin;  	boost::shared_ptr<LLAgentListener> mListener;  	//-------------------------------------------------------------------- diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 53397978e0..db99f20775 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -1306,8 +1306,9 @@ void LLAgentWearables::findAttachmentsAddRemoveInfo(LLInventoryModel::item_array  	}  	// Build up list of objects to be removed and items currently attached. -	for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin();  -		 iter != gAgentAvatarp->mAttachmentPoints.end();) +    LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); +    LLVOAvatar::attachment_map_t::iterator end = gAgentAvatarp->mAttachmentPoints.end(); +	while (iter != end)  	{  		LLVOAvatar::attachment_map_t::iterator curiter = iter++;  		LLViewerJointAttachment* attachment = curiter->second; @@ -1526,7 +1527,7 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos  }  // static -void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id) +void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb)  {  	if (type == LLWearableType::WT_INVALID || type == LLWearableType::WT_NONE) return; @@ -1538,7 +1539,7 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con  	LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp);  	LLAssetType::EType asset_type = wearable->getAssetType(); -	LLPointer<LLInventoryCallback> cb; +	LLPointer<LLBoostFuncInventoryCallback> cb;  	if(wear)  	{  		cb = new LLBoostFuncInventoryCallback(wear_and_edit_cb); @@ -1547,6 +1548,10 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con  	{  		cb = new LLBoostFuncInventoryCallback(wear_cb);  	} +    if (created_cb != NULL) +    { +        cb->addOnFireFunc(created_cb); +    }  	LLUUID folder_id; diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 2710262910..e20f5df7fa 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -129,7 +129,7 @@ protected:  	//--------------------------------------------------------------------  public: -	static void		createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null); +	static void		createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null, std::function<void(const LLUUID&)> created_cb = NULL);  	static void		editWearable(const LLUUID& item_id);  	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index 005259bcb8..087cfb8d48 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -29,11 +29,15 @@  #include "llaisapi.h"  #include "llagent.h" +#include "llappviewer.h"  #include "llcallbacklist.h"  #include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llnotificationsutil.h"  #include "llsdutil.h"  #include "llviewerregion.h" -#include "llinventoryobserver.h" +#include "llvoavatar.h" +#include "llvoavatarself.h"  #include "llviewercontrol.h"  ///---------------------------------------------------------------------------- @@ -43,11 +47,16 @@  //=========================================================================  const std::string AISAPI::INVENTORY_CAP_NAME("InventoryAPIv3");  const std::string AISAPI::LIBRARY_CAP_NAME("LibraryAPIv3"); +const S32 AISAPI::HTTP_TIMEOUT = 180;  std::list<AISAPI::ais_query_item_t> AISAPI::sPostponedQuery;  const S32 MAX_SIMULTANEOUS_COROUTINES = 2048; +// AIS3 allows '*' requests, but in reality those will be cut at some point +// Specify own depth to be able to anticipate it and mark folders as incomplete +const S32 MAX_FOLDER_DEPTH_REQUEST = 50; +  //-------------------------------------------------------------------------  /*static*/  bool AISAPI::isAvailable() @@ -93,6 +102,10 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c      if (cap.empty())      {          LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      } @@ -100,7 +113,7 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c      tid.generate();      std::string url = cap + std::string("/category/") + parentId.asString() + "?tid=" + tid.asString(); -    LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; +    LL_DEBUGS("Inventory") << "url: " << url << " parentID " << parentId << " newInventory " << newInventory << LL_ENDL;      // I may be suffering from golden hammer here, but the first part of this bind       // is actually a static cast for &HttpCoroutineAdapter::postAndSuspend so that  @@ -124,7 +137,7 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c          (&LLCoreHttpUtil::HttpCoroutineAdapter::postAndSuspend), _1, _2, _3, _4, _5, _6);      LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, -        _1, postFn, url, parentId, newInventory, callback, COPYINVENTORY)); +        _1, postFn, url, parentId, newInventory, callback, CREATEINVENTORY));      EnqueueAISCommand("CreateInventory", proc);  } @@ -135,6 +148,10 @@ void AISAPI::SlamFolder(const LLUUID& folderId, const LLSD& newInventory, comple      if (cap.empty())      {          LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      } @@ -170,6 +187,10 @@ void AISAPI::RemoveCategory(const LLUUID &categoryId, completion_t callback)      if (cap.empty())      {          LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      } @@ -203,6 +224,10 @@ void AISAPI::RemoveItem(const LLUUID &itemId, completion_t callback)      if (cap.empty())      {          LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      } @@ -235,6 +260,10 @@ void AISAPI::CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, b      if (cap.empty())      {          LL_WARNS("Inventory") << "Library cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      } @@ -279,6 +308,10 @@ void AISAPI::PurgeDescendents(const LLUUID &categoryId, completion_t callback)      if (cap.empty())      {          LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      } @@ -313,6 +346,10 @@ void AISAPI::UpdateCategory(const LLUUID &categoryId, const LLSD &updates, compl      if (cap.empty())      {          LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      }      std::string url = cap + std::string("/category/") + categoryId.asString(); @@ -345,6 +382,10 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t      if (cap.empty())      {          LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        }          return;      }      std::string url = cap + std::string("/item/") + itemId.asString(); @@ -368,6 +409,380 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t  }  /*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; +        if (callback) +        { +            callback(LLUUID::null); +        } +		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<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> +		//---- +		// _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; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    std::string url = cap + std::string("/category/") + catId.asString() + "/children"; + +    if (recursive) +    { +        // can specify depth=*, but server side is going to cap requests +        // and reject everything 'over the top',. +        depth = MAX_FOLDER_DEPTH_REQUEST; +    } +    else +    { +        depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); +    } + +    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<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> +        //---- +        // _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, FETCHCATEGORYCHILDREN)); + +    EnqueueAISCommand("FetchCategoryChildren", proc); +} + +// some folders can be requested by name, like +// animatn | bodypart | clothing | current | favorite | gesture | inbox | landmark | lsltext +// lstndfnd | my_otfts | notecard | object | outbox | root | snapshot | sound | texture | trash +void AISAPI::FetchCategoryChildren(const std::string &identifier, bool recursive, completion_t callback, S32 depth) +{ +    std::string cap; + +    cap = getInvCap(); +    if (cap.empty()) +    { +        LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    std::string url = cap + std::string("/category/") + identifier + "/children"; + +    if (recursive) +    { +        // can specify depth=*, but server side is going to cap requests +        // and reject everything 'over the top',. +        depth = MAX_FOLDER_DEPTH_REQUEST; +    } +    else +    { +        depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); +    } + +    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<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> +        //---- +        // _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, LLUUID::null, 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; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    std::string url = cap + std::string("/category/") + catId.asString() + "/categories"; + +    if (recursive) +    { +        // can specify depth=*, but server side is going to cap requests +        // and reject everything 'over the top',. +        depth = MAX_FOLDER_DEPTH_REQUEST; +    } +    else +    { +        depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); +    } + +    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<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> +        //---- +        // _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); +} + +void AISAPI::FetchCategorySubset(const LLUUID& catId, +                                   const uuid_vec_t specificChildren, +                                   ITEM_TYPE type, +                                   bool recursive, +                                   completion_t callback, +                                   S32 depth) +{ +    std::string cap = (type == INVENTORY) ? getInvCap() : getLibCap(); +    if (cap.empty()) +    { +        LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    if (specificChildren.empty()) +    { +        LL_WARNS("Inventory") << "Empty request!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    // category/any_folder_id/children?depth=*&children=child_id1,child_id2,child_id3 +    std::string url = cap + std::string("/category/") + catId.asString() + "/children"; + +    if (recursive) +    { +        depth = MAX_FOLDER_DEPTH_REQUEST; +    } +    else +    { +        depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); +    } + +    uuid_vec_t::const_iterator iter = specificChildren.begin(); +    uuid_vec_t::const_iterator end = specificChildren.end(); + +    url += "?depth=" + std::to_string(depth) + "&children=" + iter->asString(); +    iter++; + +    while (iter != end) +    { +        url += "," + iter->asString(); +        iter++; +    } + +    const S32 MAX_URL_LENGH = 2000; // RFC documentation specifies a maximum length of 2048 +    if (url.length() > MAX_URL_LENGH) +    { +        LL_WARNS("Inventory") << "Request url is too long, url: " << url << LL_ENDL; +    } + +    invokationFn_t getFn = boost::bind( +        // Humans ignore next line.  It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. +        static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> +        //---- +        // _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, FETCHCATEGORYSUBSET)); + +    EnqueueAISCommand("FetchCategorySubset", proc); +} + +/*static*/ +// Will get COF folder, links in it and items those links point to +void AISAPI::FetchCOF(completion_t callback) +{ +    std::string cap = getInvCap(); +    if (cap.empty()) +    { +        LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    std::string url = cap + std::string("/category/current/links"); + +    invokationFn_t getFn = boost::bind( +        // Humans ignore next line.  It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. +        static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string&, LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> +        //---- +        // _1 -> httpAdapter +        // _2 -> httpRequest +        // _3 -> url +        // _4 -> body  +        // _5 -> httpOptions +        // _6 -> httpHeaders +        (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + +    LLSD body; +    // Only cof folder will be full, but cof can contain an outfit +    // link with embedded outfit folder for request to parse +    body["depth"] = 0; +    LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, +                                                         _1, getFn, url, LLUUID::null, body, callback, FETCHCOF)); + +    EnqueueAISCommand("FetchCOF", proc); +} + +void AISAPI::FetchCategoryLinks(const LLUUID &catId, completion_t callback) +{ +    std::string cap = getInvCap(); +    if (cap.empty()) +    { +        LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    std::string url = cap + std::string("/category/") + catId.asString() + "/links"; + +    invokationFn_t getFn = boost::bind( +        // Humans ignore next line.  It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. +        static_cast<LLSD (LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t, const std::string &, +                                                                   LLCore::HttpOptions::ptr_t, LLCore::HttpHeaders::ptr_t)> +        //---- +        // _1 -> httpAdapter +        // _2 -> httpRequest +        // _3 -> url +        // _4 -> body +        // _5 -> httpOptions +        // _6 -> httpHeaders +        (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), +        _1, _2, _3, _5, _6); + +    LLSD body; +    body["depth"] = 0; +    LLCoprocedureManager::CoProcedure_t proc( +        boost::bind(&AISAPI::InvokeAISCommandCoro, _1, getFn, url, LLUUID::null, body, callback, FETCHCATEGORYLINKS)); + +    EnqueueAISCommand("FetchCategoryLinks", proc); +} + +/*static*/ +void AISAPI::FetchOrphans(completion_t callback) +{ +    std::string cap = getInvCap(); +    if (cap.empty()) +    { +        LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +    std::string url = cap + std::string("/orphans"); + +    invokationFn_t getFn = boost::bind( +        // Humans ignore next line.  It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. +        static_cast<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::*)(LLCore::HttpRequest::ptr_t , const std::string& , LLCore::HttpOptions::ptr_t , LLCore::HttpHeaders::ptr_t)> +        //---- +        // _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 , LLUUID::null , LLSD() , callback , FETCHORPHANS)); + +    EnqueueAISCommand("FetchOrphans" , proc); +} + +/*static*/  void AISAPI::EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc)  {      LLCoprocedureManager &inst = LLCoprocedureManager::instance(); @@ -418,21 +833,66 @@ void AISAPI::onIdle(void *userdata)  }  /*static*/ +void AISAPI::onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body) +{ +    LLTimer timer; +    if ( (type == UPDATECATEGORY || type == UPDATEITEM) +        && gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) +    { +        dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); +    } + +    AISUpdate ais_update(update, type, request_body); +    ais_update.doUpdate(); // execute the updates in the appropriate order. +    LL_DEBUGS("Inventory", "AIS3") << "Elapsed processing: " << timer.getElapsedTimeF32() << LL_ENDL; +} + +/*static*/  void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter,           invokationFn_t invoke, std::string url,           LLUUID targetId, LLSD body, completion_t callback, COMMAND_TYPE type)  { +    if (gDisconnected) +    { +        if (callback) +        { +            callback(LLUUID::null); +        } +        return; +    } +      LLCore::HttpOptions::ptr_t httpOptions(new LLCore::HttpOptions);      LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest());      LLCore::HttpHeaders::ptr_t httpHeaders; -    httpOptions->setTimeout(LLCoreHttpUtil::HTTP_REQUEST_EXPIRY_SECS); +    httpOptions->setTimeout(HTTP_TIMEOUT); -    LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; +    LL_DEBUGS("Inventory") << "Request url: " << url << LL_ENDL; -    LLSD result = invoke(httpAdapter, httpRequest, url, body, httpOptions, httpHeaders); -    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; -    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); +    LLSD result; +    LLSD httpResults; +    LLCore::HttpStatus status; + +    if (debugLoggingEnabled("Inventory")) +    { +        LLTimer ais_timer; +        ais_timer.start(); +        result = invoke(httpAdapter , httpRequest , url , body , httpOptions , httpHeaders); +        httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +        status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); +        F32MillisecondsImplicit elapsed_time = ais_timer.getElapsedTimeF32(); + +        LL_DEBUGS("Inventory") << "Request type: " << (S32)type +            << " \nRequest target: " << targetId +            << " \nElapsed time since request: " << elapsed_time +            << " \nstatus: " << status.toULong() << LL_ENDL; +    } +    else +    { +        result = invoke(httpAdapter , httpRequest , url , body , httpOptions , httpHeaders); +        httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +        status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); +    }      if (!status || !result.isMap())      { @@ -474,29 +934,127 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht                  }              }          } +        else if (status == LLCore::HttpStatus(HTTP_FORBIDDEN) /*403*/) +        { +            if (type == FETCHCATEGORYCHILDREN) +            { +                if (body.has("depth") && body["depth"].asInteger() == 0) +                { +                    // Can't fetch a single folder with depth 0, folder is too big. +                    static bool first_call = true; +                    if (first_call) +                    { +                        first_call = false; +                        LLNotificationsUtil::add("InventoryLimitReachedAISAlert"); +                    } +                    else +                    { +                        LLNotificationsUtil::add("InventoryLimitReachedAIS"); +                    } +                    LL_WARNS("Inventory") << "Fetch failed, content is over limit, url: " << url << LL_ENDL; +                } +                else +                { +                    // Result was too big, but situation is recoverable by requesting with lower depth +                    LL_DEBUGS("Inventory") << "Fetch failed, content is over limit, url: " << url << LL_ENDL; +                } +            } +        }          LL_WARNS("Inventory") << "Inventory error: " << status.toString() << LL_ENDL;          LL_WARNS("Inventory") << ll_pretty_print_sd(result) << LL_ENDL;      } -    gInventory.onAISUpdateReceived("AISCommand", result); +	LL_DEBUGS("Inventory", "AIS3") << "Result: " << result << LL_ENDL; +    onUpdateReceived(result, type, body);      if (callback && !callback.empty()) -    {    +    { +        bool needs_callback = true;          LLUUID id(LLUUID::null); -        if (result.has("category_id") && (type == COPYLIBRARYCATEGORY)) -	    { -		    id = result["category_id"]; -	    } +        switch (type) +        { +        case COPYLIBRARYCATEGORY: +        case FETCHCATEGORYCATEGORIES: +        case FETCHCATEGORYCHILDREN: +        case FETCHCATEGORYSUBSET: +        case FETCHCATEGORYLINKS: +        case FETCHCOF: +            if (result.has("category_id")) +            { +                id = result["category_id"]; +            } +            break; +        case FETCHITEM: +            if (result.has("item_id")) +            { +                // Error message might contain an item_id!!! +                id = result["item_id"]; +            } +            if (result.has("linked_id")) +            { +                id = result["linked_id"]; +            } +            break; +        case 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; +                } +            } +            break; +        default: +            break; +        } -        callback(id); +        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, AISAPI::COMMAND_TYPE type, const LLSD& request_body) +: mType(type)  { +    mFetch = (type == AISAPI::FETCHITEM) +        || (type == AISAPI::FETCHCATEGORYCHILDREN) +        || (type == AISAPI::FETCHCATEGORYCATEGORIES) +        || (type == AISAPI::FETCHCATEGORYSUBSET) +        || (type == AISAPI::FETCHCOF) +        || (type == AISAPI::FETCHCATEGORYLINKS) +        || (type == AISAPI::FETCHORPHANS); +    // parse update llsd into stuff to do or parse received items. +    mFetchDepth = MAX_FOLDER_DEPTH_REQUEST; +    if (mFetch && request_body.has("depth")) +    { +        mFetchDepth = request_body["depth"].asInteger(); +    } + +    mTimer.setTimerExpirySec(debugLoggingEnabled("Inventory") ? EXPIRY_SECONDS_DEBUG : EXPIRY_SECONDS_LIVE); +    mTimer.start();  	parseUpdate(update);  } @@ -506,6 +1064,7 @@ void AISUpdate::clearParseResults()  	mCatDescendentsKnown.clear();  	mCatVersionsUpdated.clear();  	mItemsCreated.clear(); +    mItemsLost.clear();  	mItemsUpdated.clear();  	mCategoriesCreated.clear();  	mCategoriesUpdated.clear(); @@ -514,6 +1073,16 @@ void AISUpdate::clearParseResults()  	mCategoryIds.clear();  } +void AISUpdate::checkTimeout() +{ +    if (mTimer.hasExpired()) +    { +        llcoro::suspend(); +        LLCoros::checkStop(); +        mTimer.setTimerExpirySec(debugLoggingEnabled("Inventory") ? EXPIRY_SECONDS_DEBUG : EXPIRY_SECONDS_LIVE); +    } +} +  void AISUpdate::parseUpdate(const LLSD& update)  {  	clearParseResults(); @@ -601,24 +1170,37 @@ void AISUpdate::parseMeta(const LLSD& update)  void AISUpdate::parseContent(const LLSD& update)  { -	if (update.has("linked_id")) +    // Errors from a fetch request might contain id without +    // full item or folder. +    // Todo: Depending on error we might want to do something, +    // like removing a 404 item or refetching parent folder +	if (update.has("linked_id") && update.has("parent_id"))  	{ -		parseLink(update); +		parseLink(update, mFetchDepth);  	} -	else if (update.has("item_id")) +	else if (update.has("item_id") && update.has("parent_id"))  	{  		parseItem(update);  	} -	if (update.has("category_id")) -	{ -		parseCategory(update); -	} +    if (mType == AISAPI::FETCHCATEGORYSUBSET) +    { +        // initial category is incomplete, don't process it, +        // go for content instead +        if (update.has("_embedded")) +        { +            parseEmbedded(update["_embedded"], mFetchDepth - 1); +        } +    } +    else if (update.has("category_id") && update.has("parent_id")) +    { +        parseCategory(update, mFetchDepth); +    }  	else  	{  		if (update.has("_embedded"))  		{ -			parseEmbedded(update["_embedded"]); +			parseEmbedded(update["_embedded"], mFetchDepth);  		}  	}  } @@ -636,7 +1218,17 @@ 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; +            new_item->setComplete(true); + +            if (new_item->getParentUUID().isNull()) +            { +                mItemsLost[item_id] = new_item; +            } +        } +        else if (curr_item)  		{  			mItemsUpdated[item_id] = new_item;  			// This statement is here to cause a new entry with 0 @@ -648,6 +1240,7 @@ void AISUpdate::parseItem(const LLSD& item_map)  		{  			mItemsCreated[item_id] = new_item;  			mCatDescendentDeltas[new_item->getParentUUID()]++; +            new_item->setComplete(true);  		}  	}  	else @@ -657,7 +1250,7 @@ void AISUpdate::parseItem(const LLSD& item_map)  	}  } -void AISUpdate::parseLink(const LLSD& link_map) +void AISUpdate::parseLink(const LLSD& link_map, S32 depth)  {  	LLUUID item_id = link_map["item_id"].asUUID();  	LLPointer<LLViewerInventoryItem> new_link(new LLViewerInventoryItem); @@ -671,7 +1264,24 @@ 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; +            new_link->setComplete(true); + +            if (new_link->getParentUUID().isNull()) +            { +                mItemsLost[item_id] = new_link; +            } +        } +		else if (curr_link)  		{  			mItemsUpdated[item_id] = new_link;  			// This statement is here to cause a new entry with 0 @@ -690,7 +1300,13 @@ void AISUpdate::parseLink(const LLSD& link_map)  			//LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL;  			mItemsCreated[item_id] = new_link;  			mCatDescendentDeltas[parent_id]++; +            new_link->setComplete(true);  		} + +        if (link_map.has("_embedded")) +        { +            parseEmbedded(link_map["_embedded"], depth); +        }  	}  	else  	{ @@ -700,19 +1316,30 @@ 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; -	// Check descendent count first, as it may be needed -	// to populate newly created categories -	if (category_map.has("_embedded")) -	{ -		parseDescendentCount(category_id, category_map["_embedded"]); -	} +    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 +        && curr_cat->getDescendentCount() != LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN +        && version > LLViewerInventoryCategory::VERSION_UNKNOWN +        && version < curr_cat->getVersion()) +    { +        LL_WARNS() << "Got stale folder, known: " << curr_cat->getVersion() +            << ", received: " << version << LL_ENDL; +        return; +    }  	LLPointer<LLViewerInventoryCategory> new_cat; -	LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id);  	if (curr_cat)  	{  		// Default to current values where not provided. @@ -732,13 +1359,58 @@ 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) +        // Check descendent count first, as it may be needed +        // to populate newly created categories +        if (category_map.has("_embedded")) +        { +            parseDescendentCount(category_id, new_cat->getPreferredType(), category_map["_embedded"]); +        } + +        if (mFetch) +        { +            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); + +                // set version only if we are sure this update has full data and embeded items +                // since viewer uses version to decide if folder and content still need fetching +                if (version > LLViewerInventoryCategory::VERSION_UNKNOWN +                    && depth >= 0) +                { +                    if (curr_cat && curr_cat->getVersion() > version) +                    { +                        LL_WARNS("Inventory") << "Version was " << curr_cat->getVersion() +                            << ", but fetch returned version " << version +                            << " for category " << category_id << LL_ENDL; +                    } +                    else +                    { +                        LL_DEBUGS("Inventory") << "Setting version to " << version +                            << " for category " << category_id << LL_ENDL; +                    } + +                    new_cat->setVersion(version); +                } +            } +            else if (curr_cat +                     && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN +                     && version > curr_cat->getVersion()) +            { +                // Potentially should new_cat->setVersion(unknown) here, +                // but might be waiting for a callback that would increment +                LL_DEBUGS("Inventory") << "Category " << category_id +                    << " is stale. Known version: " << curr_cat->getVersion() +                    << " server version: " << version << LL_ENDL; +            } +            mCategoriesCreated[category_id] = new_cat; +        } +		else if (curr_cat)  		{  			mCategoriesUpdated[category_id] = new_cat;  			// This statement is here to cause a new entry with 0 @@ -751,20 +1423,22 @@ void AISUpdate::parseCategory(const LLSD& category_map)  		else  		{  			// Set version/descendents for newly created categories. -			if (category_map.has("version")) -			{ -				S32 version = category_map["version"].asInteger(); -				LL_DEBUGS("Inventory") << "Setting version to " << version -									   << " for new 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 new category " << category_id << LL_ENDL; -				new_cat->setDescendentCount(descendent_count); +            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 new category " << category_id << LL_ENDL; +                new_cat->setDescendentCount(descendent_count); + +                // Don't set version unles correct children count is present +                if (category_map.has("version")) +                { +                    S32 version = category_map["version"].asInteger(); +                    LL_DEBUGS("Inventory") << "Setting version to " << version +                        << " for new category " << category_id << LL_ENDL; +                    new_cat->setVersion(version); +                }  			}  			mCategoriesCreated[category_id] = new_cat;  			mCatDescendentDeltas[new_cat->getParentUUID()]++; @@ -779,28 +1453,35 @@ 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);  	}  } -void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embedded) +void AISUpdate::parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded)  { -	// We can only determine true descendent count if this contains all descendent types. -	if (embedded.has("categories") && -		embedded.has("links") && -		embedded.has("items")) -	{ -		mCatDescendentsKnown[category_id]  = embedded["categories"].size(); -		mCatDescendentsKnown[category_id] += embedded["links"].size(); -		mCatDescendentsKnown[category_id] += embedded["items"].size(); -	} +    // We can only determine true descendent count if this contains all descendent types. +    if (embedded.has("categories") && +        embedded.has("links") && +        embedded.has("items")) +    { +        mCatDescendentsKnown[category_id] = embedded["categories"].size(); +        mCatDescendentsKnown[category_id] += embedded["links"].size(); +        mCatDescendentsKnown[category_id] += embedded["items"].size(); +    } +    else if (mFetch && embedded.has("links") && (type == LLFolderType::FT_CURRENT_OUTFIT || type == LLFolderType::FT_OUTFIT)) +    { +        // COF and outfits contain links only +        mCatDescendentsKnown[category_id] = embedded["links"].size(); +    }  } -void AISUpdate::parseEmbedded(const LLSD& embedded) +void AISUpdate::parseEmbedded(const LLSD& embedded, S32 depth)  { +    checkTimeout(); +  	if (embedded.has("links")) // _embedded in a category  	{ -		parseEmbeddedLinks(embedded["links"]); +		parseEmbeddedLinks(embedded["links"], depth);  	}  	if (embedded.has("items")) // _embedded in a category  	{ @@ -812,11 +1493,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);  	}  } @@ -833,7 +1514,7 @@ void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uui  	}  } -void AISUpdate::parseEmbeddedLinks(const LLSD& links) +void AISUpdate::parseEmbeddedLinks(const LLSD& links, S32 depth)  {  	for(LLSD::map_const_iterator linkit = links.beginMap(),  			linkend = links.endMap(); @@ -841,13 +1522,13 @@ 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;  		}  		else  		{ -			parseLink(link_map); +			parseLink(link_map, depth);  		}  	}  } @@ -857,7 +1538,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);  		} @@ -873,7 +1554,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;  		} @@ -884,19 +1565,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(), @@ -905,19 +1586,21 @@ 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);  		}  	}  }  void AISUpdate::doUpdate()  { +    checkTimeout(); +  	// Do version/descendant accounting.  	for (std::map<LLUUID,S32>::const_iterator catit = mCatDescendentDeltas.begin();  		 catit != mCatDescendentDeltas.end(); ++catit) @@ -959,6 +1642,7 @@ void AISUpdate::doUpdate()  	}  	// CREATE CATEGORIES +    const S32 MAX_UPDATE_BACKLOG = 50; // stall prevention  	for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin();  		 create_it != mCategoriesCreated.end(); ++create_it)  	{ @@ -967,6 +1651,13 @@ void AISUpdate::doUpdate()  		gInventory.updateCategory(new_category, LLInventoryObserver::CREATE);  		LL_DEBUGS("Inventory") << "created category " << category_id << LL_ENDL; + +        // fetching can receive massive amount of items and folders +        if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG) +        { +            gInventory.notifyObservers(); +            checkTimeout(); +        }  	}  	// UPDATE CATEGORIES @@ -991,6 +1682,24 @@ void AISUpdate::doUpdate()  		}  	} +    // LOST ITEMS +    if (!mItemsLost.empty()) +    { +        LL_INFOS("Inventory") << "Received " << (S32)mItemsLost.size() << " items without a parent" << LL_ENDL; +        const LLUUID lost_uuid(gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); +        if (lost_uuid.notNull()) +        { +            for (deferred_item_map_t::const_iterator lost_it = mItemsLost.begin(); +                 lost_it != mItemsLost.end(); ++lost_it) +            { +                LLPointer<LLViewerInventoryItem> new_item = lost_it->second; + +                new_item->setParent(lost_uuid); +                new_item->updateParentOnServer(FALSE); +            } +        } +    } +  	// CREATE ITEMS  	for (deferred_item_map_t::const_iterator create_it = mItemsCreated.begin();  		 create_it != mItemsCreated.end(); ++create_it) @@ -1003,6 +1712,13 @@ void AISUpdate::doUpdate()  		// case this is create.  		LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL;  		gInventory.updateItem(new_item, LLInventoryObserver::CREATE); + +        // fetching can receive massive amount of items and folders +        if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG) +        { +            gInventory.notifyObservers(); +            checkTimeout(); +        }  	}  	// UPDATE ITEMS @@ -1063,6 +1779,8 @@ void AISUpdate::doUpdate()  		}  	} +    checkTimeout(); +  	gInventory.notifyObservers();  } diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h index 856f3fc180..0fdf4a0b74 100644 --- a/indra/newview/llaisapi.h +++ b/indra/newview/llaisapi.h @@ -38,6 +38,12 @@  class AISAPI  {  public: +    static const S32 HTTP_TIMEOUT; +    typedef enum { +        INVENTORY, +        LIBRARY +    } ITEM_TYPE; +      typedef boost::function<void(const LLUUID &invItem)>    completion_t;      static bool isAvailable(); @@ -50,9 +56,16 @@ 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 FetchCategoryChildren(const std::string &identifier, 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 FetchCategorySubset(const LLUUID& catId, const uuid_vec_t specificChildren, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0); +    static void FetchCOF(completion_t callback = completion_t()); +    static void FetchCategoryLinks(const LLUUID &catId, completion_t callback = completion_t()); +    static void FetchOrphans(completion_t callback = completion_t() );      static void CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, bool copySubfolders, completion_t callback = completion_t()); -private:      typedef enum {          COPYINVENTORY,          SLAMFOLDER, @@ -61,9 +74,18 @@ private:          PURGEDESCENDENTS,          UPDATECATEGORY,          UPDATEITEM, -        COPYLIBRARYCATEGORY +        COPYLIBRARYCATEGORY, +        CREATEINVENTORY, +        FETCHITEM, +        FETCHCATEGORYCHILDREN, +        FETCHCATEGORYCATEGORIES, +        FETCHCATEGORYSUBSET, +        FETCHCOF, +        FETCHORPHANS, +        FETCHCATEGORYLINKS      } COMMAND_TYPE; +private:      static const std::string INVENTORY_CAP_NAME;      static const std::string LIBRARY_CAP_NAME; @@ -72,6 +94,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 LLSD& update, COMMAND_TYPE type, const LLSD& request_body);      static std::string getInvCap();      static std::string getLibCap(); @@ -87,24 +110,30 @@ private:  class AISUpdate  {  public: -	AISUpdate(const LLSD& update); +	AISUpdate(const LLSD& update, AISAPI::COMMAND_TYPE type, const LLSD& request_body);  	void parseUpdate(const LLSD& update);  	void parseMeta(const LLSD& update);  	void parseContent(const LLSD& update);  	void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids); -	void parseLink(const LLSD& link_map); +	void parseLink(const LLSD& link_map, S32 depth);  	void parseItem(const LLSD& link_map); -	void parseCategory(const LLSD& link_map); -	void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded); -	void parseEmbedded(const LLSD& embedded); -	void parseEmbeddedLinks(const LLSD& links); +	void parseCategory(const LLSD& link_map, S32 depth); +	void parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded); +	void parseEmbedded(const LLSD& embedded, S32 depth); +	void parseEmbeddedLinks(const LLSD& links, S32 depth);  	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(); +    void checkTimeout(); + +    // Debug is very log-heavy, give it more time or it will take forever to process +    // Todo: find a way to make throttle static isntead of per-request +    const F32 EXPIRY_SECONDS_DEBUG = 1.f; +    const F32 EXPIRY_SECONDS_LIVE = 0.008f;  	typedef std::map<LLUUID,S32> uuid_int_map_t;  	uuid_int_map_t mCatDescendentDeltas; @@ -113,6 +142,7 @@ private:  	typedef std::map<LLUUID,LLPointer<LLViewerInventoryItem> > deferred_item_map_t;  	deferred_item_map_t mItemsCreated; +    deferred_item_map_t mItemsLost;  	deferred_item_map_t mItemsUpdated;  	typedef std::map<LLUUID,LLPointer<LLViewerInventoryCategory> > deferred_category_map_t;  	deferred_category_map_t mCategoriesCreated; @@ -123,6 +153,10 @@ private:  	uuid_list_t mObjectsDeletedIds;  	uuid_list_t mItemIds;  	uuid_list_t mCategoryIds; +    bool mFetch; +    S32 mFetchDepth; +    LLTimer mTimer; +    AISAPI::COMMAND_TYPE mType;  };  #endif diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index d0fad07f1c..8010b84c20 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -40,6 +40,7 @@  #include "llgesturemgr.h"  #include "llinventorybridge.h"  #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h"  #include "llinventoryobserver.h"  #include "llmd5.h"  #include "llnotificationsutil.h" @@ -590,6 +591,71 @@ LLUpdateAppearanceAndEditWearableOnDestroy::~LLUpdateAppearanceAndEditWearableOn  	}  } +class LLBrokenLinkObserver : public LLInventoryObserver +{ +public: +    LLUUID mUUID; +    bool mEnforceItemRestrictions; +    bool mEnforceOrdering; +    nullary_func_t mPostUpdateFunc; + +    LLBrokenLinkObserver(const LLUUID& uuid, +                          bool enforce_item_restrictions , +                          bool enforce_ordering , +                          nullary_func_t post_update_func) : +        mUUID(uuid), +        mEnforceItemRestrictions(enforce_item_restrictions), +        mEnforceOrdering(enforce_ordering), +        mPostUpdateFunc(post_update_func) +    { +    } +    /* virtual */ void changed(U32 mask); +    void postProcess(); +}; + +void LLBrokenLinkObserver::changed(U32 mask) +{ +    if (mask & LLInventoryObserver::REBUILD) +    { +        // This observer should be executed after LLInventoryPanel::itemChanged(), +        // but if it isn't, consider calling updateAppearanceFromCOF with a delay +        const uuid_set_t& changed_item_ids = gInventory.getChangedIDs(); +        for (uuid_set_t::const_iterator it = changed_item_ids.begin(); it != changed_item_ids.end(); ++it) +        { +            const LLUUID& id = *it; +            if (id == mUUID) +            { +                // Might not be processed yet and it is not a +                // good idea to update appearane here, postpone. +                doOnIdleOneTime([this]() +                                { +                                    postProcess(); +                                }); + +                gInventory.removeObserver(this); +                return; +            } +        } +    } +} + +void LLBrokenLinkObserver::postProcess() +{ +    LLViewerInventoryItem* item = gInventory.getItem(mUUID); +    llassert(item && !item->getIsBrokenLink()); // the whole point was to get a correct link +    if (item && item->getIsBrokenLink()) +    { +        LL_INFOS_ONCE("Avatar") << "Outfit link broken despite being regenerated" << LL_ENDL; +        LL_DEBUGS("Avatar", "Inventory") << "Outfit link " << mUUID << " \"" << item->getName() << "\" is broken despite being regenerated" << LL_ENDL; +    } + +    LLAppearanceMgr::instance().updateAppearanceFromCOF( +        mEnforceItemRestrictions , +        mEnforceOrdering , +        mPostUpdateFunc); +    delete this; +} +  struct LLFoundData  { @@ -1706,12 +1772,18 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds  	{  		parent_id = gInventory.getRootFolderID();  	} -	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(); +    }, +        src_cat->getThumbnailUUID() +    );  }  void LLAppearanceMgr::slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id, @@ -2414,6 +2486,39 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions,  	LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL; +    if (gInventory.hasPosiblyBrockenLinks()) +    { +        // Inventory has either broken links or links that +        // haven't loaded yet. +        // Check if LLAppearanceMgr needs to wait. +        LLUUID current_outfit_id = getCOF(); +        LLInventoryModel::item_array_t cof_items; +        LLInventoryModel::cat_array_t cof_cats; +        LLFindBrokenLinks is_brocken_link; +        gInventory.collectDescendentsIf(current_outfit_id, +            cof_cats, +            cof_items, +            LLInventoryModel::EXCLUDE_TRASH, +            is_brocken_link); + +        if (cof_items.size() > 0) +        { +            // Some links haven't loaded yet, but fetch isn't complete so +            // links are likely fine and we will have to wait for them to +            // load +            if (LLInventoryModelBackgroundFetch::getInstance()->folderFetchActive()) +            { + +                LLBrokenLinkObserver* observer = new LLBrokenLinkObserver(cof_items.front()->getUUID(), +                                                                            enforce_item_restrictions, +                                                                            enforce_ordering, +                                                                            post_update_func); +                gInventory.addObserver(observer); +                return; +            } +        } +    } +  	if (enforce_item_restrictions)  	{  		// The point here is just to call @@ -2730,22 +2835,29 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap  		{  			pid = gInventory.getRootFolderID();  		} -		 -		LLUUID new_cat_id = gInventory.createNewCategory( + +		gInventory.createNewCategory(  			pid,  			LLFolderType::FT_NONE, -			name); - -		// 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(); +            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)); + +            // BAP fixes a lag in display of created dir. +            gInventory.notifyObservers(); +        }, +            cat->getThumbnailUUID() +        );  	}  	else  	{ @@ -3203,7 +3315,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; @@ -3714,7 +3826,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd          if (cofVersion == LLViewerInventoryCategory::VERSION_UNKNOWN)          { -            LL_WARNS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL; +            LL_INFOS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL;              return;          }          else @@ -3986,26 +4098,15 @@ 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); -    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); -		gInventory.createNewCategory( -			parent_id, -			LLFolderType::FT_OUTFIT, -			new_folder_name, -			func); -	} -	else -	{		 -		LLUUID folder_id = gInventory.createNewCategory( -			parent_id, -			LLFolderType::FT_OUTFIT, -			new_folder_name); -		onOutfitFolderCreated(folder_id, show_panel); -	} + +    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() @@ -4338,6 +4439,73 @@ public:  	~CallAfterCategoryFetchStage1()  	{  	} +    /*virtual*/ void startFetch() +    { +        bool ais3 = AISAPI::isAvailable(); +        for (uuid_vec_t::const_iterator it = mIDs.begin(); it != mIDs.end(); ++it) +        { +            LLViewerInventoryCategory* cat = gInventory.getCategory(*it); +            if (!cat) continue; +            if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) +            { +                // CHECK IT: isCategoryComplete() checks both version and descendant count but +                // fetch() only works for Unknown version and doesn't care about descentants, +                // as result fetch won't start and folder will potentially get stuck as +                // incomplete in observer. +                // Likely either both should use only version or both should check descendants. +                cat->fetch();		//blindly fetch it without seeing if anything else is fetching it. +                mIncomplete.push_back(*it);	//Add to list of things being downloaded for this observer. +            } +            else if (!isCategoryComplete(cat)) +            { +                LL_DEBUGS("Inventory") << "Categoty " << *it << " incomplete despite having version" << LL_ENDL; +                LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true); +                mIncomplete.push_back(*it); +            } +            else if (ais3) +            { +                LLInventoryModel::cat_array_t* cats; +                LLInventoryModel::item_array_t* items; +                gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); + +                if (items) +                { +                    S32 complete_count = 0; +                    S32 incomplete_count = 0; +                    for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); it < items->end(); ++it) +                    { +                        if (!(*it)->isFinished()) +                        { +                            incomplete_count++; +                        } +                        else +                        { +                            complete_count++; +                        } +                    } +                    // AIS can fetch couple items, but if there +                    // is more than a dozen it will be very slow +                    // it's faster to get whole folder in such case +                    if (incomplete_count > LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS +                        || (incomplete_count > 1 && complete_count == 0)) +                    { +                        LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true); +                        mIncomplete.push_back(*it); +                    } +                    else +                    { +                        // let stage2 handle incomplete ones +                        mComplete.push_back(*it); +                    } +                } +                // else should have been handled by isCategoryComplete +            } +            else +            { +                mComplete.push_back(*it); +            } +        } +    }  	virtual void done()  	{          if (mComplete.size() <= 0) @@ -4354,13 +4522,11 @@ public:  		// What we do here is get the complete information on the  		// items in the requested category, and set up an observer  		// that will wait for that to happen. -		LLInventoryModel::cat_array_t cat_array; -		LLInventoryModel::item_array_t item_array; -		gInventory.collectDescendents(mComplete.front(), -									  cat_array, -									  item_array, -									  LLInventoryModel::EXCLUDE_TRASH); -		S32 count = item_array.size(); +        LLInventoryModel::cat_array_t* cats; +        LLInventoryModel::item_array_t* items; +        gInventory.getDirectDescendentsOf(mComplete.front(), cats, items); + +		S32 count = items->size();  		if(!count)  		{  			LL_WARNS() << "Nothing fetched in category " << mComplete.front() @@ -4372,11 +4538,13 @@ public:  			return;  		} -		LL_INFOS() << "stage1 got " << item_array.size() << " items, passing to stage2 " << LL_ENDL; +        LLViewerInventoryCategory* cat = gInventory.getCategory(mComplete.front()); +        S32 version = cat ? cat->getVersion() : -2; +		LL_INFOS() << "stage1, category " << mComplete.front() << " got " << count << " items, version " << version << " passing to stage2 " << LL_ENDL;  		uuid_vec_t ids;  		for(S32 i = 0; i < count; ++i)  		{ -			ids.push_back(item_array.at(i)->getUUID()); +			ids.push_back(items->at(i)->getUUID());  		}  		gInventory.removeObserver(this); @@ -4401,18 +4569,78 @@ protected:  	nullary_func_t mCallable;  }; +void callAfterCOFFetch(nullary_func_t cb) +{ +    LLUUID cat_id = LLAppearanceMgr::instance().getCOF(); +    LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + +    if (AISAPI::isAvailable()) +    { +        // Mark cof (update timer) so that background fetch won't request it +        cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE); +        // For reliability assume that we have no relevant cache, so +        // fetch cof along with items cof's links point to. +        AISAPI::FetchCOF([cb](const LLUUID& id) +                         { +                             cb(); +                             LLUUID cat_id = LLAppearanceMgr::instance().getCOF(); +                             LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); +                             if (cat) +                             { +                                 cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); +                             } +                         }); +    } +    else +    { +        LL_INFOS() << "AIS API v3 not available, using callAfterCategoryFetch" << LL_ENDL; +        // startup should have marked folder as fetching, remove that +        cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); +        callAfterCategoryFetch(cat_id, cb); +    } +} +  void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)  { -	CallAfterCategoryFetchStage1 *stage1 = new CallAfterCategoryFetchStage1(cat_id, cb); -	stage1->startFetch(); -	if (stage1->isFinished()) -	{ -		stage1->done(); -	} -	else -	{ -		gInventory.addObserver(stage1); -	} +    CallAfterCategoryFetchStage1* stage1 = new CallAfterCategoryFetchStage1(cat_id, cb); +    stage1->startFetch(); +    if (stage1->isFinished()) +    { +        stage1->done(); +    } +    else +    { +        gInventory.addObserver(stage1); +    } +} + +void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb) +{ +    LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); +    if (AISAPI::isAvailable()) +    { +        // Mark folder (update timer) so that background fetch won't request it +        cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE); +        // Assume that we have no relevant cache. Fetch folder, and items folder's links point to. +        AISAPI::FetchCategoryLinks(cat_id, +            [cb, cat_id](const LLUUID &id) +            { +                cb(); +                LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); +                if (cat) +                { +                    cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); +                } +            }); +        } +        else +        { +            LL_WARNS() << "AIS API v3 not available, can't use AISAPI::FetchCOF" << LL_ENDL; +            // startup should have marked folder as fetching, remove that +            cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); +            callAfterCategoryFetch(cat_id, cb); +        } +      }  void add_wearable_type_counts(const uuid_vec_t& ids, diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index cf953d21ac..43839e47a6 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -338,7 +338,9 @@ public:  LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name);  // Invoke a given callable after category contents are fully fetched. +void callAfterCOFFetch(nullary_func_t cb);  void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb); +void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb);  // Wear all items in a uuid vector.  void wear_multiple(const uuid_vec_t& ids, bool replace); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1de0ed3f93..3991b069e6 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -216,7 +216,7 @@  #include "llcommandlineparser.h"  #include "llfloatermemleak.h"  #include "llfloaterreg.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h"  #include "llfloatersnapshot.h"  #include "llsidepanelinventory.h"  #include "llatmosphere.h" @@ -1537,7 +1537,7 @@ bool LLAppViewer::doFrame()                      LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Snapshot" )                      pingMainloopTimeout("Main:Snapshot");                      LLFloaterSnapshot::update(); // take snapshots -                    LLFloaterSimpleOutfitSnapshot::update(); +                    LLFloaterSimpleSnapshot::update();                      gGLActive = FALSE;                  } diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index 1feefc3ef8..d3fce306bc 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -240,6 +240,13 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments()              return;          } +        if (LLAppearanceMgr::instance().getCOFVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) +        { +            // Wait for cof to load +            LL_DEBUGS_ONCE("Avatar") << "Received atachments, but cof isn't loaded yet, postponing processing" << LL_ENDL; +            return; +        } +          LL_DEBUGS("Avatar") << "ATT checking COF linkability for " << mRecentlyArrivedAttachments.size()                              << " recently arrived items" << LL_ENDL; diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 3e450e6dec..313339f131 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -730,39 +730,55 @@ namespace action_give_inventory  	/**  	 * Checks My Inventory visibility.  	 */ +    static bool is_give_inventory_acceptable_ids(const std::set<LLUUID> inventory_selected_uuids) +    { +        if (inventory_selected_uuids.empty()) return false; // nothing selected + +        bool acceptable = false; +        std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin(); +        const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end(); +        for (; it != it_end; ++it) +        { +            LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); +            // any category can be offered. +            if (inv_cat) +            { +                acceptable = true; +                continue; +            } + +            LLViewerInventoryItem* inv_item = gInventory.getItem(*it); +            // check if inventory item can be given +            if (LLGiveInventory::isInventoryGiveAcceptable(inv_item)) +            { +                acceptable = true; +                continue; +            } + +            // there are neither item nor category in inventory +            acceptable = false; +            break; +        } +    return acceptable; +    }  	static bool is_give_inventory_acceptable(LLInventoryPanel* panel = NULL)  	{  		// check selection in the panel -		const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel); -		if (inventory_selected_uuids.empty()) return false; // nothing selected - -		bool acceptable = false; -		std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin(); -		const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end(); -		for (; it != it_end; ++it) -		{ -			LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); -			// any category can be offered. -			if (inv_cat) -			{ -				acceptable = true; -				continue; -			} - -			LLViewerInventoryItem* inv_item = gInventory.getItem(*it); -			// check if inventory item can be given -			if (LLGiveInventory::isInventoryGiveAcceptable(inv_item)) -			{ -				acceptable = true; -				continue; -			} +        std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel); +		if (inventory_selected_uuids.empty()) +        { +            if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode()) +            { +                inventory_selected_uuids.insert(panel->getRootFolderID()); +            } +            else +            { +                return false; // nothing selected +            } +        } -			// there are neither item nor category in inventory -			acceptable = false; -			break; -		} -		return acceptable; +        return is_give_inventory_acceptable_ids(inventory_selected_uuids);  	}  	static void build_items_string(const std::set<LLUUID>& inventory_selected_uuids , std::string& items_string) @@ -890,46 +906,65 @@ namespace action_give_inventory  	 * @param avatar_names - avatar names request to be sent.  	 * @param avatar_uuids - avatar names request to be sent.  	 */ -	static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, LLInventoryPanel* panel = NULL) -	{ -		llassert(avatar_names.size() == avatar_uuids.size()); -		const std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel); -		if (inventory_selected_uuids.empty()) -		{ -			return; -		} +    static void give_inventory_ids(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, const uuid_set_t inventory_selected_uuids) +    { +        llassert(avatar_names.size() == avatar_uuids.size()); -		std::string residents; -		LLAvatarActions::buildResidentsString(avatar_names, residents, true); +        if (inventory_selected_uuids.empty()) +        { +            return; +        } -		std::string items; -		build_items_string(inventory_selected_uuids, items); +        std::string residents; +        LLAvatarActions::buildResidentsString(avatar_names, residents, true); -		int folders_count = 0; -		std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin(); +        std::string items; +        build_items_string(inventory_selected_uuids, items); -		//traverse through selected inventory items and count folders among them -		for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it) -		{ -			LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); -			if (NULL != inv_cat) -			{ -				folders_count++; -			} -		} +        int folders_count = 0; +        std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin(); -		// EXP-1599 -		// In case of sharing multiple folders, make the confirmation -		// dialog contain a warning that only one folder can be shared at a time. -		std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation"; -		LLSD substitutions; -		substitutions["RESIDENTS"] = residents; -		substitutions["ITEMS"] = items; -		LLShareInfo::instance().mAvatarNames = avatar_names; -		LLShareInfo::instance().mAvatarUuids = avatar_uuids; -		LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids)); -	} +        //traverse through selected inventory items and count folders among them +        for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it) +        { +            LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); +            if (NULL != inv_cat) +            { +                folders_count++; +            } +        } + +        // EXP-1599 +        // In case of sharing multiple folders, make the confirmation +        // dialog contain a warning that only one folder can be shared at a time. +        std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation"; +        LLSD substitutions; +        substitutions["RESIDENTS"] = residents; +        substitutions["ITEMS"] = items; +        LLShareInfo::instance().mAvatarNames = avatar_names; +        LLShareInfo::instance().mAvatarUuids = avatar_uuids; +        LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids)); +    } + +    static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector<LLAvatarName> avatar_names, LLInventoryPanel* panel = NULL) +    { +        llassert(avatar_names.size() == avatar_uuids.size()); +        std::set<LLUUID> inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);; + +        if (inventory_selected_uuids.empty()) +        { +            if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode()) +            { +                inventory_selected_uuids.insert(panel->getRootFolderID()); +            } +            else +            { +                return; +            } +        } +        give_inventory_ids(avatar_uuids, avatar_names, inventory_selected_uuids); +    }  }  // static @@ -1037,6 +1072,28 @@ void LLAvatarActions::shareWithAvatars(LLView * panel)  	LLNotificationsUtil::add("ShareNotification");  } +//static +void LLAvatarActions::shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater) +{ +    using namespace action_give_inventory; + +    LLFloaterAvatarPicker* picker = +        LLFloaterAvatarPicker::show(boost::bind(give_inventory_ids, _1, _2, inventory_selected_uuids), TRUE, FALSE, FALSE, root_floater->getName()); +    if (!picker) +    { +        return; +    } + +    picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable_ids, inventory_selected_uuids)); +    picker->openFriendsTab(); +     +    if (root_floater) +    { +        root_floater->addDependentFloater(picker); +    } +    LLNotificationsUtil::add("ShareNotification"); +} +  // static  bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NULL*/)  { diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 86183cc119..8a0f40dd52 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -133,6 +133,7 @@ public:  	 * Share items with the picked avatars.  	 */  	static void shareWithAvatars(LLView * panel); +    static void shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater);  	/**  	 * Block/unblock the avatar by id. diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index 7c6980a7e6..3f607d434e 100644 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -111,6 +111,7 @@ public:  	virtual void previewItem( void );  	virtual void selectItem(void) { }   	virtual void showProperties(void); +    virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {}  	// Methods used in sorting (see LLConversationSort::operator())  	EConversationType const getType() const { return mConvType; } @@ -249,7 +250,7 @@ public:  	bool 				check(const LLFolderViewModelItem* item) { return true; }  	bool				checkFolder(const LLFolderViewModelItem* folder) const { return true; }  	void 				setEmptyLookupMessage(const std::string& message) { } -	std::string			getEmptyLookupMessage() const { return mEmpty; } +	std::string			getEmptyLookupMessage(bool is_empty_folder = false) const { return mEmpty; }  	bool				showAllResults() const { return true; }  	std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const { return std::string::npos; }  	std::string::size_type getFilterStringSize() const { return 0; } diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp new file mode 100644 index 0000000000..780130039b --- /dev/null +++ b/indra/newview/llfloaterchangeitemthumbnail.cpp @@ -0,0 +1,956 @@ +/**  + * @file llfloaterchangeitemthumbnail.cpp + * @brief LLFloaterChangeItemThumbnail class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterchangeitemthumbnail.h" + +#include "llbutton.h" +#include "llclipboard.h" +#include "lliconctrl.h" +#include "llinventoryfunctions.h" +#include "llinventoryicon.h" +#include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llfloaterreg.h" +#include "llfloatersimplesnapshot.h" +#include "lllineeditor.h" +#include "llnotificationsutil.h" +#include "lltextbox.h" +#include "lltexturectrl.h" +#include "llthumbnailctrl.h" +#include "llviewerfoldertype.h" +#include "llviewermenufile.h" +#include "llviewerobjectlist.h" +#include "llviewertexturelist.h" +#include "llwindow.h" + + +class LLThumbnailImagePicker : public LLFilePickerThread +{ +public: +    LLThumbnailImagePicker(const LLUUID &item_id); +    LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id); +    ~LLThumbnailImagePicker(); +    void notify(const std::vector<std::string>& filenames) override; + +private: +    LLUUID mInventoryId; +    LLUUID mTaskId; +}; + +LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id) +    : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE) +    , mInventoryId(item_id) +{ +} + +LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id) +    : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE) +    , mInventoryId(item_id) +    , mTaskId(task_id) +{ +} + +LLThumbnailImagePicker::~LLThumbnailImagePicker() +{ +} + +void LLThumbnailImagePicker::notify(const std::vector<std::string>& filenames) +{ +    if (filenames.empty()) +    { +        return; +    } +    std::string file_path = filenames[0]; +    if (file_path.empty()) +    { +        return; +    } +     +    LLFloaterSimpleSnapshot::uploadThumbnail(file_path, mInventoryId, mTaskId); +} + +LLFloaterChangeItemThumbnail::LLFloaterChangeItemThumbnail(const LLSD& key) +    : LLFloater(key) +    , mObserverInitialized(false) +    , mTooltipState(TOOLTIP_NONE) +{ +} + +LLFloaterChangeItemThumbnail::~LLFloaterChangeItemThumbnail() +{ +    gInventory.removeObserver(this); +    removeVOInventoryListener(); +} + +BOOL LLFloaterChangeItemThumbnail::postBuild() +{ +    mItemNameText = getChild<LLUICtrl>("item_name"); +    mItemTypeIcon = getChild<LLIconCtrl>("item_type_icon"); +    mThumbnailCtrl = getChild<LLThumbnailCtrl>("item_thumbnail"); +    mToolTipTextBox = getChild<LLTextBox>("tooltip_text"); + +    LLSD tooltip_text; +    mToolTipTextBox->setValue(tooltip_text); + +    LLButton *upload_local = getChild<LLButton>("upload_local"); +    upload_local->setClickedCallback(onUploadLocal, (void*)this); +    upload_local->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_LOCAL)); +    upload_local->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_LOCAL)); + +    LLButton *upload_snapshot = getChild<LLButton>("upload_snapshot"); +    upload_snapshot->setClickedCallback(onUploadSnapshot, (void*)this); +    upload_snapshot->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT)); +    upload_snapshot->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT)); + +    LLButton *use_texture = getChild<LLButton>("use_texture"); +    use_texture->setClickedCallback(onUseTexture, (void*)this); +    use_texture->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_USE_TEXTURE)); +    use_texture->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_USE_TEXTURE)); + +    mCopyToClipboardBtn = getChild<LLButton>("copy_to_clipboard"); +    mCopyToClipboardBtn->setClickedCallback(onCopyToClipboard, (void*)this); +    mCopyToClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD)); +    mCopyToClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD)); + +    mPasteFromClipboardBtn = getChild<LLButton>("paste_from_clipboard"); +    mPasteFromClipboardBtn->setClickedCallback(onPasteFromClipboard, (void*)this); +    mPasteFromClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD)); +    mPasteFromClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD)); + +    mRemoveImageBtn = getChild<LLButton>("remove_image"); +    mRemoveImageBtn->setClickedCallback(onRemove, (void*)this); +    mRemoveImageBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_REMOVE)); +    mRemoveImageBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_REMOVE)); + +    return LLFloater::postBuild(); +} + +void LLFloaterChangeItemThumbnail::onOpen(const LLSD& key) +{ +    if (!key.has("item_id") && !key.isUUID()) +    { +        closeFloater(); +    } + +    if (key.isUUID()) +    { +        mItemId = key.asUUID(); +    } +    else +    { +        mItemId = key["item_id"].asUUID(); +        mTaskId = key["task_id"].asUUID(); +    } + +    refreshFromInventory(); +} + +void LLFloaterChangeItemThumbnail::onFocusReceived() +{ +    mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); +} + +void LLFloaterChangeItemThumbnail::onMouseEnter(S32 x, S32 y, MASK mask) +{ +    mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); +} + +BOOL LLFloaterChangeItemThumbnail::handleDragAndDrop( +    S32 x, +    S32 y, +    MASK mask, +    BOOL drop, +    EDragAndDropType cargo_type, +    void *cargo_data, +    EAcceptance *accept, +    std::string& tooltip_msg) +{ +    if (cargo_type == DAD_TEXTURE) +    { +        LLInventoryItem *item = (LLInventoryItem *)cargo_data; +        if (item->getAssetUUID().notNull()) +        { +            if (drop) +            { +                assignAndValidateAsset(item->getAssetUUID()); +            } + +            *accept = ACCEPT_YES_SINGLE; +        } +        else +        { +            *accept = ACCEPT_NO; +        } +    } +    else +    { +        *accept = ACCEPT_NO; +    } + +    LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFloaterChangeItemThumbnail " << getKey() << LL_ENDL; + +    return TRUE; +} + +void LLFloaterChangeItemThumbnail::changed(U32 mask) +{ +    //LLInventoryObserver + +    if (mTaskId.notNull() || mItemId.isNull()) +    { +        // Task inventory or not set up yet +        return; +    } + +    const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs(); +    std::set<LLUUID>::const_iterator it; + +    for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) +    { +        // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288) +        if (*it == mItemId) +        { +            // if there's a change we're interested in. +            if ((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) +            { +                refreshFromInventory(); +            } +        } +    } +} + +void LLFloaterChangeItemThumbnail::inventoryChanged(LLViewerObject* object, +    LLInventoryObject::object_list_t* inventory, +    S32 serial_num, +    void* user_data) +{ +    //LLVOInventoryListener +    refreshFromInventory(); +} + +LLInventoryObject* LLFloaterChangeItemThumbnail::getInventoryObject() +{ +    LLInventoryObject* obj = NULL; +    if (mTaskId.isNull()) +    { +        // it is in agent inventory +        if (!mObserverInitialized) +        { +            gInventory.addObserver(this); +            mObserverInitialized = true; +        } + +        obj = gInventory.getObject(mItemId); +    } +    else +    { +        LLViewerObject* object = gObjectList.findObject(mTaskId); +        if (object) +        { +            if (!mObserverInitialized) +            { +                registerVOInventoryListener(object, NULL); +                mObserverInitialized = false; +            } + +            obj = object->getInventoryObject(mItemId); +        } +    } +    return obj; +} + +void LLFloaterChangeItemThumbnail::refreshFromInventory() +{ +    LLInventoryObject* obj = getInventoryObject(); +    if (!obj) +    { +        closeFloater(); +    } + +    if (obj) +    { +        const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); +        bool in_trash = gInventory.isObjectDescendentOf(obj->getUUID(), trash_id); +        if (in_trash && obj->getUUID() != trash_id) +        { +            // Close properties when moving to trash +            // Aren't supposed to view properties from trash +            closeFloater(); +        } +        else +        { +            refreshFromObject(obj); +        } +    } +    else +    { +        closeFloater(); +    } +} + +class LLIsOutfitTextureType : public LLInventoryCollectFunctor +{ +public: +    LLIsOutfitTextureType() {} +    virtual ~LLIsOutfitTextureType() {} +    virtual bool operator()(LLInventoryCategory* cat, +        LLInventoryItem* item); +}; + +bool LLIsOutfitTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ +    return item && (item->getType() == LLAssetType::AT_TEXTURE); +} + +void LLFloaterChangeItemThumbnail::refreshFromObject(LLInventoryObject* obj) +{ +    LLUIImagePtr icon_img; +    LLUUID thumbnail_id = obj->getThumbnailUUID(); + +    LLViewerInventoryItem* item = dynamic_cast<LLViewerInventoryItem*>(obj); +    if (item) +    { +        setTitle(getString("title_item_thumbnail")); + +        icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE); +        mRemoveImageBtn->setEnabled(thumbnail_id.notNull() && ((item->getActualType() != LLAssetType::AT_TEXTURE) || (item->getAssetUUID() != thumbnail_id))); +    } +    else +    { +        LLViewerInventoryCategory* cat = dynamic_cast<LLViewerInventoryCategory*>(obj); + +        if (cat) +        { +            setTitle(getString("title_folder_thumbnail")); +            icon_img = LLUI::getUIImage(LLViewerFolderType::lookupIconName(cat->getPreferredType(), true)); + +            if (thumbnail_id.isNull() && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) +            { +                // Legacy support, check if there is an image inside + +                LLInventoryModel::cat_array_t cats; +                LLInventoryModel::item_array_t items; +                // Not LLIsOfAssetType, because we allow links +                LLIsOutfitTextureType f; +                gInventory.getDirectDescendentsOf(mItemId, cats, items, f); + +                if (1 == items.size()) +                { +                    LLViewerInventoryItem* item = items.front(); +                    if (item && item->getIsLinkType()) +                    { +                        item = item->getLinkedItem(); +                    } +                    if (item) +                    { +                        thumbnail_id = item->getAssetUUID(); +                        if (thumbnail_id.notNull()) +                        { +                            // per SL-19188, set this image as a thumbnail +                            LL_INFOS() << "Setting image " << thumbnail_id +                                       << " from outfit as a thumbnail for inventory object " << obj->getUUID() +                                       << LL_ENDL; +                            assignAndValidateAsset(thumbnail_id, true); +                        } +                    } +                } +            } + +            mRemoveImageBtn->setEnabled(thumbnail_id.notNull()); +        } +    } +    mItemTypeIcon->setImage(icon_img); +    mItemNameText->setValue(obj->getName()); + +    mThumbnailCtrl->setValue(thumbnail_id); + +    mCopyToClipboardBtn->setEnabled(thumbnail_id.notNull()); +    mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); + +    // todo: some elements might not support setting thumbnails +    // since they already have them +    // It is unclear how system folders should function +} + +void LLFloaterChangeItemThumbnail::onUploadLocal(void *userdata) +{ +    LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + +    (new LLThumbnailImagePicker(self->mItemId, self->mTaskId))->getFile(); + +    LLFloater* floaterp = self->mPickerHandle.get(); +    if (floaterp) +    { +        floaterp->closeFloater(); +    } +    floaterp = self->mSnapshotHandle.get(); +    if (floaterp) +    { +        floaterp->closeFloater(); +    } +} + +void LLFloaterChangeItemThumbnail::onUploadSnapshot(void *userdata) +{ +    LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + +    LLFloater* floaterp = self->mSnapshotHandle.get(); +    // Show the dialog +    if (floaterp) +    { +        floaterp->openFloater(); +    } +    else +    { +        LLSD key; +        key["item_id"] = self->mItemId; +        key["task_id"] = self->mTaskId; +        LLFloaterSimpleSnapshot* snapshot_floater = (LLFloaterSimpleSnapshot*)LLFloaterReg::showInstance("simple_snapshot", key, true); +        if (snapshot_floater) +        { +            self->addDependentFloater(snapshot_floater); +            self->mSnapshotHandle = snapshot_floater->getHandle(); +            snapshot_floater->setOwner(self); +        } +    } + +    floaterp = self->mPickerHandle.get(); +    if (floaterp) +    { +        floaterp->closeFloater(); +    } +} + +void LLFloaterChangeItemThumbnail::onUseTexture(void *userdata) +{ +    LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; +    LLInventoryObject* obj = self->getInventoryObject(); +    if (obj) +    { +        self->showTexturePicker(obj->getThumbnailUUID()); +    } + +    LLFloater* floaterp = self->mSnapshotHandle.get(); +    if (floaterp) +    { +        floaterp->closeFloater(); +    } +} + +void LLFloaterChangeItemThumbnail::onCopyToClipboard(void *userdata) +{ +    LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; +    LLInventoryObject* obj = self->getInventoryObject(); +    if (obj) +    { +        LLClipboard::instance().reset(); +        LLClipboard::instance().addToClipboard(obj->getThumbnailUUID(), LLAssetType::AT_NONE); +        self->mPasteFromClipboardBtn->setEnabled(true); +    } +} + +void LLFloaterChangeItemThumbnail::onPasteFromClipboard(void *userdata) +{ +    LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; +    std::vector<LLUUID> objects; +    LLClipboard::instance().pasteFromClipboard(objects); +    if (objects.size() > 0) +    { +        LLUUID potential_uuid = objects[0]; +        LLUUID asset_id; + +        if (potential_uuid.notNull()) +        { +            LLViewerInventoryItem* item = gInventory.getItem(potential_uuid); +            if (item) +            { +                // no point checking snapshot? +                if (item->getType() == LLAssetType::AT_TEXTURE) +                { +                    bool copy = item->getPermissions().allowCopyBy(gAgent.getID()); +                    bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + +                    if (copy && xfer) +                    { +                        asset_id = item->getAssetUUID(); +                    } +                    else +                    { +                        LLNotificationsUtil::add("ThumbnailInsufficientPermissions"); +                        return; +                    } +                } +            } +            else +            { +                // assume that this is a texture +                asset_id = potential_uuid; +            } +        } + +        LLInventoryObject* obj = self->getInventoryObject(); +        if (obj && obj->getThumbnailUUID() == asset_id) +        { +            // nothing to do +            return; +        } +        if (asset_id.notNull()) +        { +            self->assignAndValidateAsset(asset_id); +        } +        // else show 'buffer has no texture' warning? +    } +} + +void LLFloaterChangeItemThumbnail::onRemove(void *userdata) +{ +    LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + +    LLSD payload; +    payload["item_id"] = self->mItemId; +    payload["object_id"] = self->mTaskId; +    LLNotificationsUtil::add("DeleteThumbnail", LLSD(), payload, boost::bind(&LLFloaterChangeItemThumbnail::onRemovalConfirmation, _1, _2, self->getHandle())); +} + +// static  +void LLFloaterChangeItemThumbnail::onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle) +{ +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +    if (option == 0 && !handle.isDead() && !handle.get()->isDead()) +    { +        LLFloaterChangeItemThumbnail* self = (LLFloaterChangeItemThumbnail*)handle.get(); +        self->setThumbnailId(LLUUID::null); +    } +} + +struct ImageLoadedData +{ +    LLUUID mThumbnailId; +    LLUUID mObjectId; +    LLHandle<LLFloater> mFloaterHandle; +    bool mSilent; +    // Keep image reference to prevent deletion on timeout +    LLPointer<LLViewerFetchedTexture> mTexturep; +}; + +void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id, bool silent) +{ +    LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::getFetchedTexture(asset_id); +    if (texturep->isMissingAsset()) +    { +        LL_WARNS() << "Attempted to assign missing asset " << asset_id << LL_ENDL; +        if (!silent) +        { +            LLNotificationsUtil::add("ThumbnailDimentionsLimit"); +        } +    } +    else if (texturep->getFullWidth() == 0) +    { +        if (silent) +        { +            mExpectingAssetId = LLUUID::null; +        } +        else +        { +            // don't warn user multiple times if some textures took their time +            mExpectingAssetId = asset_id; +        } +        ImageLoadedData *data = new ImageLoadedData(); +        data->mObjectId = mItemId; +        data->mThumbnailId = asset_id; +        data->mFloaterHandle = getHandle(); +        data->mSilent = silent; +        data->mTexturep = texturep; + +        texturep->setLoadedCallback(onImageDataLoaded, +            MAX_DISCARD_LEVEL, // Don't need full image, just size data +            FALSE, +            FALSE, +            (void*)data, +            NULL, +            FALSE); +    } +    else +    { +        if (validateAsset(asset_id)) +        { +            setThumbnailId(asset_id); +        } +        else if (!silent) +        { +            LLNotificationsUtil::add("ThumbnailDimentionsLimit"); +        } +    } +} +bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id) +{ +    if (asset_id.isNull()) +    { +        return false; +    } + +    LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD); + +    if (!texturep) +    { +        return false; +    } + +    if (texturep->isMissingAsset()) +    { +        return false; +    } + +    if (texturep->getFullWidth() != texturep->getFullHeight()) +    { +        return false; +    } + +    if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX +        || texturep->getFullHeight() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX) +    { +        return false; +    } + +    if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN +        || texturep->getFullHeight() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN) +    { +        return false; +    } +    return true; +} + +//static +void LLFloaterChangeItemThumbnail::onImageDataLoaded( +    BOOL success, +    LLViewerFetchedTexture *src_vi, +    LLImageRaw* src, +    LLImageRaw* aux_src, +    S32 discard_level, +    BOOL final, +    void* userdata) +{ +    if (!userdata) return; + +    if (!final && success) return; //not done yet + +    ImageLoadedData* data = (ImageLoadedData*)userdata; + +    if (success) +    { +        // Update the item, set it even if floater is dead +        if (validateAsset(data->mThumbnailId)) +        { +            setThumbnailId(data->mThumbnailId, data->mObjectId); +        } +        else if (!data->mSilent) +        { +            // Should this only appear if floater is alive? +            LLNotificationsUtil::add("ThumbnailDimentionsLimit"); +        } +    } + +    // Update floater +    if (!data->mSilent && !data->mFloaterHandle.isDead()) +    { +        LLFloaterChangeItemThumbnail* self = static_cast<LLFloaterChangeItemThumbnail*>(data->mFloaterHandle.get()); +        if (self && self->mExpectingAssetId == data->mThumbnailId) +        { +            self->mExpectingAssetId = LLUUID::null; +        } +    } + +    delete data; +} + +//static +void LLFloaterChangeItemThumbnail::onFullImageLoaded( +    BOOL success, +    LLViewerFetchedTexture* src_vi, +    LLImageRaw* src, +    LLImageRaw* aux_src, +    S32 discard_level, +    BOOL final, +    void* userdata) +{ +    if (!userdata) return; + +    if (!final && success) return; //not done yet + +    ImageLoadedData* data = (ImageLoadedData*)userdata; + +    if (success) +    { +        if (src_vi->getFullWidth() != src_vi->getFullHeight() +            || src_vi->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN) +        { +            if (!data->mSilent) +            { +                LLNotificationsUtil::add("ThumbnailDimentionsLimit"); +            } +        } +        else if (src_vi->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX) +        { +            LLFloaterSimpleSnapshot::uploadThumbnail(src, data->mObjectId, LLUUID::null); +        } +        else +        { +            setThumbnailId(data->mThumbnailId, data->mObjectId); +        } +    } + +    delete data; +} + +void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id) +{ +    // show hourglass cursor when loading inventory window +    getWindow()->setCursor(UI_CURSOR_WAIT); + +    LLFloater* floaterp = mPickerHandle.get(); +    // Show the dialog +    if (floaterp) +    { +        floaterp->openFloater(); +    } +    else +    { +        floaterp = new LLFloaterTexturePicker( +            this, +            thumbnail_id, +            thumbnail_id, +            thumbnail_id, +            FALSE, +            TRUE, +            "SELECT PHOTO", +            PERM_NONE, +            PERM_NONE, +            PERM_NONE, +            FALSE, +            NULL); + +        mPickerHandle = floaterp->getHandle(); + +        LLFloaterTexturePicker* texture_floaterp = dynamic_cast<LLFloaterTexturePicker*>(floaterp); +        if (texture_floaterp) +        { +            //texture_floaterp->setTextureSelectedCallback(); +            //texture_floaterp->setOnUpdateImageStatsCallback(); +            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource, const LLUUID&, const LLUUID&) +            { +                if (op == LLTextureCtrl::TEXTURE_SELECT) +                { +                    onTexturePickerCommit(); +                } +            } +            ); + +            texture_floaterp->setLocalTextureEnabled(FALSE); +            texture_floaterp->setBakeTextureEnabled(FALSE); +            texture_floaterp->setCanApplyImmediately(false); +            texture_floaterp->setCanApply(false, true, false /*Hide 'preview disabled'*/); +            texture_floaterp->setMinDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN); + +            addDependentFloater(texture_floaterp); +        } + +        floaterp->openFloater(); +    } +    floaterp->setFocus(TRUE); +} + +void LLFloaterChangeItemThumbnail::onTexturePickerCommit() +{ +    LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mPickerHandle.get(); + +    if (floaterp) +    { +        LLUUID asset_id = floaterp->getAssetID(); + +        if (asset_id.isNull()) +        { +            setThumbnailId(asset_id); +            return; +        } + +        LLInventoryObject* obj = getInventoryObject(); +        if (obj && obj->getThumbnailUUID() == asset_id) +        { +            // nothing to do +            return; +        } + +        LLPointer<LLViewerFetchedTexture> texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD); +        if (!texturep) +        { +            LL_WARNS() << "Image " << asset_id << " doesn't exist" << LL_ENDL; +            return; +        } + +        if (texturep->isMissingAsset()) +        { +            LL_WARNS() << "Image " << asset_id << " is missing" << LL_ENDL; +            return; +        } + +        if (texturep->getFullWidth() != texturep->getFullHeight()) +        { +            LLNotificationsUtil::add("ThumbnailDimentionsLimit"); +            return; +        } + +        if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN +            && texturep->getFullWidth() > 0) +        { +            LLNotificationsUtil::add("ThumbnailDimentionsLimit"); +            return; +        } + +        if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX +            || texturep->getFullWidth() == 0) +        { +            if (texturep->isFullyLoaded() +                && (texturep->getCachedRawImageLevel() == 0 || texturep->getRawImageLevel() == 0) +                && (texturep->isCachedRawImageReady() || texturep->isRawImageValid())) +            { +                if (texturep->isRawImageValid()) +                { +                    LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getRawImage(), mItemId, mTaskId); +                } +                else +                { +                    LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getCachedRawImage(), mItemId, mTaskId); +                } +            } +            else +            { +                ImageLoadedData* data = new ImageLoadedData(); +                data->mObjectId = mItemId; +                data->mThumbnailId = asset_id; +                data->mFloaterHandle = getHandle(); +                data->mSilent = false; +                data->mTexturep = texturep; + +                texturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW); +                texturep->setMinDiscardLevel(0); +                texturep->setLoadedCallback(onFullImageLoaded, +                                            0, // Need best quality +                                            TRUE, +                                            FALSE, +                                            (void*)data, +                                            NULL, +                                            FALSE); +                texturep->forceToSaveRawImage(0); +            } +            return; +        } + +        setThumbnailId(asset_id); +    } +} + + +void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID &new_thumbnail_id) +{ +    LLInventoryObject* obj = getInventoryObject(); +    if (!obj) +    { +        return; +    } + +    if (mTaskId.notNull()) +    { +        LL_ERRS() << "Not implemented yet" << LL_ENDL; +        return; +    } + +    setThumbnailId(new_thumbnail_id, mItemId, obj); +} + +void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id) +{ +    LLInventoryObject* obj = gInventory.getObject(object_id); +    if (!obj) +    { +        return; +    } + +    setThumbnailId(new_thumbnail_id, object_id, obj); +} +void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj) +{ +    if (obj->getThumbnailUUID() != new_thumbnail_id) +    { +        LLSD updates; +        if (new_thumbnail_id.notNull()) +        { +            // At the moment server expects id as a string +            updates["thumbnail"] = LLSD().with("asset_id", new_thumbnail_id.asString()); +        } +        else +        { +            // No thumbnail isntead of 'null id thumbnail' +            updates["thumbnail"] = LLSD(); +        } +        LLViewerInventoryCategory* view_folder = dynamic_cast<LLViewerInventoryCategory*>(obj); +        if (view_folder) +        { +            update_inventory_category(object_id, updates, NULL); +        } +        LLViewerInventoryItem* view_item = dynamic_cast<LLViewerInventoryItem*>(obj); +        if (view_item) +        { +            update_inventory_item(object_id, updates, NULL); +        } +    } +} + +void LLFloaterChangeItemThumbnail::onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state) +{ +    mTooltipState = state; + +    std::string tooltip_text; +    std::string tooltip_name = "tooltip_" + button->getName(); +    if (hasString(tooltip_name)) +    { +        tooltip_text = getString(tooltip_name); +    } + +    mToolTipTextBox->setValue(tooltip_text); +} + +void LLFloaterChangeItemThumbnail::onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state) +{ +    if (mTooltipState == state) +    { +        mTooltipState = TOOLTIP_NONE; +        LLSD tooltip_text; +        mToolTipTextBox->setValue(tooltip_text); +    } +} + diff --git a/indra/newview/llfloaterchangeitemthumbnail.h b/indra/newview/llfloaterchangeitemthumbnail.h new file mode 100644 index 0000000000..a91e9b8ee9 --- /dev/null +++ b/indra/newview/llfloaterchangeitemthumbnail.h @@ -0,0 +1,139 @@ +/**  + * @file llfloaterchangeitemthumbnail.h + * @brief LLFloaterChangeItemThumbnail class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERCHANGEITEMTHUMBNAIL_H +#define LL_LLFLOATERCHANGEITEMTHUMBNAIL_H + +#include "llfloater.h" +#include "llinventoryobserver.h" +#include "llvoinventorylistener.h" + +class LLButton; +class LLIconCtrl; +class LLTextBox; +class LLThumbnailCtrl; +class LLUICtrl; +class LLViewerInventoryItem; +class LLViewerFetchedTexture; + +class LLFloaterChangeItemThumbnail : public LLFloater, public LLInventoryObserver, public LLVOInventoryListener +{ +public: +    LLFloaterChangeItemThumbnail(const LLSD& key); +	~LLFloaterChangeItemThumbnail(); + +    BOOL postBuild() override; +    void onOpen(const LLSD& key) override; +    void onFocusReceived() override; +    void onMouseEnter(S32 x, S32 y, MASK mask) override; + +    BOOL handleDragAndDrop( +        S32 x, +        S32 y, +        MASK mask, +        BOOL drop, +        EDragAndDropType cargo_type, +        void *cargo_data, +        EAcceptance *accept, +        std::string& tooltip_msg) override; + +    void changed(U32 mask) override; +    void inventoryChanged(LLViewerObject* object, +        LLInventoryObject::object_list_t* inventory, +        S32 serial_num, +        void* user_data) override; + +    static bool validateAsset(const LLUUID &asset_id); + +private: + +    LLInventoryObject* getInventoryObject(); +    void refreshFromInventory(); +    void refreshFromObject(LLInventoryObject* obj); + +    static void onUploadLocal(void*); +    static void onUploadSnapshot(void*); +    static void onUseTexture(void*); +    static void onCopyToClipboard(void*); +    static void onPasteFromClipboard(void*); +    static void onRemove(void*); +    static void onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle<LLFloater> handle); + +    void assignAndValidateAsset(const LLUUID &asset_id, bool silent = false); +    static void onImageDataLoaded(BOOL success, +        LLViewerFetchedTexture *src_vi, +        LLImageRaw* src, +        LLImageRaw* aux_src, +        S32 discard_level, +        BOOL final, +        void* userdata); +    static void onFullImageLoaded(BOOL success, +                                  LLViewerFetchedTexture* src_vi, +                                  LLImageRaw* src, +                                  LLImageRaw* aux_src, +                                  S32 discard_level, +                                  BOOL final, +                                  void* userdata); + +    void showTexturePicker(const LLUUID &thumbnail_id); +    void onTexturePickerCommit(); + +    void setThumbnailId(const LLUUID &new_thumbnail_id); +    static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id); +    static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj); + +    enum EToolTipState +    { +        TOOLTIP_NONE, +        TOOLTIP_UPLOAD_LOCAL, +        TOOLTIP_UPLOAD_SNAPSHOT, +        TOOLTIP_USE_TEXTURE, +        TOOLTIP_COPY_TO_CLIPBOARD, +        TOOLTIP_COPY_FROM_CLIPBOARD, +        TOOLTIP_REMOVE, +    }; + +    void onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state); +    void onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state); + +    bool mObserverInitialized; +    EToolTipState mTooltipState; +    LLUUID mItemId; +    LLUUID mTaskId; +    LLUUID mExpectingAssetId; + +    LLIconCtrl *mItemTypeIcon; +    LLUICtrl *mItemNameText; +    LLThumbnailCtrl *mThumbnailCtrl; +    LLTextBox *mToolTipTextBox; +    LLButton *mCopyToClipboardBtn; +    LLButton *mPasteFromClipboardBtn; +    LLButton *mRemoveImageBtn; + +    LLHandle<LLFloater> mPickerHandle; +    LLHandle<LLFloater> mSnapshotHandle; +}; +#endif  // LL_LLFLOATERCHANGEITEMTHUMBNAIL_H 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/llfloaterforgetuser.cpp b/indra/newview/llfloaterforgetuser.cpp index 97b022699f..f576ce7a76 100644 --- a/indra/newview/llfloaterforgetuser.cpp +++ b/indra/newview/llfloaterforgetuser.cpp @@ -164,6 +164,12 @@ bool LLFloaterForgetUser::onConfirmLogout(const LLSD& notification, const LLSD&      if (option == 0)      {          // Remove creds +        std::string grid_id = LLGridManager::getInstance()->getGridId(grid); +        if (grid_id.empty()) +        { +            grid_id = grid; +        } +        gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, LLStartUp::getUserId()); // doesn't write          gSecAPIHandler->removeFromCredentialMap("login_list", grid, LLStartUp::getUserId());          LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(grid); @@ -228,7 +234,13 @@ void LLFloaterForgetUser::processForgetUser()  void LLFloaterForgetUser::forgetUser(const std::string &userid, const std::string &fav_id, const std::string &grid, bool delete_data)  {      // Remove creds -    gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid); +    std::string grid_id = LLGridManager::getInstance()->getGridId(grid); +    if (grid_id.empty()) +    { +        grid_id = grid; +    } +    gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, userid); // doesn't write +    gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid); // write operation      LLPointer<LLCredential> cred = gSecAPIHandler->loadCredential(grid);      if (cred.notNull() && cred->userID() == userid) diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index d17889bed1..f29046c513 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -211,7 +211,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/llfloaterinventorysettings.cpp b/indra/newview/llfloaterinventorysettings.cpp new file mode 100644 index 0000000000..29d6e90a33 --- /dev/null +++ b/indra/newview/llfloaterinventorysettings.cpp @@ -0,0 +1,44 @@ +/** + * @file llfloaterinventorysettings.cpp + * @brief LLFloaterInventorySettings class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterinventorysettings.h" + +LLFloaterInventorySettings::LLFloaterInventorySettings(const LLSD& key) +  : LLFloater(key) +{ +} + +LLFloaterInventorySettings::~LLFloaterInventorySettings() +{} + +BOOL LLFloaterInventorySettings::postBuild() +{ +    getChild<LLButton>("ok_btn")->setCommitCallback(boost::bind(&LLFloater::closeFloater, this, false)); +    return TRUE; +} + diff --git a/indra/newview/llfloaterinventorysettings.h b/indra/newview/llfloaterinventorysettings.h new file mode 100644 index 0000000000..50304276c7 --- /dev/null +++ b/indra/newview/llfloaterinventorysettings.h @@ -0,0 +1,45 @@ +/** + * @file llfloaterinventorysettings.h + * @brief LLFloaterInventorySettings class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERINVENTORYSETTINGS_H +#define LL_LLFLOATERINVENTORYSETTINGS_H + +#include "llfloater.h" + +class LLFloaterInventorySettings +    :    public LLFloater +{ +    friend class LLFloaterReg; + +public: +    virtual BOOL postBuild(); + +private: +    LLFloaterInventorySettings(const LLSD& key); +    ~LLFloaterInventorySettings(); +}; + +#endif diff --git a/indra/newview/llfloaterlinkreplace.cpp b/indra/newview/llfloaterlinkreplace.cpp index 8ee7a72055..b42c49c607 100644 --- a/indra/newview/llfloaterlinkreplace.cpp +++ b/indra/newview/llfloaterlinkreplace.cpp @@ -335,8 +335,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 e755e9924c..71b3b16809 100644 --- a/indra/newview/llfloatermarketplacelistings.cpp +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -41,8 +41,11 @@  #include "llnotificationmanager.h"  #include "llnotificationsutil.h"  #include "llsidepaneliteminfo.h" +#include "llsidepaneltaskinfo.h" +#include "lltabcontainer.h"  #include "lltextbox.h"  #include "lltrans.h" +#include "llviewerwindow.h"  ///----------------------------------------------------------------------------  /// LLPanelMarketplaceListings @@ -227,18 +230,31 @@ void LLPanelMarketplaceListings::onTabChange()  void LLPanelMarketplaceListings::onAddButtonClicked()  { -	// Find active panel -	LLInventoryPanel* panel = (LLInventoryPanel*)getChild<LLTabContainer>("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<LLPanel> 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<LLTabContainer>("marketplace_filter_tabs")->getCurrentPanel(); +        if (panel) +        { +            gInventory.notifyObservers(); +            panel->setSelectionByID(new_cat_id, TRUE); +            panel->getRootFolder()->setNeedsAutoRename(TRUE); +        }      } +    );  }  void LLPanelMarketplaceListings::onAuditButtonClicked() @@ -359,6 +375,7 @@ LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key)  , mInventoryTitle(NULL)  , mPanelListings(NULL)  , mPanelListingsSet(false) +, mRootFolderCreating(false)  {  } @@ -431,7 +448,7 @@ void LLFloaterMarketplaceListings::fetchContents()  	{          LLMarketplaceData::instance().setDataFetchedSignal(boost::bind(&LLFloaterMarketplaceListings::updateView, this));          LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING); -		LLInventoryModelBackgroundFetch::instance().start(mRootFolderId); +		LLInventoryModelBackgroundFetch::instance().start(mRootFolderId, true);          LLMarketplaceData::instance().getSLMListings();  	}  } @@ -444,15 +461,50 @@ 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<LLFloaterMarketplaceListings>("marketplace_listings"); +                if (marketplace) +                { +                    if (new_cat_id.notNull()) +                    { +                        // will call setRootFolder again +                        marketplace->updateView(); +                    } +                    // don't update in case of failure, createNewCategory can return +                    // immediately if cap is missing and will cause a loop +                    else +                    { +                        // unblock +                        marketplace->mRootFolderCreating = false; +                        LL_WARNS("SLM") << "Inventory warning: Failed to create marketplace listings folder for a merchant" << LL_ENDL; +                    } +                } +            } +            ); +        } +        return; +    } + +    mRootFolderCreating = false;  	// No longer need to observe new category creation  	if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) @@ -540,6 +592,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)) ) @@ -843,14 +900,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 @@ -954,18 +1014,44 @@ LLFloaterItemProperties::~LLFloaterItemProperties()  BOOL LLFloaterItemProperties::postBuild()  { -    // On the standalone properties floater, we have no need for a back button... -    LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel"); -    LLButton* back_btn = panel->getChild<LLButton>("back_btn"); -    back_btn->setVisible(FALSE); -      	return LLFloater::postBuild();  }  void LLFloaterItemProperties::onOpen(const LLSD& key)  {      // Tell the panel which item it needs to visualize -    LLSidepanelItemInfo* panel = getChild<LLSidepanelItemInfo>("item_panel"); -    panel->setItemID(key["id"].asUUID()); +    LLPanel* panel = findChild<LLPanel>("sidepanel"); +     +    LLSidepanelItemInfo* item_panel = dynamic_cast<LLSidepanelItemInfo*>(panel); +    if (item_panel) +    { +        item_panel->setItemID(key["id"].asUUID()); +        if (key.has("object")) +        { +            item_panel->setObjectID(key["object"].asUUID()); +        } +        item_panel->setParentFloater(this); +    } +     +    LLSidepanelTaskInfo* task_panel = dynamic_cast<LLSidepanelTaskInfo*>(panel); +    if (task_panel) +    { +        task_panel->setObjectSelection(LLSelectMgr::getInstance()->getSelection()); +    }  } +LLMultiItemProperties::LLMultiItemProperties(const LLSD& key) +	: LLMultiFloater(LLSD()) +{ +	// start with a small rect in the top-left corner ; will get resized +	LLRect rect; +	rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 350, 350); +	setRect(rect); +	LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup(key.asString()); +	if (last_floater) +	{ +		stackWith(*last_floater); +	} +	setTitle(LLTrans::getString("MultiPropertiesTitle")); +	buildTabContainer(); +} diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h index ffc098e28a..085e517a9d 100644 --- a/indra/newview/llfloatermarketplacelistings.h +++ b/indra/newview/llfloatermarketplacelistings.h @@ -33,6 +33,7 @@  #include "llinventorypanel.h"  #include "llnotificationptr.h"  #include "llmodaldialog.h" +#include "llmultifloater.h"  #include "lltexteditor.h"  class LLInventoryCategoriesObserver; @@ -139,6 +140,7 @@ private:  	LLTextBox *		mInventoryTitle;  	LLUUID			mRootFolderId; +    bool            mRootFolderCreating;  	LLPanelMarketplaceListings * mPanelListings;      bool            mPanelListingsSet;  }; @@ -223,4 +225,10 @@ public:  private:  }; +class LLMultiItemProperties : public LLMultiFloater +{ +public: +	LLMultiItemProperties(const LLSD& key); +}; +  #endif // LL_LLFLOATERMARKETPLACELISTINGS_H diff --git a/indra/newview/llfloaternewfeaturenotification.cpp b/indra/newview/llfloaternewfeaturenotification.cpp new file mode 100644 index 0000000000..3a2035b9b9 --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.cpp @@ -0,0 +1,76 @@ +/**  + * @file llfloaternewfeaturenotification.cpp + * @brief LLFloaterNewFeatureNotification class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaternewfeaturenotification.h" + + +LLFloaterNewFeatureNotification::LLFloaterNewFeatureNotification(const LLSD& key) +  : LLFloater(key) +{ +} + +LLFloaterNewFeatureNotification::~LLFloaterNewFeatureNotification() +{ +} + +BOOL LLFloaterNewFeatureNotification::postBuild() +{ +    setCanDrag(FALSE); +    getChild<LLButton>("close_btn")->setCommitCallback(boost::bind(&LLFloaterNewFeatureNotification::onCloseBtn, this)); + +    const std::string title_txt = "title_txt"; +    const std::string dsc_txt = "description_txt"; +    std::string feature = "_" + getKey().asString(); +     +    getChild<LLUICtrl>(title_txt)->setValue(getString(title_txt + feature)); +    getChild<LLUICtrl>(dsc_txt)->setValue(getString(dsc_txt + feature)); + +    return TRUE; +} + +void LLFloaterNewFeatureNotification::onOpen(const LLSD& key) +{ +    centerOnScreen(); +} + +void LLFloaterNewFeatureNotification::onCloseBtn() +{ +    closeFloater(); +} + +void LLFloaterNewFeatureNotification::centerOnScreen() +{ +    LLVector2 window_size = LLUI::getInstance()->getWindowSize(); +    centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY]))); +    LLFloaterView* parent = dynamic_cast<LLFloaterView*>(getParent()); +    if (parent) +    { +        parent->bringToFront(this); +    } +} + diff --git a/indra/newview/llfloaternewfeaturenotification.h b/indra/newview/llfloaternewfeaturenotification.h new file mode 100644 index 0000000000..95501451dc --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.h @@ -0,0 +1,49 @@ +/**  + * @file llfloaternewfeaturenotification.h + * @brief LLFloaterNewFeatureNotification class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_FLOATER_NEW_FEATURE_NOTOFICATION_H +#define LL_FLOATER_NEW_FEATURE_NOTOFICATION_H + +#include "llfloater.h" + +class LLFloaterNewFeatureNotification: +    public LLFloater +{ +    friend class LLFloaterReg; +public: +    BOOL postBuild() override; +    void onOpen(const LLSD& key) override; + +private: +    LLFloaterNewFeatureNotification(const LLSD& key); +    /*virtual*/	~LLFloaterNewFeatureNotification(); + +    void centerOnScreen(); + +    void onCloseBtn();	 +}; + +#endif diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp index a682064dad..d3ab22f792 100644 --- a/indra/newview/llfloateropenobject.cpp +++ b/indra/newview/llfloateropenobject.cpp @@ -164,34 +164,12 @@ void LLFloaterOpenObject::moveToInventory(bool wear, bool replace)  	}  	inventory_func_type func = boost::bind(LLFloaterOpenObject::callbackCreateInventoryCategory,_1,object_id,wear,replace); -	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. -	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"); -		} -	} +	// D567 copy thumbnail info +	gInventory.createNewCategory( +        parent_category_id, +        LLFolderType::FT_NONE, +        name, +        func);  }  // static @@ -206,9 +184,14 @@ void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLUUID& category  	// 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*)wear_data); +	BOOL success = move_inv_category_world_to_agent(object_id, +                                                    category_id, +                                                    TRUE, +                                                    [](S32 result, void* data, const LLMoveInv*) +                                                    { +                                                        callbackMoveInventory(result, data); +                                                    }, +                                                    (void*)wear_data);  	if (!success)  	{  		delete wear_data; diff --git a/indra/newview/llfloateroutfitphotopreview.cpp b/indra/newview/llfloateroutfitphotopreview.cpp deleted file mode 100644 index ade258aef7..0000000000 --- a/indra/newview/llfloateroutfitphotopreview.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/**  - * @file llfloateroutfitphotopreview.cpp - * @brief LLFloaterOutfitPhotoPreview class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llwindow.h" - -#include "llfloateroutfitphotopreview.h" - -#include "llagent.h" -#include "llappearancemgr.h" -#include "llbutton.h" -#include "llcombobox.h" -#include "llfilepicker.h" -#include "llfloaterreg.h" -#include "llimagetga.h" -#include "llimagepng.h" -#include "llinventory.h" -#include "llinventorymodel.h" -#include "llnotificationsutil.h" -#include "llresmgr.h" -#include "lltrans.h" -#include "lltextbox.h" -#include "lltextureview.h" -#include "llui.h" -#include "llviewerinventory.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" -#include "lluictrlfactory.h" -#include "llviewerwindow.h" -#include "lllineeditor.h" - -const S32 MAX_OUTFIT_PHOTO_WIDTH = 256; -const S32 MAX_OUTFIT_PHOTO_HEIGHT = 256; - -const S32 CLIENT_RECT_VPAD = 4; - -LLFloaterOutfitPhotoPreview::LLFloaterOutfitPhotoPreview(const LLSD& key) -	: LLPreview(key), -	  mUpdateDimensions(TRUE), -	  mImage(NULL), -	  mOutfitID(LLUUID()), -	  mImageOldBoostLevel(LLGLTexture::BOOST_NONE), -	  mExceedLimits(FALSE) -{ -	updateImageID(); -} - -LLFloaterOutfitPhotoPreview::~LLFloaterOutfitPhotoPreview() -{ -	LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ; - -	if (mImage.notNull()) -	{ -		mImage->setBoostLevel(mImageOldBoostLevel); -		mImage = NULL; -	} -} - -// virtual -BOOL LLFloaterOutfitPhotoPreview::postBuild() -{ -	getChild<LLButton>("ok_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onOkBtn, this)); -	getChild<LLButton>("cancel_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onCancelBtn, this)); - -	return LLPreview::postBuild(); -} - -void LLFloaterOutfitPhotoPreview::draw() -{ -	updateDimensions(); -	 -	LLPreview::draw(); - -	if (!isMinimized()) -	{ -		LLGLSUIDefault gls_ui; -		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); -		 -		const LLRect& border = mClientRect; -		LLRect interior = mClientRect; -		interior.stretch( -PREVIEW_BORDER_WIDTH ); - -		// ...border -		gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f)); -		gl_rect_2d_checkerboard( interior ); - -		if ( mImage.notNull() ) -		{ -			// Draw the texture -			gGL.diffuseColor3f( 1.f, 1.f, 1.f ); -			gl_draw_scaled_image(interior.mLeft, -								interior.mBottom, -								interior.getWidth(), -								interior.getHeight(), -								mImage); - -			// Pump the texture priority -			F32 pixel_area = (F32)(interior.getWidth() * interior.getHeight() ); -			mImage->addTextureStats( pixel_area ); - -			S32 int_width = interior.getWidth(); -			S32 int_height = interior.getHeight(); -			mImage->setKnownDrawSize(int_width, int_height); -		} -	}  - -} - -// virtual -void LLFloaterOutfitPhotoPreview::reshape(S32 width, S32 height, BOOL called_from_parent) -{ -	LLPreview::reshape(width, height, called_from_parent); - -	LLRect dim_rect(getChildView("dimensions")->getRect()); - -	S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE; - -	S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD; - -	LLRect client_rect(horiz_pad, getRect().getHeight(), getRect().getWidth() - horiz_pad, 0); -	client_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD); -	client_rect.mBottom += PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height ; - -	S32 client_width = client_rect.getWidth(); -	S32 client_height = client_width; - -	if(client_height > client_rect.getHeight()) -	{ -		client_height = client_rect.getHeight(); -		client_width = client_height; -	} -	mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() +  (client_height / 2), client_width, client_height); - -} - - -void LLFloaterOutfitPhotoPreview::updateDimensions() -{ -	if (!mImage) -	{ -		return; -	} -	if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0) -	{ -		return; -	} - -	if (mAssetStatus != PREVIEW_ASSET_LOADED) -	{ -		mAssetStatus = PREVIEW_ASSET_LOADED; -		mUpdateDimensions = TRUE; -	} -	 -	getChild<LLUICtrl>("dimensions")->setTextArg("[WIDTH]",  llformat("%d", mImage->getFullWidth())); -	getChild<LLUICtrl>("dimensions")->setTextArg("[HEIGHT]", llformat("%d", mImage->getFullHeight())); - -	if ((mImage->getFullWidth() <= MAX_OUTFIT_PHOTO_WIDTH) && (mImage->getFullHeight() <= MAX_OUTFIT_PHOTO_HEIGHT)) -	{ -		getChild<LLButton>("ok_btn")->setEnabled(TRUE); -		mExceedLimits = FALSE; -	} -	else -	{ -		mExceedLimits = TRUE; -		LLStringUtil::format_map_t args; -		args["MAX_WIDTH"] = llformat("%d", MAX_OUTFIT_PHOTO_WIDTH); -		args["MAX_HEIGHT"] = llformat("%d", MAX_OUTFIT_PHOTO_HEIGHT); -		std::string label = getString("exceed_limits", args); -		getChild<LLUICtrl>("notification")->setValue(label); -		getChild<LLUICtrl>("notification")->setColor(LLColor4::yellow); -		getChild<LLButton>("ok_btn")->setEnabled(FALSE); -	} - -	if (mUpdateDimensions) -	{ -		mUpdateDimensions = FALSE; - -		reshape(getRect().getWidth(), getRect().getHeight()); -		gFloaterView->adjustToFitScreen(this, FALSE); -	} -} - -void LLFloaterOutfitPhotoPreview::loadAsset() -{ -	if (mImage.notNull()) -	{ -		mImage->setBoostLevel(mImageOldBoostLevel); -	} -	mImage = LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); -	mImageOldBoostLevel = mImage->getBoostLevel(); -	mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW); -	mImage->forceToSaveRawImage(0) ; -	mAssetStatus = PREVIEW_ASSET_LOADING; -	mUpdateDimensions = TRUE; -	updateDimensions(); -} - -LLPreview::EAssetStatus LLFloaterOutfitPhotoPreview::getAssetStatus() -{ -	if (mImage.notNull() && (mImage->getFullWidth() * mImage->getFullHeight() > 0)) -	{ -		mAssetStatus = PREVIEW_ASSET_LOADED; -	} -	return mAssetStatus; -} - -void LLFloaterOutfitPhotoPreview::updateImageID() -{ -	const LLViewerInventoryItem *item = static_cast<const LLViewerInventoryItem*>(getItem()); -	if(item) -	{ -		mImageID = item->getAssetUUID(); -	} -	else -	{ -		mImageID = mItemUUID; -	} - -} - -/* virtual */ -void LLFloaterOutfitPhotoPreview::setObjectID(const LLUUID& object_id) -{ -	mObjectUUID = object_id; - -	const LLUUID old_image_id = mImageID; - -	updateImageID(); -	if (mImageID != old_image_id) -	{ -		mAssetStatus = PREVIEW_ASSET_UNLOADED; -		loadAsset(); -	} -	refreshFromItem(); -} - -void LLFloaterOutfitPhotoPreview::setOutfitID(const LLUUID& outfit_id) -{ -	mOutfitID = outfit_id; -	LLViewerInventoryCategory* outfit_folder = gInventory.getCategory(mOutfitID); -	if(outfit_folder && !mExceedLimits) -	{ -		getChild<LLUICtrl>("notification")->setValue( getString("photo_confirmation")); -		getChild<LLUICtrl>("notification")->setTextArg("[OUTFIT]", outfit_folder->getName()); -		getChild<LLUICtrl>("notification")->setColor(LLColor4::white); -	} - -} - -void LLFloaterOutfitPhotoPreview::onOkBtn() -{ -	if(mOutfitID.notNull() && getItem()) -	{ -		LLAppearanceMgr::instance().removeOutfitPhoto(mOutfitID); -		LLPointer<LLInventoryCallback> cb = NULL; -		link_inventory_object(mOutfitID, LLConstPointer<LLInventoryObject>(getItem()), cb); -	} -	closeFloater(); -} - -void LLFloaterOutfitPhotoPreview::onCancelBtn() -{ -	closeFloater(); -} diff --git a/indra/newview/llfloateroutfitphotopreview.h b/indra/newview/llfloateroutfitphotopreview.h deleted file mode 100644 index a1e7b58abe..0000000000 --- a/indra/newview/llfloateroutfitphotopreview.h +++ /dev/null @@ -1,77 +0,0 @@ -/**  - * @file llfloateroutfitphotopreview.h - * @brief LLFloaterOutfitPhotoPreview class definition - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLFLOATEROUTFITPHOTOPREVIEW_H -#define LL_LLFLOATEROUTFITPHOTOPREVIEW_H - -#include "llpreview.h" -#include "llbutton.h" -#include "llframetimer.h" -#include "llviewertexture.h" - -class LLComboBox; -class LLImageRaw; - -class LLFloaterOutfitPhotoPreview : public LLPreview -{ -public: -	LLFloaterOutfitPhotoPreview(const LLSD& key); -	~LLFloaterOutfitPhotoPreview(); - -	virtual void		draw(); - -	virtual void		loadAsset(); -	virtual EAssetStatus	getAssetStatus(); -	 -	virtual void		reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - -	/*virtual*/ void setObjectID(const LLUUID& object_id); - -	void setOutfitID(const LLUUID& outfit_id); -	void onOkBtn(); -	void onCancelBtn(); - -protected: -	void				init(); -	/* virtual */ BOOL	postBuild(); -	 -private: -	void				updateImageID(); // set what image is being uploaded. -	void				updateDimensions(); -	LLUUID				mImageID; -	LLUUID				mOutfitID; -	LLPointer<LLViewerFetchedTexture>		mImage; -	S32                 mImageOldBoostLevel; - -	// This is stored off in a member variable, because the save-as -	// button and drag and drop functionality need to know. -	BOOL mUpdateDimensions; - -	BOOL mExceedLimits; - -	LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ; -}; -#endif  // LL_LLFLOATEROUTFITPHOTOPREVIEW_H diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp deleted file mode 100644 index 64ad40f419..0000000000 --- a/indra/newview/llfloaterproperties.cpp +++ /dev/null @@ -1,891 +0,0 @@ -/**  - * @file llfloaterproperties.cpp - * @brief A floater which shows an inventory item's properties. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llfloaterproperties.h" - -#include <algorithm> -#include <functional> -#include "llcachename.h" -#include "llavatarnamecache.h" -#include "lldbstrings.h" -#include "llfloaterreg.h" - -#include "llagent.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llcombobox.h" -#include "llavataractions.h" -#include "llinventorydefines.h" -#include "llinventoryobserver.h" -#include "llinventorymodel.h" -#include "lllineeditor.h" -//#include "llspinctrl.h" -#include "llradiogroup.h" -#include "llresmgr.h" -#include "roles_constants.h" -#include "llselectmgr.h" -#include "lltextbox.h" -#include "lltrans.h" -#include "lluiconstants.h" -#include "llviewerinventory.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llgroupactions.h" - -#include "lluictrlfactory.h" - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLPropertiesObserver -// -// helper class to watch the inventory.  -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// Ugh. This can't be a singleton because it needs to remove itself -//  from the inventory observer list when destroyed, which could -//  happen after gInventory has already been destroyed if a singleton. -// Instead, do our own ref counting and create / destroy it as needed -class LLPropertiesObserver : public LLInventoryObserver -{ -public: -	LLPropertiesObserver(LLFloaterProperties* floater) -		: mFloater(floater) -	{ -		gInventory.addObserver(this); -	} -	virtual ~LLPropertiesObserver() -	{ -		gInventory.removeObserver(this); -	} -	virtual void changed(U32 mask); -private: -	LLFloaterProperties* mFloater; // Not a handle because LLFloaterProperties is managing LLPropertiesObserver -}; - -void LLPropertiesObserver::changed(U32 mask) -{ -	// if there's a change we're interested in. -	if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) -	{ -		mFloater->dirty(); -	} -} - - - -///---------------------------------------------------------------------------- -/// Class LLFloaterProperties -///---------------------------------------------------------------------------- - -// Default constructor -LLFloaterProperties::LLFloaterProperties(const LLUUID& item_id) -  : LLFloater(mItemID), -	mItemID(item_id), -	mDirty(TRUE) -{ -	mPropertiesObserver = new LLPropertiesObserver(this); -} - -// Destroys the object -LLFloaterProperties::~LLFloaterProperties() -{ -	delete mPropertiesObserver; -	mPropertiesObserver = NULL; -} - -// virtual -BOOL LLFloaterProperties::postBuild() -{ -	// build the UI -	// item name & description -	getChild<LLLineEditor>("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); -	getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitName,this)); -	getChild<LLLineEditor>("LabelItemDesc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); -	getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLFloaterProperties:: onCommitDescription, this)); -	// Creator information -	getChild<LLUICtrl>("BtnCreator")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickCreator,this)); -	// owner information -	getChild<LLUICtrl>("BtnOwner")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickOwner,this)); -	// acquired date -	// owner permissions -	// Permissions debug text -	// group permissions -	getChild<LLUICtrl>("CheckShareWithGroup")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); -	// everyone permissions -	getChild<LLUICtrl>("CheckEveryoneCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); -	// next owner permissions -	getChild<LLUICtrl>("CheckNextOwnerModify")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); -	getChild<LLUICtrl>("CheckNextOwnerCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); -	getChild<LLUICtrl>("CheckNextOwnerTransfer")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); -	// Mark for sale or not, and sale info -	getChild<LLUICtrl>("CheckPurchase")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this)); -	getChild<LLUICtrl>("ComboBoxSaleType")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleType, this)); -	// "Price" label for edit -	getChild<LLUICtrl>("Edit Cost")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this)); -	// The UI has been built, now fill in all the values -	refresh(); - -	return TRUE; -} - -// virtual -void LLFloaterProperties::onOpen(const LLSD& key) -{ -	refresh(); -} - -void LLFloaterProperties::refresh() -{ -	LLInventoryItem* item = findItem(); -	if(item) -	{ -		refreshFromItem(item); -	} -	else -	{ -		//RN: it is possible that the container object is in the middle of an inventory refresh -		// causing findItem() to fail, so just temporarily disable everything -		 -		mDirty = TRUE; - -		const char* enableNames[]={ -			"LabelItemName", -			"LabelItemDesc", -			"LabelCreatorName", -			"BtnCreator", -			"LabelOwnerName", -			"BtnOwner", -			"CheckOwnerModify", -			"CheckOwnerCopy", -			"CheckOwnerTransfer", -			"CheckShareWithGroup", -			"CheckEveryoneCopy", -			"CheckNextOwnerModify", -			"CheckNextOwnerCopy", -			"CheckNextOwnerTransfer", -			"CheckPurchase", -			"ComboBoxSaleType", -			"Edit Cost" -		}; -		for(size_t t=0; t<LL_ARRAY_SIZE(enableNames); ++t) -		{ -			getChildView(enableNames[t])->setEnabled(false); -		} -		const char* hideNames[]={ -			"BaseMaskDebug", -			"OwnerMaskDebug", -			"GroupMaskDebug", -			"EveryoneMaskDebug", -			"NextMaskDebug" -		}; -		for(size_t t=0; t<LL_ARRAY_SIZE(hideNames); ++t) -		{ -			getChildView(hideNames[t])->setVisible(false); -		} -	} -} - -void LLFloaterProperties::draw() -{ -	if (mDirty) -	{ -		// RN: clear dirty first because refresh can set dirty to TRUE -		mDirty = FALSE; -		refresh(); -	} - -	LLFloater::draw(); -} - -void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) -{ -	//////////////////////// -	// PERMISSIONS LOOKUP // -	//////////////////////// - -	// do not enable the UI for incomplete items. -	LLViewerInventoryItem* i = (LLViewerInventoryItem*)item; -	BOOL is_complete = i->isFinished(); -	const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(i->getInventoryType()); -	const BOOL is_calling_card = (i->getInventoryType() == LLInventoryType::IT_CALLINGCARD); -	const LLPermissions& perm = item->getPermissions(); -	const BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm,  -															GP_OBJECT_MANIPULATE); -	const BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm,  -													  GP_OBJECT_SET_SALE) && -		!cannot_restrict_permissions; -	const BOOL is_link = i->getIsLinkType(); - -	// You need permission to modify the object to modify an inventory -	// item in it. -	LLViewerObject* object = NULL; -	if(!mObjectID.isNull()) object = gObjectList.findObject(mObjectID); -	BOOL is_obj_modify = TRUE; -	if(object) -	{ -		is_obj_modify = object->permOwnerModify(); -	} - -	////////////////////// -	// ITEM NAME & DESC // -	////////////////////// -	BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm, -											   GP_OBJECT_MANIPULATE) -		&& is_obj_modify && is_complete; - -	getChildView("LabelItemNameTitle")->setEnabled(TRUE); -	getChildView("LabelItemName")->setEnabled(is_modifiable && !is_calling_card); // for now, don't allow rename of calling cards -	getChild<LLUICtrl>("LabelItemName")->setValue(item->getName()); -	getChildView("LabelItemDescTitle")->setEnabled(TRUE); -	getChildView("LabelItemDesc")->setEnabled(is_modifiable); -	getChildView("IconLocked")->setVisible(!is_modifiable); -	getChild<LLUICtrl>("LabelItemDesc")->setValue(item->getDescription()); - -	////////////////// -	// CREATOR NAME // -	////////////////// -	if(!gCacheName) return; -	if(!gAgent.getRegion()) return; - -	if (item->getCreatorUUID().notNull()) -	{ -		LLAvatarName av_name; -		LLAvatarNameCache::get(item->getCreatorUUID(), &av_name); -		getChildView("BtnCreator")->setEnabled(TRUE); -		getChildView("LabelCreatorTitle")->setEnabled(TRUE); -		getChildView("LabelCreatorName")->setEnabled(TRUE); -		getChild<LLUICtrl>("LabelCreatorName")->setValue(av_name.getUserName()); -	} -	else -	{ -		getChildView("BtnCreator")->setEnabled(FALSE); -		getChildView("LabelCreatorTitle")->setEnabled(FALSE); -		getChildView("LabelCreatorName")->setEnabled(FALSE); -		getChild<LLUICtrl>("LabelCreatorName")->setValue(getString("unknown")); -	} - -	//////////////// -	// OWNER NAME // -	//////////////// -	if(perm.isOwned()) -	{ -		std::string name; -		if (perm.isGroupOwned()) -		{ -			gCacheName->getGroupName(perm.getGroup(), name); -		} -		else -		{ -			LLAvatarName av_name; -			LLAvatarNameCache::get(perm.getOwner(), &av_name); -			name = av_name.getUserName(); -		} -		getChildView("BtnOwner")->setEnabled(TRUE); -		getChildView("LabelOwnerTitle")->setEnabled(TRUE); -		getChildView("LabelOwnerName")->setEnabled(TRUE); -		getChild<LLUICtrl>("LabelOwnerName")->setValue(name); -	} -	else -	{ -		getChildView("BtnOwner")->setEnabled(FALSE); -		getChildView("LabelOwnerTitle")->setEnabled(FALSE); -		getChildView("LabelOwnerName")->setEnabled(FALSE); -		getChild<LLUICtrl>("LabelOwnerName")->setValue(getString("public")); -	} -	 -	////////////////// -	// ACQUIRE DATE // -	////////////////// -	 -	time_t time_utc = item->getCreationDate(); -	if (0 == time_utc) -	{ -		getChild<LLUICtrl>("LabelAcquiredDate")->setValue(getString("unknown")); -	} -	else -	{ -		std::string timeStr = getString("acquiredDate"); -		LLSD substitution; -		substitution["datetime"] = (S32) time_utc; -		LLStringUtil::format (timeStr, substitution); -		getChild<LLUICtrl>("LabelAcquiredDate")->setValue(timeStr); -	} - -	/////////////////////// -	// OWNER PERMISSIONS // -	/////////////////////// -	if(can_agent_manipulate) -	{ -		getChild<LLUICtrl>("OwnerLabel")->setValue(getString("you_can")); -	} -	else -	{ -		getChild<LLUICtrl>("OwnerLabel")->setValue(getString("owner_can")); -	} - -	U32 base_mask		= perm.getMaskBase(); -	U32 owner_mask		= perm.getMaskOwner(); -	U32 group_mask		= perm.getMaskGroup(); -	U32 everyone_mask	= perm.getMaskEveryone(); -	U32 next_owner_mask	= perm.getMaskNextOwner(); - -	getChildView("OwnerLabel")->setEnabled(TRUE); -	getChildView("CheckOwnerModify")->setEnabled(FALSE); -	getChild<LLUICtrl>("CheckOwnerModify")->setValue(LLSD((BOOL)(owner_mask & PERM_MODIFY))); -	getChildView("CheckOwnerCopy")->setEnabled(FALSE); -	getChild<LLUICtrl>("CheckOwnerCopy")->setValue(LLSD((BOOL)(owner_mask & PERM_COPY))); -	getChildView("CheckOwnerTransfer")->setEnabled(FALSE); -	getChild<LLUICtrl>("CheckOwnerTransfer")->setValue(LLSD((BOOL)(owner_mask & PERM_TRANSFER))); - -	/////////////////////// -	// DEBUG PERMISSIONS // -	/////////////////////// - -	if( gSavedSettings.getBOOL("DebugPermissions") ) -	{ -		BOOL slam_perm 			= FALSE; -		BOOL overwrite_group	= FALSE; -		BOOL overwrite_everyone	= FALSE; - -		if (item->getType() == LLAssetType::AT_OBJECT) -		{ -			U32 flags = item->getFlags(); -			slam_perm 			= flags & LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM; -			overwrite_everyone	= flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE; -			overwrite_group		= flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP; -		} -		 -		std::string perm_string; - -		perm_string = "B: "; -		perm_string += mask_to_string(base_mask); -		getChild<LLUICtrl>("BaseMaskDebug")->setValue(perm_string); -		getChildView("BaseMaskDebug")->setVisible(TRUE); -		 -		perm_string = "O: "; -		perm_string += mask_to_string(owner_mask); -		getChild<LLUICtrl>("OwnerMaskDebug")->setValue(perm_string); -		getChildView("OwnerMaskDebug")->setVisible(TRUE); -		 -		perm_string = "G"; -		perm_string += overwrite_group ? "*: " : ": "; -		perm_string += mask_to_string(group_mask); -		getChild<LLUICtrl>("GroupMaskDebug")->setValue(perm_string); -		getChildView("GroupMaskDebug")->setVisible(TRUE); -		 -		perm_string = "E"; -		perm_string += overwrite_everyone ? "*: " : ": "; -		perm_string += mask_to_string(everyone_mask); -		getChild<LLUICtrl>("EveryoneMaskDebug")->setValue(perm_string); -		getChildView("EveryoneMaskDebug")->setVisible(TRUE); -		 -		perm_string = "N"; -		perm_string += slam_perm ? "*: " : ": "; -		perm_string += mask_to_string(next_owner_mask); -		getChild<LLUICtrl>("NextMaskDebug")->setValue(perm_string); -		getChildView("NextMaskDebug")->setVisible(TRUE); -	} -	else -	{ -		getChildView("BaseMaskDebug")->setVisible(FALSE); -		getChildView("OwnerMaskDebug")->setVisible(FALSE); -		getChildView("GroupMaskDebug")->setVisible(FALSE); -		getChildView("EveryoneMaskDebug")->setVisible(FALSE); -		getChildView("NextMaskDebug")->setVisible(FALSE); -	} - -	///////////// -	// SHARING // -	///////////// - -	// Check for ability to change values. -	if (is_link || cannot_restrict_permissions) -	{ -		getChildView("CheckShareWithGroup")->setEnabled(FALSE); -		getChildView("CheckEveryoneCopy")->setEnabled(FALSE); -	} -	else if (is_obj_modify && can_agent_manipulate) -	{ -		getChildView("CheckShareWithGroup")->setEnabled(TRUE); -		getChildView("CheckEveryoneCopy")->setEnabled((owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER)); -	} -	else -	{ -		getChildView("CheckShareWithGroup")->setEnabled(FALSE); -		getChildView("CheckEveryoneCopy")->setEnabled(FALSE); -	} - -	// Set values. -	BOOL is_group_copy = (group_mask & PERM_COPY) ? TRUE : FALSE; -	BOOL is_group_modify = (group_mask & PERM_MODIFY) ? TRUE : FALSE; -	BOOL is_group_move = (group_mask & PERM_MOVE) ? TRUE : FALSE; - -	if (is_group_copy && is_group_modify && is_group_move) -	{ -		getChild<LLUICtrl>("CheckShareWithGroup")->setValue(LLSD((BOOL)TRUE)); - -		LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup"); -		if(ctl) -		{ -			ctl->setTentative(FALSE); -		} -	} -	else if (!is_group_copy && !is_group_modify && !is_group_move) -	{ -		getChild<LLUICtrl>("CheckShareWithGroup")->setValue(LLSD((BOOL)FALSE)); -		LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup"); -		if(ctl) -		{ -			ctl->setTentative(FALSE); -		} -	} -	else -	{ -		LLCheckBoxCtrl* ctl = getChild<LLCheckBoxCtrl>("CheckShareWithGroup"); -		if(ctl) -		{ -			ctl->setTentative(TRUE); -			ctl->set(TRUE); -		} -	} -	 -	getChild<LLUICtrl>("CheckEveryoneCopy")->setValue(LLSD((BOOL)(everyone_mask & PERM_COPY))); - -	/////////////// -	// SALE INFO // -	/////////////// - -	const LLSaleInfo& sale_info = item->getSaleInfo(); -	BOOL is_for_sale = sale_info.isForSale(); -	LLComboBox* combo_sale_type = getChild<LLComboBox>("ComboBoxSaleType"); -	LLUICtrl* edit_cost = getChild<LLUICtrl>("Edit Cost"); - -	// Check for ability to change values. -	if (is_obj_modify && can_agent_sell  -		&& gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE)) -	{ -		getChildView("CheckPurchase")->setEnabled(is_complete); - -		getChildView("NextOwnerLabel")->setEnabled(TRUE); -		getChildView("CheckNextOwnerModify")->setEnabled((base_mask & PERM_MODIFY) && !cannot_restrict_permissions); -		getChildView("CheckNextOwnerCopy")->setEnabled((base_mask & PERM_COPY) && !cannot_restrict_permissions); -		getChildView("CheckNextOwnerTransfer")->setEnabled((next_owner_mask & PERM_COPY) && !cannot_restrict_permissions); - -		combo_sale_type->setEnabled(is_complete && is_for_sale); -		edit_cost->setEnabled(is_complete && is_for_sale); -	} -	else -	{ -		getChildView("CheckPurchase")->setEnabled(FALSE); - -		getChildView("NextOwnerLabel")->setEnabled(FALSE); -		getChildView("CheckNextOwnerModify")->setEnabled(FALSE); -		getChildView("CheckNextOwnerCopy")->setEnabled(FALSE); -		getChildView("CheckNextOwnerTransfer")->setEnabled(FALSE); - -		combo_sale_type->setEnabled(FALSE); -		edit_cost->setEnabled(FALSE); -	} - -	// Set values. -	getChild<LLUICtrl>("CheckPurchase")->setValue(is_for_sale); -	getChild<LLUICtrl>("CheckNextOwnerModify")->setValue(LLSD(BOOL(next_owner_mask & PERM_MODIFY))); -	getChild<LLUICtrl>("CheckNextOwnerCopy")->setValue(LLSD(BOOL(next_owner_mask & PERM_COPY))); -	getChild<LLUICtrl>("CheckNextOwnerTransfer")->setValue(LLSD(BOOL(next_owner_mask & PERM_TRANSFER))); - -	if (is_for_sale) -	{ -		S32 numerical_price; -		numerical_price = sale_info.getSalePrice(); -		edit_cost->setValue(llformat("%d",numerical_price)); -		combo_sale_type->setValue(sale_info.getSaleType()); -	} -	else -	{ -		edit_cost->setValue(llformat("%d",0)); -		combo_sale_type->setValue(LLSaleInfo::FS_COPY); -	} -} - -void LLFloaterProperties::onClickCreator() -{ -	LLInventoryItem* item = findItem(); -	if(!item) return; -	if(!item->getCreatorUUID().isNull()) -	{ -		LLAvatarActions::showProfile(item->getCreatorUUID()); -	} -} - -// static -void LLFloaterProperties::onClickOwner() -{ -	LLInventoryItem* item = findItem(); -	if(!item) return; -	if(item->getPermissions().isGroupOwned()) -	{ -		LLGroupActions::show(item->getPermissions().getGroup()); -	} -	else -	{ -		LLAvatarActions::showProfile(item->getPermissions().getOwner()); -	} -} - -// static -void LLFloaterProperties::onCommitName() -{ -	//LL_INFOS() << "LLFloaterProperties::onCommitName()" << LL_ENDL; -	LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); -	if(!item) -	{ -		return; -	} -	LLLineEditor* labelItemName = getChild<LLLineEditor>("LabelItemName"); - -	if(labelItemName&& -	   (item->getName() != labelItemName->getText()) &&  -	   (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) ) -	{ -		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); -		new_item->rename(labelItemName->getText()); -		if(mObjectID.isNull()) -		{ -			new_item->updateServer(FALSE); -			gInventory.updateItem(new_item); -			gInventory.notifyObservers(); -		} -		else -		{ -			LLViewerObject* object = gObjectList.findObject(mObjectID); -			if(object) -			{ -				object->updateInventory( -					new_item, -					TASK_INVENTORY_ITEM_KEY, -					false); -			} -		} -	} -} - -void LLFloaterProperties::onCommitDescription() -{ -	//LL_INFOS() << "LLFloaterProperties::onCommitDescription()" << LL_ENDL; -	LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); -	if(!item) return; - -	LLLineEditor* labelItemDesc = getChild<LLLineEditor>("LabelItemDesc"); -	if(!labelItemDesc) -	{ -		return; -	} -	if((item->getDescription() != labelItemDesc->getText()) &&  -	   (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))) -	{ -		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - -		new_item->setDescription(labelItemDesc->getText()); -		if(mObjectID.isNull()) -		{ -			new_item->updateServer(FALSE); -			gInventory.updateItem(new_item); -			gInventory.notifyObservers(); -		} -		else -		{ -			LLViewerObject* object = gObjectList.findObject(mObjectID); -			if(object) -			{ -				object->updateInventory( -					new_item, -					TASK_INVENTORY_ITEM_KEY, -					false); -			} -		} -	} -} - -// static -void LLFloaterProperties::onCommitPermissions() -{ -	//LL_INFOS() << "LLFloaterProperties::onCommitPermissions()" << LL_ENDL; -	LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); -	if(!item) return; -	LLPermissions perm(item->getPermissions()); - - -	LLCheckBoxCtrl* CheckShareWithGroup = getChild<LLCheckBoxCtrl>("CheckShareWithGroup"); - -	if(CheckShareWithGroup) -	{ -		perm.setGroupBits(gAgent.getID(), gAgent.getGroupID(), -						CheckShareWithGroup->get(), -						PERM_MODIFY | PERM_MOVE | PERM_COPY); -	} -	LLCheckBoxCtrl* CheckEveryoneCopy = getChild<LLCheckBoxCtrl>("CheckEveryoneCopy"); -	if(CheckEveryoneCopy) -	{ -		perm.setEveryoneBits(gAgent.getID(), gAgent.getGroupID(), -						 CheckEveryoneCopy->get(), PERM_COPY); -	} - -	LLCheckBoxCtrl* CheckNextOwnerModify = getChild<LLCheckBoxCtrl>("CheckNextOwnerModify"); -	if(CheckNextOwnerModify) -	{ -		perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(), -							CheckNextOwnerModify->get(), PERM_MODIFY); -	} -	LLCheckBoxCtrl* CheckNextOwnerCopy = getChild<LLCheckBoxCtrl>("CheckNextOwnerCopy"); -	if(CheckNextOwnerCopy) -	{ -		perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(), -							CheckNextOwnerCopy->get(), PERM_COPY); -	} -	LLCheckBoxCtrl* CheckNextOwnerTransfer = getChild<LLCheckBoxCtrl>("CheckNextOwnerTransfer"); -	if(CheckNextOwnerTransfer) -	{ -		perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(), -							CheckNextOwnerTransfer->get(), PERM_TRANSFER); -	} -	if(perm != item->getPermissions() -		&& item->isFinished()) -	{ -		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); -		new_item->setPermissions(perm); -		U32 flags = new_item->getFlags(); -		// If next owner permissions have changed (and this is an object) -		// then set the slam permissions flag so that they are applied on rez. -		if((perm.getMaskNextOwner()!=item->getPermissions().getMaskNextOwner()) -		   && (item->getType() == LLAssetType::AT_OBJECT)) -		{ -			flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM; -		} -		// If everyone permissions have changed (and this is an object) -		// then set the overwrite everyone permissions flag so they -		// are applied on rez. -		if ((perm.getMaskEveryone()!=item->getPermissions().getMaskEveryone()) -			&& (item->getType() == LLAssetType::AT_OBJECT)) -		{ -			flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE; -		} -		// If group permissions have changed (and this is an object) -		// then set the overwrite group permissions flag so they -		// are applied on rez. -		if ((perm.getMaskGroup()!=item->getPermissions().getMaskGroup()) -			&& (item->getType() == LLAssetType::AT_OBJECT)) -		{ -			flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP; -		} -		new_item->setFlags(flags); -		if(mObjectID.isNull()) -		{ -			new_item->updateServer(FALSE); -			gInventory.updateItem(new_item); -			gInventory.notifyObservers(); -		} -		else -		{ -			LLViewerObject* object = gObjectList.findObject(mObjectID); -			if(object) -			{ -				object->updateInventory( -					new_item, -					TASK_INVENTORY_ITEM_KEY, -					false); -			} -		} -	} -	else -	{ -		// need to make sure we don't just follow the click -		refresh(); -	} -} - -// static -void LLFloaterProperties::onCommitSaleInfo() -{ -	//LL_INFOS() << "LLFloaterProperties::onCommitSaleInfo()" << LL_ENDL; -	updateSaleInfo(); -} - -// static -void LLFloaterProperties::onCommitSaleType() -{ -	//LL_INFOS() << "LLFloaterProperties::onCommitSaleType()" << LL_ENDL; -	updateSaleInfo(); -} - -void LLFloaterProperties::updateSaleInfo() -{ -	LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); -	if(!item) return; -	LLSaleInfo sale_info(item->getSaleInfo()); -	if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE)) -	{ -		getChild<LLUICtrl>("CheckPurchase")->setValue(LLSD((BOOL)FALSE)); -	} - -	if((BOOL)getChild<LLUICtrl>("CheckPurchase")->getValue()) -	{ -		// turn on sale info -		LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY; - -		LLComboBox* combo_sale_type = getChild<LLComboBox>("ComboBoxSaleType"); -		if (combo_sale_type) -		{ -			sale_type = static_cast<LLSaleInfo::EForSale>(combo_sale_type->getValue().asInteger()); -		} - -		if (sale_type == LLSaleInfo::FS_COPY  -			&& !gAgent.allowOperation(PERM_COPY, item->getPermissions(),  -									  GP_OBJECT_SET_SALE)) -		{ -			sale_type = LLSaleInfo::FS_ORIGINAL; -		} - -	      -		 -		S32 price = -1; -		price =  getChild<LLUICtrl>("Edit Cost")->getValue().asInteger();; - -		// Invalid data - turn off the sale -		if (price < 0) -		{ -			sale_type = LLSaleInfo::FS_NOT; -			price = 0; -		} - -		sale_info.setSaleType(sale_type); -		sale_info.setSalePrice(price); -	} -	else -	{ -		sale_info.setSaleType(LLSaleInfo::FS_NOT); -	} -	if(sale_info != item->getSaleInfo() -		&& item->isFinished()) -	{ -		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - -		// Force an update on the sale price at rez -		if (item->getType() == LLAssetType::AT_OBJECT) -		{ -			U32 flags = new_item->getFlags(); -			flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_SALE; -			new_item->setFlags(flags); -		} - -		new_item->setSaleInfo(sale_info); -		if(mObjectID.isNull()) -		{ -			// This is in the agent's inventory. -			new_item->updateServer(FALSE); -			gInventory.updateItem(new_item); -			gInventory.notifyObservers(); -		} -		else -		{ -			// This is in an object's contents. -			LLViewerObject* object = gObjectList.findObject(mObjectID); -			if(object) -			{ -				object->updateInventory( -					new_item, -					TASK_INVENTORY_ITEM_KEY, -					false); -			} -		} -	} -	else -	{ -		// need to make sure we don't just follow the click -		refresh(); -	} -} - -LLInventoryItem* LLFloaterProperties::findItem() const -{ -	LLInventoryItem* item = NULL; -	if(mObjectID.isNull()) -	{ -		// it is in agent inventory -		item = gInventory.getItem(mItemID); -	} -	else -	{ -		LLViewerObject* object = gObjectList.findObject(mObjectID); -		if(object) -		{ -			item = (LLInventoryItem*)object->getInventoryObject(mItemID); -		} -	} -	return item; -} - -//static -void LLFloaterProperties::dirtyAll() -{ -	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("properties"); -	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); -		 iter != inst_list.end(); ++iter) -	{ -		LLFloaterProperties* floater = dynamic_cast<LLFloaterProperties*>(*iter); -		llassert(floater); // else cast failed - wrong type D: -		if (floater) -		{ -			floater->dirty(); -		} -	} -} - -///---------------------------------------------------------------------------- -/// LLMultiProperties -///---------------------------------------------------------------------------- - -LLMultiProperties::LLMultiProperties() -	: LLMultiFloater(LLSD()) -{ -	// start with a small rect in the top-left corner ; will get resized -	LLRect rect; -	rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 20, 20); -	setRect(rect); -	LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup("properties"); -	if (last_floater) -	{ -		stackWith(*last_floater); -	} -	setTitle(LLTrans::getString("MultiPropertiesTitle")); -	buildTabContainer(); -} - -///---------------------------------------------------------------------------- -/// Local function definitions -///---------------------------------------------------------------------------- diff --git a/indra/newview/llfloaterproperties.h b/indra/newview/llfloaterproperties.h deleted file mode 100644 index aa3fcec337..0000000000 --- a/indra/newview/llfloaterproperties.h +++ /dev/null @@ -1,98 +0,0 @@ -/**  - * @file llfloaterproperties.h - * @brief A floater which shows an inventory item's properties. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLFLOATERPROPERTIES_H -#define LL_LLFLOATERPROPERTIES_H - -#include <map> -#include "llmultifloater.h" -#include "lliconctrl.h" - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFloaterProperties -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLButton; -class LLCheckBoxCtrl; -class LLInventoryItem; -class LLLineEditor; -class LLRadioGroup; -class LLTextBox; - -class LLPropertiesObserver; - -class LLFloaterProperties : public LLFloater -{ -public: -	LLFloaterProperties(const LLUUID& item_id); -	/*virtual*/ ~LLFloaterProperties(); -	 -	/*virtual*/ BOOL postBuild(); -	/*virtual*/ void onOpen(const LLSD& key); -	void setObjectID(const LLUUID& object_id) { mObjectID = object_id; } - -	void dirty() { mDirty = TRUE; } -	void refresh(); -	 -	static void dirtyAll(); -	 -protected: -	// ui callbacks -	void onClickCreator(); -	void onClickOwner(); -	void onCommitName(); -	void onCommitDescription(); -	void onCommitPermissions(); -	void onCommitSaleInfo(); -	void onCommitSaleType(); -	void updateSaleInfo(); - -	LLInventoryItem* findItem() const; - -	void refreshFromItem(LLInventoryItem* item); -	virtual void draw(); - -protected: -	// The item id of the inventory item in question. -	LLUUID mItemID; - -	// mObjectID will have a value if it is associated with a task in -	// the world, and will be == LLUUID::null if it's in the agent -	// inventory. -	LLUUID mObjectID; - -	BOOL mDirty; - -	LLPropertiesObserver* mPropertiesObserver; -}; - -class LLMultiProperties : public LLMultiFloater -{ -public: -	LLMultiProperties(); -}; - -#endif // LL_LLFLOATERPROPERTIES_H diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp index dd7ce40e97..a875d33679 100644 --- a/indra/newview/llfloatersidepanelcontainer.cpp +++ b/indra/newview/llfloatersidepanelcontainer.cpp @@ -90,6 +90,29 @@ void LLFloaterSidePanelContainer::closeFloater(bool app_quitting)  	}  } +LLFloater* LLFloaterSidePanelContainer::getTopmostInventoryFloater() +{ +    LLFloater* topmost_floater = NULL; +    S32 z_min = S32_MAX; +     +    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(); ++iter) +    { +        LLFloater* inventory_floater = (*iter); + +        if (inventory_floater && inventory_floater->getVisible()) +        { +            S32 z_order = gFloaterView->getZOrder(inventory_floater); +            if (z_order < z_min) +            { +                z_min = z_order; +                topmost_floater = inventory_floater; +            } +        } +    } +    return topmost_floater; +} +  LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_name, const LLSD& params)  {  	LLView* view = findChildView(panel_name, true); diff --git a/indra/newview/llfloatersidepanelcontainer.h b/indra/newview/llfloatersidepanelcontainer.h index 20baf28184..5e7e755d1f 100644 --- a/indra/newview/llfloatersidepanelcontainer.h +++ b/indra/newview/llfloatersidepanelcontainer.h @@ -57,6 +57,8 @@ public:  	LLPanel* openChildPanel(const std::string& panel_name, const LLSD& params); +    static LLFloater* getTopmostInventoryFloater(); +  	static void showPanel(const std::string& floater_name, const LLSD& key);  	static void showPanel(const std::string& floater_name, const std::string& panel_name, const LLSD& key); diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.cpp b/indra/newview/llfloatersimpleoutfitsnapshot.cpp deleted file mode 100644 index bab2efbbd5..0000000000 --- a/indra/newview/llfloatersimpleoutfitsnapshot.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/**  -* @file llfloatersimpleoutfitsnapshot.cpp -* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery -* -* $LicenseInfo:firstyear=2022&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2022, Linden Research, Inc. -*  -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -*  -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU -* Lesser General Public License for more details. -*  -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA -*  -* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA -* $/LicenseInfo$ -*/ - -#include "llviewerprecompiledheaders.h" - -#include "llfloatersimpleoutfitsnapshot.h" - -#include "llfloaterreg.h" -#include "llimagefiltersmanager.h" -#include "llstatusbar.h" // can_afford_transaction() -#include "llnotificationsutil.h" -#include "llagentbenefits.h" -#include "llviewercontrol.h" - -LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView = NULL; - -const S32 OUTFIT_SNAPSHOT_WIDTH = 256; -const S32 OUTFIT_SNAPSHOT_HEIGHT = 256; - -static LLDefaultChildRegistry::Register<LLSimpleOutfitSnapshotFloaterView> r("simple_snapshot_outfit_floater_view"); - -///---------------------------------------------------------------------------- -/// Class LLFloaterSimpleOutfitSnapshot::Impl -///---------------------------------------------------------------------------- - -LLSnapshotModel::ESnapshotFormat LLFloaterSimpleOutfitSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater) -{ -    return LLSnapshotModel::SNAPSHOT_FORMAT_PNG; -} - -LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleOutfitSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater) -{ -    return LLSnapshotModel::SNAPSHOT_TYPE_COLOR; -} - -void LLFloaterSimpleOutfitSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater) -{ -    LLSnapshotLivePreview* previewp = getPreviewView(); -    updateResolution(floater); -    if (previewp) -    { -        previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE); -        previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG); -        previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR); -    } -} - -std::string LLFloaterSimpleOutfitSnapshot::Impl::getSnapshotPanelPrefix() -{ -    return "panel_outfit_snapshot_"; -} - -void LLFloaterSimpleOutfitSnapshot::Impl::updateResolution(void* data) -{ -    LLFloaterSimpleOutfitSnapshot *view = (LLFloaterSimpleOutfitSnapshot *)data; - -    if (!view) -    { -        llassert(view); -        return; -    } - -    S32 width = OUTFIT_SNAPSHOT_WIDTH; -    S32 height = OUTFIT_SNAPSHOT_HEIGHT; - -    LLSnapshotLivePreview* previewp = getPreviewView(); -    if (previewp) -    { -        S32 original_width = 0, original_height = 0; -        previewp->getSize(original_width, original_height); - -        if (gSavedSettings.getBOOL("RenderHUDInSnapshot")) -        { //clamp snapshot resolution to window size when showing UI HUD in snapshot -            width = llmin(width, gViewerWindow->getWindowWidthRaw()); -            height = llmin(height, gViewerWindow->getWindowHeightRaw()); -        } - -        llassert(width > 0 && height > 0); - -        previewp->setSize(width, height); - -        if (original_width != width || original_height != height) -        { -            // hide old preview as the aspect ratio could be wrong -            checkAutoSnapshot(previewp, FALSE); -            previewp->updateSnapshot(TRUE); -        } -    } -} - -void LLFloaterSimpleOutfitSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg) -{ -    switch (status) -    { -    case STATUS_READY: -        mFloater->setCtrlsEnabled(true); -        break; -    case STATUS_WORKING: -        mFloater->setCtrlsEnabled(false); -        break; -    case STATUS_FINISHED: -        mFloater->setCtrlsEnabled(true); -        break; -    } - -    mStatus = status; -} - -///----------------------------------------------------------------re------------ -/// Class LLFloaterSimpleOutfitSnapshot -///---------------------------------------------------------------------------- - -LLFloaterSimpleOutfitSnapshot::LLFloaterSimpleOutfitSnapshot(const LLSD& key) -    : LLFloaterSnapshotBase(key), -    mOutfitGallery(NULL) -{ -    impl = new Impl(this); -} - -LLFloaterSimpleOutfitSnapshot::~LLFloaterSimpleOutfitSnapshot() -{ -} - -BOOL LLFloaterSimpleOutfitSnapshot::postBuild() -{ -    getChild<LLUICtrl>("save_btn")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost())); - -    childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this); -    childSetAction("save_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onSend, this)); -    childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onCancel, this)); - -    mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); - -    // create preview window -    LLRect full_screen_rect = getRootView()->getRect(); -    LLSnapshotLivePreview::Params p; -    p.rect(full_screen_rect); -    LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); -    LLView* parent_view = gSnapshotFloaterView->getParent(); - -    parent_view->removeChild(gSnapshotFloaterView); -    // make sure preview is below snapshot floater -    parent_view->addChild(previewp); -    parent_view->addChild(gSnapshotFloaterView); - -    //move snapshot floater to special purpose snapshotfloaterview -    gFloaterView->removeChild(this); -    gSnapshotFloaterView->addChild(this); - -    impl->mPreviewHandle = previewp->getHandle(); -    previewp->setContainer(this); -    impl->updateControls(this); -    impl->setAdvanced(true); -    impl->setSkipReshaping(true); - -    previewp->mKeepAspectRatio = FALSE; -    previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect()); -    previewp->setAllowRenderUI(false); - -    return TRUE; -} -const S32 PREVIEW_OFFSET_X = 12; -const S32 PREVIEW_OFFSET_Y = 70; - -void LLFloaterSimpleOutfitSnapshot::draw() -{ -    LLSnapshotLivePreview* previewp = getPreviewView(); - -    if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock())) -    { -        // don't render snapshot window in snapshot, even if "show ui" is turned on -        return; -    } - -    LLFloater::draw(); - -    if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible()) -    {		 -        if(previewp->getThumbnailImage()) -        { -            bool working = impl->getStatus() == ImplBase::STATUS_WORKING; -            const LLRect& thumbnail_rect = getThumbnailPlaceholderRect(); -            const S32 thumbnail_w = previewp->getThumbnailWidth(); -            const S32 thumbnail_h = previewp->getThumbnailHeight(); - -            S32 offset_x = PREVIEW_OFFSET_X; -            S32 offset_y = PREVIEW_OFFSET_Y; - -            gGL.matrixMode(LLRender::MM_MODELVIEW); -            // Apply floater transparency to the texture unless the floater is focused. -            F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); -            LLColor4 color = working ? LLColor4::grey4 : LLColor4::white; -            gl_draw_scaled_image(offset_x, offset_y,  -                thumbnail_w, thumbnail_h, -                previewp->getThumbnailImage(), color % alpha); -#if LL_DARWIN -            std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "OutfitSnapshotMacMask" : "OutfitSnapshotMacMask2"; -#else -            std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "FloaterFocusBackgroundColor" : "DkGray"; -#endif - -            previewp->drawPreviewRect(offset_x, offset_y, LLUIColorTable::instance().getColor(alpha_color)); - -            gGL.pushUIMatrix(); -            LLUI::translate((F32) thumbnail_rect.mLeft, (F32) thumbnail_rect.mBottom); -            mThumbnailPlaceholder->draw(); -            gGL.popUIMatrix(); -        } -    } -    impl->updateLayout(this); -} - -void LLFloaterSimpleOutfitSnapshot::onOpen(const LLSD& key) -{ -    LLSnapshotLivePreview* preview = getPreviewView(); -    if (preview) -    { -        preview->updateSnapshot(TRUE); -    } -    focusFirstItem(FALSE); -    gSnapshotFloaterView->setEnabled(TRUE); -    gSnapshotFloaterView->setVisible(TRUE); -    gSnapshotFloaterView->adjustToFitScreen(this, FALSE); - -    impl->updateControls(this); -    impl->setStatus(ImplBase::STATUS_READY); -} - -void LLFloaterSimpleOutfitSnapshot::onCancel() -{ -    closeFloater(); -} - -void LLFloaterSimpleOutfitSnapshot::onSend() -{ -    S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); -    if (can_afford_transaction(expected_upload_cost)) -    { -        saveTexture(); -        postSave(); -    } -    else -    { -        LLSD args; -        args["COST"] = llformat("%d", expected_upload_cost); -        LLNotificationsUtil::add("ErrorPhotoCannotAfford", args); -        inventorySaveFailed(); -    } -} - -void LLFloaterSimpleOutfitSnapshot::postSave() -{ -    impl->setStatus(ImplBase::STATUS_WORKING); -} - -// static  -void LLFloaterSimpleOutfitSnapshot::update() -{ -    LLFloaterSimpleOutfitSnapshot* inst = findInstance(); -    if (inst != NULL) -    { -        inst->impl->updateLivePreview(); -    } -} - - -// static -LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::findInstance() -{ -    return LLFloaterReg::findTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot"); -} - -// static -LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::getInstance() -{ -    return LLFloaterReg::getTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot"); -} - -void LLFloaterSimpleOutfitSnapshot::saveTexture() -{ -     LLSnapshotLivePreview* previewp = getPreviewView(); -    if (!previewp) -    { -        llassert(previewp != NULL); -        return; -    } - -    if (mOutfitGallery) -    { -        mOutfitGallery->onBeforeOutfitSnapshotSave(); -    } -    previewp->saveTexture(TRUE, getOutfitID().asString()); -    if (mOutfitGallery) -    { -        mOutfitGallery->onAfterOutfitSnapshotSave(); -    } -    closeFloater(); -} - -///---------------------------------------------------------------------------- -/// Class LLSimpleOutfitSnapshotFloaterView -///---------------------------------------------------------------------------- - -LLSimpleOutfitSnapshotFloaterView::LLSimpleOutfitSnapshotFloaterView(const Params& p) : LLFloaterView(p) -{ -} - -LLSimpleOutfitSnapshotFloaterView::~LLSimpleOutfitSnapshotFloaterView() -{ -} diff --git a/indra/newview/llfloatersimplesnapshot.cpp b/indra/newview/llfloatersimplesnapshot.cpp new file mode 100644 index 0000000000..58604c5628 --- /dev/null +++ b/indra/newview/llfloatersimplesnapshot.cpp @@ -0,0 +1,499 @@ +/**  +* @file llfloatersimplesnapshot.cpp +* @brief Snapshot preview window for saving as a thumbnail +* +* $LicenseInfo:firstyear=2022&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2022, Linden Research, Inc. +*  +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +*  +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +*  +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +*  +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatersimplesnapshot.h" + +#include "llfloaterreg.h" +#include "llimagefiltersmanager.h" +#include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llstatusbar.h" // can_afford_transaction() +#include "llnotificationsutil.h" +#include "llagent.h" +#include "llagentbenefits.h" +#include "llviewercontrol.h" +#include "llviewertexturelist.h" + + + +LLSimpleSnapshotFloaterView* gSimpleSnapshotFloaterView = NULL; + +const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX = 256; +const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN = 64; + +// Thumbnail posting coro + +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) +{ +    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); +    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t +        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_coro", httpPolicy)); +    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); +    LLCore::HttpHeaders::ptr_t httpHeaders; + +    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); +    httpOpts->setFollowRedirects(true); + +    LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders); + +    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + +    if (!status) +    { +        // todo: notification? +        LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL; +        return; +    } +    if (!result.has("uploader")) +    { +        // todo: notification? +        LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL; +        return; +    } +    std::string uploader_cap = result["uploader"].asString(); +    if (uploader_cap.empty()) +    { +        LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL; +        return; +    } + +    // Upload the image + +    LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest); +    LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders); +    LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions); +    S64 length; + +    { +        llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate); +        if (!instream.is_open()) +        { +            LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL; +            return; +        } +        length = instream.tellg(); +    } + +    uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional +    uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required! +    uploaderhttpOpts->setFollowRedirects(true); + +    result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders); + +    httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; +    status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + +    LL_DEBUGS("Thumbnail") << result << LL_ENDL; + +    if (!status) +    { +        LL_WARNS("Thumbnail") << "Failed to upload image " << status.toString() << LL_ENDL; +        return; +    } + +    if (result["state"].asString() != "complete") +    { +        if (result.has("message")) +        { +            LL_WARNS("Thumbnail") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL; +        } +        else +        { +            LL_WARNS("Thumbnail") << "Failed to upload image " << result << LL_ENDL; +        } +        return; +    } + +    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); +    } +} + +///---------------------------------------------------------------------------- +/// Class LLFloaterSimpleSnapshot::Impl +///---------------------------------------------------------------------------- + +LLSnapshotModel::ESnapshotFormat LLFloaterSimpleSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater) +{ +    return LLSnapshotModel::SNAPSHOT_FORMAT_PNG; +} + +LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater) +{ +    return LLSnapshotModel::SNAPSHOT_TYPE_COLOR; +} + +void LLFloaterSimpleSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater) +{ +    LLSnapshotLivePreview* previewp = getPreviewView(); +    updateResolution(floater); +    if (previewp) +    { +        previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE); +        previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG); +        previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR); +    } +} + +std::string LLFloaterSimpleSnapshot::Impl::getSnapshotPanelPrefix() +{ +    return "panel_outfit_snapshot_"; +} + +void LLFloaterSimpleSnapshot::Impl::updateResolution(void* data) +{ +    LLFloaterSimpleSnapshot *view = (LLFloaterSimpleSnapshot *)data; + +    if (!view) +    { +        llassert(view); +        return; +    } + +    S32 width = THUMBNAIL_SNAPSHOT_DIM_MAX; +    S32 height = THUMBNAIL_SNAPSHOT_DIM_MAX; + +    LLSnapshotLivePreview* previewp = getPreviewView(); +    if (previewp) +    { +        S32 original_width = 0, original_height = 0; +        previewp->getSize(original_width, original_height); + +        if (gSavedSettings.getBOOL("RenderHUDInSnapshot")) +        { //clamp snapshot resolution to window size when showing UI HUD in snapshot +            width = llmin(width, gViewerWindow->getWindowWidthRaw()); +            height = llmin(height, gViewerWindow->getWindowHeightRaw()); +        } + +        llassert(width > 0 && height > 0); + +        previewp->setSize(width, height); + +        if (original_width != width || original_height != height) +        { +            // hide old preview as the aspect ratio could be wrong +            checkAutoSnapshot(previewp, FALSE); +            previewp->updateSnapshot(TRUE); +        } +    } +} + +void LLFloaterSimpleSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg) +{ +    switch (status) +    { +    case STATUS_READY: +        mFloater->setCtrlsEnabled(true); +        break; +    case STATUS_WORKING: +        mFloater->setCtrlsEnabled(false); +        break; +    case STATUS_FINISHED: +        mFloater->setCtrlsEnabled(true); +        break; +    } + +    mStatus = status; +} + +///----------------------------------------------------------------re------------ +/// Class LLFloaterSimpleSnapshot +///---------------------------------------------------------------------------- + +LLFloaterSimpleSnapshot::LLFloaterSimpleSnapshot(const LLSD& key) +    : LLFloaterSnapshotBase(key) +    , mOwner(NULL) +    , mContextConeOpacity(0.f) +{ +    impl = new Impl(this); +} + +LLFloaterSimpleSnapshot::~LLFloaterSimpleSnapshot() +{ +} + +BOOL LLFloaterSimpleSnapshot::postBuild() +{ +    childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this); +    childSetAction("save_btn", boost::bind(&LLFloaterSimpleSnapshot::onSend, this)); +    childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleSnapshot::onCancel, this)); + +    mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); + +    // create preview window +    LLRect full_screen_rect = getRootView()->getRect(); +    LLSnapshotLivePreview::Params p; +    p.rect(full_screen_rect); +    LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); + +    // Do not move LLFloaterSimpleSnapshot floater into gSnapshotFloaterView +    // since it can be a dependednt floater and does not draw UI + +    impl->mPreviewHandle = previewp->getHandle(); +    previewp->setContainer(this); +    impl->updateControls(this); +    impl->setAdvanced(true); +    impl->setSkipReshaping(true); + +    previewp->mKeepAspectRatio = FALSE; +    previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect()); +    previewp->setAllowRenderUI(false); +    previewp->setThumbnailSubsampled(TRUE); + +    return TRUE; +} + +const S32 PREVIEW_OFFSET_Y = 70; + +void LLFloaterSimpleSnapshot::draw() +{ +    if (mOwner) +    { +        static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); +        drawConeToOwner(mContextConeOpacity, max_opacity, mOwner); +    } + +    LLSnapshotLivePreview* previewp = getPreviewView(); + +    if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock())) +    { +        // don't render snapshot window in snapshot, even if "show ui" is turned on +        return; +    } + +    LLFloater::draw(); + +    if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible()) +    {		 +        if(previewp->getThumbnailImage()) +        { +            bool working = impl->getStatus() == ImplBase::STATUS_WORKING; +            const S32 thumbnail_w = previewp->getThumbnailWidth(); +            const S32 thumbnail_h = previewp->getThumbnailHeight(); + +            LLRect local_rect = getLocalRect(); +            S32 offset_x = (local_rect.getWidth() - thumbnail_w) / 2; +            S32 offset_y = PREVIEW_OFFSET_Y; + +            gGL.matrixMode(LLRender::MM_MODELVIEW); +            // Apply floater transparency to the texture unless the floater is focused. +            F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); +            LLColor4 color = working ? LLColor4::grey4 : LLColor4::white; +            gl_draw_scaled_image(offset_x, offset_y,  +                thumbnail_w, thumbnail_h, +                previewp->getThumbnailImage(), color % alpha); +        } +    } +    impl->updateLayout(this); +} + +void LLFloaterSimpleSnapshot::onOpen(const LLSD& key) +{ +    LLSnapshotLivePreview* preview = getPreviewView(); +    if (preview) +    { +        preview->updateSnapshot(TRUE); +    } +    focusFirstItem(FALSE); +    gSnapshotFloaterView->setEnabled(TRUE); +    gSnapshotFloaterView->setVisible(TRUE); +    gSnapshotFloaterView->adjustToFitScreen(this, FALSE); + +    impl->updateControls(this); +    impl->setStatus(ImplBase::STATUS_READY); + +    mInventoryId = key["item_id"].asUUID(); +    mTaskId = key["task_id"].asUUID(); +} + +void LLFloaterSimpleSnapshot::onCancel() +{ +    closeFloater(); +} + +void LLFloaterSimpleSnapshot::onSend() +{ +    LLSnapshotLivePreview* previewp = getPreviewView(); + +    std::string temp_file = gDirUtilp->getTempFilename(); +    if (previewp->createUploadFile(temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN)) +    { +        uploadImageUploadFile(temp_file, mInventoryId, mTaskId); +        closeFloater(); +    } +    else +    { +        LLSD notif_args; +        notif_args["REASON"] = LLImage::getLastError().c_str(); +        LLNotificationsUtil::add("CannotUploadTexture", notif_args); +    } +} + +void LLFloaterSimpleSnapshot::postSave() +{ +    impl->setStatus(ImplBase::STATUS_WORKING); +} + +// static +void LLFloaterSimpleSnapshot::uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id) +{ +    // generate a temp texture file for coroutine +    std::string temp_file = gDirUtilp->getTempFilename(); +    U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path)); +    if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN, true)) +    { +        LLSD notif_args; +        notif_args["REASON"] = LLImage::getLastError().c_str(); +        LLNotificationsUtil::add("CannotUploadTexture", notif_args); +        LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL; +        return; +    } +    uploadImageUploadFile(temp_file, inventory_id, task_id); +} + +// static +void LLFloaterSimpleSnapshot::uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id) +{ +    std::string temp_file = gDirUtilp->getTempFilename(); +    if (!LLViewerTextureList::createUploadFile(raw_image, temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN)) +    { +        LLSD notif_args; +        notif_args["REASON"] = LLImage::getLastError().c_str(); +        LLNotificationsUtil::add("CannotUploadTexture", notif_args); +        LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL; +        return; +    } +    uploadImageUploadFile(temp_file, inventory_id, task_id); +} + +// static +void LLFloaterSimpleSnapshot::uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id) +{ +    LLSD data; + +    if (task_id.notNull()) +    { +        data["item_id"] = inventory_id; +        data["task_id"] = task_id; +    } +    else if (gInventory.getCategory(inventory_id)) +    { +        data["category_id"] = inventory_id; +    } +    else +    { +        data["item_id"] = inventory_id; +    } + +    std::string cap_url = gAgent.getRegionCapability(THUMBNAIL_UPLOAD_CAP); +    if (cap_url.empty()) +    { +        LLSD args; +        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; +    } + +    LLCoros::instance().launch("postAgentUserImageCoro", +        boost::bind(post_thumbnail_image_coro, cap_url, temp_file, data)); +} + +void LLFloaterSimpleSnapshot::update() +{ +    // initializes snapshots when needed +    LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("simple_snapshot"); +    for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); +        iter != inst_list.end(); ++iter) +    { +        LLFloaterSimpleSnapshot* floater = dynamic_cast<LLFloaterSimpleSnapshot*>(*iter); +        if (floater) +        { +            floater->impl->updateLivePreview(); +        } +    } +} + + +// static +LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::findInstance(const LLSD &key) +{ +    return LLFloaterReg::findTypedInstance<LLFloaterSimpleSnapshot>("simple_snapshot", key); +} + +// static +LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::getInstance(const LLSD &key) +{ +    return LLFloaterReg::getTypedInstance<LLFloaterSimpleSnapshot>("simple_snapshot", key); +} + +void LLFloaterSimpleSnapshot::saveTexture() +{ +    LLSnapshotLivePreview* previewp = getPreviewView(); +    if (!previewp) +    { +        llassert(previewp != NULL); +        return; +    } + +    previewp->saveTexture(TRUE, getInventoryId().asString()); +    closeFloater(); +} + +///---------------------------------------------------------------------------- +/// Class LLSimpleOutfitSnapshotFloaterView +///---------------------------------------------------------------------------- + +LLSimpleSnapshotFloaterView::LLSimpleSnapshotFloaterView(const Params& p) : LLFloaterView(p) +{ +} + +LLSimpleSnapshotFloaterView::~LLSimpleSnapshotFloaterView() +{ +} diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.h b/indra/newview/llfloatersimplesnapshot.h index cc9a6c5d1e..91a81ee5c3 100644 --- a/indra/newview/llfloatersimpleoutfitsnapshot.h +++ b/indra/newview/llfloatersimplesnapshot.h @@ -1,6 +1,6 @@  /** -* @file llfloatersimpleoutfitsnapshot.h -* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery +* @file llfloatersimplesnapshot.h +* @brief Snapshot preview window for saving as a thumbnail  *  * $LicenseInfo:firstyear=2022&license=viewerlgpl$  * Second Life Viewer Source Code @@ -24,26 +24,25 @@  * $/LicenseInfo$  */ -#ifndef LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H -#define LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H +#ifndef LL_LLFLOATERSIMPLESNAPSHOT_H +#define LL_LLFLOATERSIMPLESNAPSHOT_H  #include "llfloater.h"  #include "llfloatersnapshot.h" -#include "lloutfitgallery.h"  #include "llsnapshotlivepreview.h"  ///---------------------------------------------------------------------------- -/// Class LLFloaterSimpleOutfitSnapshot +/// Class LLFloaterSimpleSnapshot  ///---------------------------------------------------------------------------- -class LLFloaterSimpleOutfitSnapshot : public LLFloaterSnapshotBase +class LLFloaterSimpleSnapshot : public LLFloaterSnapshotBase  { -    LOG_CLASS(LLFloaterSimpleOutfitSnapshot); +    LOG_CLASS(LLFloaterSimpleSnapshot);  public: -    LLFloaterSimpleOutfitSnapshot(const LLSD& key); -    ~LLFloaterSimpleOutfitSnapshot(); +    LLFloaterSimpleSnapshot(const LLSD& key); +    ~LLFloaterSimpleSnapshot();      BOOL postBuild();      void onOpen(const LLSD& key); @@ -51,36 +50,48 @@ public:      static void update(); -    static LLFloaterSimpleOutfitSnapshot* getInstance(); -    static LLFloaterSimpleOutfitSnapshot* findInstance(); +    static LLFloaterSimpleSnapshot* getInstance(const LLSD &key); +    static LLFloaterSimpleSnapshot* findInstance(const LLSD &key);      void saveTexture();      const LLRect& getThumbnailPlaceholderRect() { return mThumbnailPlaceholder->getRect(); } -    void setOutfitID(LLUUID id) { mOutfitID = id; } -    LLUUID getOutfitID() { return mOutfitID; } -    void setGallery(LLOutfitGallery* gallery) { mOutfitGallery = gallery; } +    void setInventoryId(const LLUUID &inventory_id) { mInventoryId = inventory_id; } +    LLUUID getInventoryId() { return mInventoryId; } +    void setTaskId(const LLUUID &task_id) { mTaskId = task_id; } +    void setOwner(LLView *owner_view) { mOwner = owner_view; }      void postSave(); +    static void uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id); +    static void uploadThumbnail(LLPointer<LLImageRaw> raw_image, const LLUUID& inventory_id, const LLUUID& task_id);      class Impl;      friend class Impl; +    static const S32 THUMBNAIL_SNAPSHOT_DIM_MAX; +    static const S32 THUMBNAIL_SNAPSHOT_DIM_MIN; +  private:      void onSend();      void onCancel(); -    LLUUID mOutfitID; -    LLOutfitGallery* mOutfitGallery; +    // uploads upload-ready file +    static void uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id); + +    LLUUID mInventoryId; +    LLUUID mTaskId; + +    LLView* mOwner; +    F32	 mContextConeOpacity;  };  ///---------------------------------------------------------------------------- -/// Class LLFloaterSimpleOutfitSnapshot::Impl +/// Class LLFloaterSimpleSnapshot::Impl  ///---------------------------------------------------------------------------- -class LLFloaterSimpleOutfitSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase +class LLFloaterSimpleSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase  { -    LOG_CLASS(LLFloaterSimpleOutfitSnapshot::Impl); +    LOG_CLASS(LLFloaterSimpleSnapshot::Impl);  public:      Impl(LLFloaterSnapshotBase* floater)          : LLFloaterSnapshotBase::ImplBase(floater) @@ -108,7 +119,7 @@ private:  /// Class LLSimpleOutfitSnapshotFloaterView  ///---------------------------------------------------------------------------- -class LLSimpleOutfitSnapshotFloaterView : public LLFloaterView +class LLSimpleSnapshotFloaterView : public LLFloaterView  {  public:      struct Params @@ -117,13 +128,13 @@ public:      };  protected: -    LLSimpleOutfitSnapshotFloaterView(const Params& p); +    LLSimpleSnapshotFloaterView(const Params& p);      friend class LLUICtrlFactory;  public: -    virtual ~LLSimpleOutfitSnapshotFloaterView(); +    virtual ~LLSimpleSnapshotFloaterView();  }; -extern LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView; +extern LLSimpleSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView; -#endif // LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H +#endif // LL_LLFLOATERSIMPLESNAPSHOT_H diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h index de28091c32..1649b2eed7 100644 --- a/indra/newview/llfolderviewmodelinventory.h +++ b/indra/newview/llfolderviewmodelinventory.h @@ -39,12 +39,14 @@ class LLFolderViewModelItemInventory  public:  	LLFolderViewModelItemInventory(class LLFolderViewModelInventory& root_view_model);  	virtual const LLUUID& getUUID() const = 0; +    virtual const LLUUID& getThumbnailUUID() const = 0;  	virtual time_t getCreationDate() const = 0;	// UTC seconds  	virtual void setCreationDate(time_t creation_date_utc) = 0;  	virtual PermissionMask getPermissionMask() const = 0;  	virtual LLFolderType::EType getPreferredType() const = 0;  	virtual void showProperties(void) = 0;  	virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make   into pure virtual. +    virtual bool isItemInOutfits() const { return false; }  	virtual BOOL isAgentInventory() const { return FALSE; }  	virtual BOOL isUpToDate() const = 0;      virtual void addChild(LLFolderViewModelItem* child); diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index e395da7f1e..ce28915d93 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/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index 5329f10612..c52d0be213 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -662,9 +662,7 @@ void LLInspectObject::onClickOpen()  void LLInspectObject::onClickMoreInfo()  { -	LLSD key; -	key["task"] = "task"; -	LLFloaterSidePanelContainer::showPanel("inventory", key); +    LLFloaterReg::showInstance("task_properties");  	closeFloater();  } diff --git a/indra/newview/llinspecttexture.cpp b/indra/newview/llinspecttexture.cpp new file mode 100644 index 0000000000..da4e3c0949 --- /dev/null +++ b/indra/newview/llinspecttexture.cpp @@ -0,0 +1,249 @@ +/** + * @file llinspecttexture.cpp + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinspect.h" +#include "llinspecttexture.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "lltexturectrl.h" +#include "lltrans.h" +#include "llviewertexturelist.h" + + +// ============================================================================ +// Helper functions +// + +LLToolTip* LLInspectTextureUtil::createInventoryToolTip(LLToolTip::Params p) +{ +    const LLSD& sdTooltip = p.create_params; +     +    if (sdTooltip.has("thumbnail_id") && sdTooltip["thumbnail_id"].asUUID().notNull()) +    { +        // go straight for thumbnail regardless of type +        // TODO: make a tooltip factory? +        return LLUICtrlFactory::create<LLTextureToolTip>(p); +    } + +	LLInventoryType::EType eInvType = (sdTooltip.has("inv_type")) ? (LLInventoryType::EType)sdTooltip["inv_type"].asInteger() : LLInventoryType::IT_NONE; +	switch (eInvType) +	{ +		case LLInventoryType::IT_CATEGORY: +			{ +				if (sdTooltip.has("item_id")) +				{ +					const LLUUID idCategory = sdTooltip["item_id"].asUUID(); +                    LLViewerInventoryCategory* cat = gInventory.getCategory(idCategory); +                    if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) +                    { +                        LLInventoryModel::cat_array_t cats; +                        LLInventoryModel::item_array_t items; +                        // Not LLIsOfAssetType, because we allow links +                        LLIsTextureType f; +                        gInventory.getDirectDescendentsOf(idCategory, cats, items, f); + +                        // Exactly one texture found => show the texture tooltip +                        if (1 == items.size()) +                        { +                            LLViewerInventoryItem* item = items.front(); +                            if (item && item->getIsLinkType()) +                            { +                                item = item->getLinkedItem(); +                            } +                            if (item) +                            { +                                // Note: LLFloaterChangeItemThumbnail will attempt to write this +                                // into folder's thumbnail id when opened +                                p.create_params.getValue()["thumbnail_id"] = item->getAssetUUID(); +                                return LLUICtrlFactory::create<LLTextureToolTip>(p); +                            } +                        } +                    } +				} + +				// No or more than one texture found => show default tooltip +				return LLUICtrlFactory::create<LLToolTip>(p); +			} +		default: +			return LLUICtrlFactory::create<LLToolTip>(p); +	} +} + +// ============================================================================ +// LLTexturePreviewView helper class +// + +class LLTexturePreviewView : public LLView +{ +public: +	LLTexturePreviewView(const LLView::Params& p); +	~LLTexturePreviewView(); + +public: +	void draw() override; + +public: +	void setImageFromAssetId(const LLUUID& idAsset); +	void setImageFromItemId(const LLUUID& idItem); + +protected: +	LLPointer<LLViewerFetchedTexture> m_Image; +	S32         mImageBoostLevel = LLGLTexture::BOOST_NONE; +	std::string mLoadingText; +}; + + +LLTexturePreviewView::LLTexturePreviewView(const LLView::Params& p) +	: LLView(p) +{ +	mLoadingText = LLTrans::getString("texture_loading"); +} + +LLTexturePreviewView::~LLTexturePreviewView() +{ +	if (m_Image) +	{ +		m_Image->setBoostLevel(mImageBoostLevel); +		m_Image = nullptr; +	} +} + +void LLTexturePreviewView::draw() +{ +    LLView::draw(); + +	if (m_Image) +	{ +		LLRect rctClient = getLocalRect(); + +        if (4 == m_Image->getComponents()) +        { +            const LLColor4 color(.098f, .098f, .098f); +            gl_rect_2d(rctClient, color, TRUE); +        } +		gl_draw_scaled_image(rctClient.mLeft, rctClient.mBottom, rctClient.getWidth(), rctClient.getHeight(), m_Image); + +		bool isLoading = (!m_Image->isFullyLoaded()) && (m_Image->getDiscardLevel() > 0); +		if (isLoading) +			LLFontGL::getFontSansSerif()->renderUTF8(mLoadingText, 0, llfloor(rctClient.mLeft + 3),  llfloor(rctClient.mTop - 25), LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); +		m_Image->addTextureStats((isLoading) ? MAX_IMAGE_AREA : (F32)(rctClient.getWidth() * rctClient.getHeight())); +	} +} + +void LLTexturePreviewView::setImageFromAssetId(const LLUUID& idAsset) +{ +	m_Image = LLViewerTextureManager::getFetchedTexture(idAsset, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); +	if (m_Image) +	{ +		mImageBoostLevel = m_Image->getBoostLevel(); +		m_Image->setBoostLevel(LLGLTexture::BOOST_PREVIEW); +		m_Image->forceToSaveRawImage(0); +		if ( (!m_Image->isFullyLoaded()) && (!m_Image->hasFetcher()) ) +		{ +			if (m_Image->isInFastCacheList()) +			{ +				m_Image->loadFromFastCache(); +			} +			gTextureList.forceImmediateUpdate(m_Image); +		} +	} +} + +void LLTexturePreviewView::setImageFromItemId(const LLUUID& idItem) +{ +	const LLViewerInventoryItem* pItem = gInventory.getItem(idItem); +	setImageFromAssetId( (pItem) ? pItem->getAssetUUID() : LLUUID::null ); +} + +// ============================================================================ +// LLTextureToolTip class +// + +LLTextureToolTip::LLTextureToolTip(const LLToolTip::Params& p) +	: LLToolTip(p) +	, mPreviewView(nullptr) +	, mPreviewSize(256) +{ +	mMaxWidth = llmax(mMaxWidth, mPreviewSize); + +    // Currently has to share params with LLToolTip, override values +    setBackgroundColor(LLColor4::black); +    setTransparentColor(LLColor4::black); +    setBorderVisible(true); +} + +LLTextureToolTip::~LLTextureToolTip() +{ +} + +void LLTextureToolTip::initFromParams(const LLToolTip::Params& p) +{ +    LLToolTip::initFromParams(p); +     +    // Create and add the preview control +    LLView::Params p_preview; +    p_preview.name = "texture_preview"; +    LLRect rctPreview; +    rctPreview.setOriginAndSize(mPadding, mTextBox->getRect().mTop, mPreviewSize, mPreviewSize); +    p_preview.rect = rctPreview; +    mPreviewView = LLUICtrlFactory::create<LLTexturePreviewView>(p_preview); +    addChild(mPreviewView); +     +    // Parse the control params +    const LLSD& sdTextureParams = p.create_params; +    if (sdTextureParams.has("thumbnail_id")) +    { +        mPreviewView->setImageFromAssetId(sdTextureParams["thumbnail_id"].asUUID()); +    } +    else if (sdTextureParams.has("item_id")) +    { +        mPreviewView->setImageFromItemId(sdTextureParams["item_id"].asUUID()); +    } + +    // Currently has to share params with LLToolTip, override values manually +    // Todo: provide from own params instead, may be like object inspector does it +    LLViewBorder::Params border_params; +    border_params.border_thickness(LLPANEL_BORDER_WIDTH); +    border_params.highlight_light_color(LLColor4::white); +    border_params.highlight_dark_color(LLColor4::white); +    border_params.shadow_light_color(LLColor4::white); +    border_params.shadow_dark_color(LLColor4::white); +    addBorder(border_params); +    setBorderVisible(true); + +    setBackgroundColor(LLColor4::black); +    setBackgroundVisible(true); +    setBackgroundOpaque(true); +    setBackgroundImage(nullptr); +    setTransparentImage(nullptr); + +    mTextBox->setColor(LLColor4::white); + +	snapToChildren(); +} + +// ============================================================================ diff --git a/indra/newview/llinspecttexture.h b/indra/newview/llinspecttexture.h new file mode 100644 index 0000000000..ff0d80b825 --- /dev/null +++ b/indra/newview/llinspecttexture.h @@ -0,0 +1,49 @@ +/** + * @file llinspecttexture.h + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "lltooltip.h" + +class LLTexturePreviewView; + +namespace LLInspectTextureUtil +{ +	LLToolTip* createInventoryToolTip(LLToolTip::Params p); +} + +class LLTextureToolTip : public LLToolTip +{ +public: +	LLTextureToolTip(const LLToolTip::Params& p); +	~LLTextureToolTip(); + +public: +	void initFromParams(const LLToolTip::Params& p) override; + +protected: +	LLTexturePreviewView* mPreviewView; +	S32                   mPreviewSize; +}; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index db347f7096..73005d6903 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -41,7 +41,6 @@  #include "llfloateropenobject.h"  #include "llfloaterreg.h"  #include "llfloatermarketplacelistings.h" -#include "llfloateroutfitphotopreview.h"  #include "llfloatersidepanelcontainer.h"  #include "llsidepanelinventory.h"  #include "llfloaterworldmap.h" @@ -90,29 +89,13 @@  void copy_slurl_to_clipboard_callback_inv(const std::string& slurl); -typedef std::pair<LLUUID, LLUUID> two_uuids_t; -typedef std::list<two_uuids_t> two_uuids_list_t; -  const F32 SOUND_GAIN = 1.0f; -struct LLMoveInv -{ -	LLUUID mObjectID; -	LLUUID mCategoryID; -	two_uuids_list_t mMoveList; -	void (*mCallback)(S32, void*); -	void* mUserData; -}; -  using namespace LLOldEvents;  // Function declarations -bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, boost::shared_ptr<LLMoveInv>);  bool confirm_attachment_rez(const LLSD& notification, const LLSD& response);  void teleport_via_landmark(const LLUUID& asset_id); -static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); -static bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit); -static BOOL can_move_to_landmarks(LLInventoryItem* inv_item);  static bool check_category(LLInventoryModel* model,  						   const LLUUID& cat_id,  						   LLInventoryPanel* active_panel, @@ -138,6 +121,12 @@ bool isMarketplaceSendAction(const std::string& action)  	return ("send_to_marketplace" == action);  } +bool isPanelActive(const std::string& panel_name) +{ +    LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); +    return (active_panel && (active_panel->getName() == panel_name)); +} +  // Used by LLFolderBridge as callback for directory fetching recursion  class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver  { @@ -173,6 +162,65 @@ public:  	}  }; +class LLPasteIntoFolderCallback: public LLInventoryCallback +{ +public: +    LLPasteIntoFolderCallback(LLHandle<LLInventoryPanel>& handle) +        : mInventoryPanel(handle) +    { +    } +    ~LLPasteIntoFolderCallback() +    { +        processItems(); +    } + +    void fire(const LLUUID& inv_item) +    { +        mChangedIds.push_back(inv_item); +    } + +    void processItems() +    { +        LLInventoryPanel* panel = mInventoryPanel.get(); +        bool has_elements = false; +        for (LLUUID& inv_item : mChangedIds) +        { +            LLInventoryItem* item = gInventory.getItem(inv_item); +            if (item && panel) +            { +                LLUUID root_id = panel->getRootFolderID(); + +                if (inv_item == root_id) +                { +                    return; +                } + +                LLFolderViewItem* item = panel->getItemByID(inv_item); +                if (item) +                { +                    if (!has_elements) +                    { +                        panel->clearSelection(); +                        panel->getRootFolder()->clearSelection(); +                        panel->getRootFolder()->requestArrange(); +                        panel->getRootFolder()->update(); +                        has_elements = true; +                    } +                    panel->getRootFolder()->changeSelection(item, TRUE); +                } +            } +        } + +        if (has_elements) +        { +            panel->getRootFolder()->scrollToShowSelection(); +        } +    } +private: +    LLHandle<LLInventoryPanel> mInventoryPanel; +    std::vector<LLUUID> mChangedIds; +}; +  // +=================================================+  // |        LLInvFVBridge                            |  // +=================================================+ @@ -212,54 +260,17 @@ const std::string& LLInvFVBridge::getDisplayName() const  std::string LLInvFVBridge::getSearchableDescription() const  { -	const LLInventoryModel* model = getInventoryModel(); -	if (model) -	{ -		const LLInventoryItem *item = model->getItem(mUUID); -		if(item) -		{ -			std::string desc = item->getDescription(); -			LLStringUtil::toUpper(desc); -			return desc; -		} -	} -	return LLStringUtil::null; +    return get_searchable_description(getInventoryModel(), mUUID);  }  std::string LLInvFVBridge::getSearchableCreatorName() const  { -	const LLInventoryModel* model = getInventoryModel(); -	if (model) -	{ -		const LLInventoryItem *item = model->getItem(mUUID); -		if(item) -		{ -			LLAvatarName av_name; -			if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) -			{ -				std::string username = av_name.getUserName(); -				LLStringUtil::toUpper(username); -				return username; -			} -		} -	} -	return LLStringUtil::null; +    return get_searchable_creator_name(getInventoryModel(), mUUID);  }  std::string LLInvFVBridge::getSearchableUUIDString() const  { -	const LLInventoryModel* model = getInventoryModel(); -	if (model) -	{ -		const LLViewerInventoryItem *item = model->getItem(mUUID); -		if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery())) -		{ -			std::string uuid = item->getAssetUUID().asString(); -			LLStringUtil::toUpper(uuid); -			return uuid; -		} -	} -	return LLStringUtil::null; +    return get_searchable_UUID(getInventoryModel(), mUUID);  }  // Folders have full perms @@ -327,7 +338,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) || @@ -407,6 +418,32 @@ void LLInvFVBridge::showProperties()      }  } +void LLInvFVBridge::navigateToFolder(bool new_window, bool change_mode) +{ +    if(new_window) +    { +        mInventoryPanel.get()->openSingleViewInventory(mUUID); +    } +    else +    { +        if(change_mode) +        { +            LLInventoryPanel::setSFViewAndOpenFolder(mInventoryPanel.get(), mUUID); +        } +        else +        { +            LLInventorySingleFolderPanel* panel = dynamic_cast<LLInventorySingleFolderPanel*>(mInventoryPanel.get()); +            if (!panel || !getInventoryModel() || mUUID.isNull()) +            { +                return; +            } + +            panel->changeFolderRoot(mUUID); +        } + +    } +} +  void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch)  {  	// Deactivate gestures when moving them into Trash @@ -800,6 +837,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,  										menuentry_vec_t &disabled_items, U32 flags)  {  	const LLInventoryObject *obj = getInventoryObject(); +    bool single_folder_root = (mRoot == NULL);  	if (obj)  	{ @@ -811,7 +849,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,  			disabled_items.push_back(std::string("Copy"));  		} -        if (isAgentInventory()) +        if (isAgentInventory() && !single_folder_root)          {              items.push_back(std::string("New folder from selected"));              items.push_back(std::string("Subfolder Separator")); @@ -845,7 +883,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,  				items.push_back(std::string("Find Links"));  			} -			if (!isInboxFolder()) +			if (!isInboxFolder() && !single_folder_root)  			{  				items.push_back(std::string("Rename"));  				if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0)) @@ -853,14 +891,20 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,  					disabled_items.push_back(std::string("Rename"));  				}  			} -			 + +            items.push_back(std::string("thumbnail")); +            if (isLibraryItem()) +            { +                disabled_items.push_back(std::string("thumbnail")); +            } + +            LLViewerInventoryItem *inv_item = gInventory.getItem(mUUID);  			if (show_asset_id)  			{  				items.push_back(std::string("Copy Asset UUID"));  				bool is_asset_knowable = false; -				LLViewerInventoryItem* inv_item = gInventory.getItem(mUUID);  				if (inv_item)  				{  					is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(inv_item->getType()); @@ -873,6 +917,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,  				}  			} +            if(!single_folder_root) +            {  			items.push_back(std::string("Cut"));  			if (!isItemMovable() || !isItemRemovable())  			{ @@ -894,6 +940,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,                      }                  }  			} +            }  		}  	} @@ -919,16 +966,12 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,  	items.push_back(std::string("Paste Separator")); -	addDeleteContextMenuOptions(items, disabled_items); - -	// If multiple items are selected, disable properties (if it exists). -	if ((flags & FIRST_SELECTED_ITEM) == 0) -	{ -		disabled_items.push_back(std::string("Properties")); -	} +    if(!single_folder_root) +    { +        addDeleteContextMenuOptions(items, disabled_items); +    } -	LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); -	if (active_panel && (active_panel->getName() != "All Items")) +	if (!isPanelActive("All Items") && !isPanelActive("comb_single_folder_inv"))  	{  		items.push_back(std::string("Show in Main Panel"));  	} @@ -1019,7 +1062,7 @@ void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items,  	items.push_back(std::string("Delete")); -	if (!isItemRemovable()) +	if (!isItemRemovable() || isPanelActive("Favorite Items"))  	{  		disabled_items.push_back(std::string("Delete"));  	} @@ -1250,6 +1293,16 @@ BOOL LLInvFVBridge::isLinkedObjectInTrash() const  	return FALSE;  } +bool LLInvFVBridge::isItemInOutfits() const +{ +    const LLInventoryModel* model = getInventoryModel(); +    if(!model) return false; + +    const LLUUID my_outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + +    return isCOFFolder() || (my_outfits_cat == mUUID) || model->isObjectDescendentOf(mUUID, my_outfits_cat); +} +  BOOL LLInvFVBridge::isLinkedObjectMissing() const  {  	const LLInventoryObject *obj = getInventoryObject(); @@ -1280,7 +1333,7 @@ BOOL LLInvFVBridge::isCOFFolder() 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())  	{ @@ -1292,7 +1345,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())  	{ @@ -1592,7 +1645,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); @@ -1691,6 +1744,12 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)  		restoreItem();  		return;  	} +    else if ("thumbnail" == action) +    { +        LLSD data(mUUID); +        LLFloaterReg::showInstance("change_item_thumbnail", data); +        return; +    }  	else if ("copy_uuid" == action)  	{  		// Single item only @@ -1745,7 +1804,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));      } @@ -1959,9 +2018,9 @@ std::string LLItemBridge::getLabelSuffix() const  {  	// String table is loaded before login screen and inventory items are  	// loaded after login, so LLTrans should be ready. -	static std::string NO_COPY = LLTrans::getString("no_copy"); -	static std::string NO_MOD = LLTrans::getString("no_modify"); -	static std::string NO_XFER = LLTrans::getString("no_transfer"); +	static std::string NO_COPY = LLTrans::getString("no_copy_lbl"); +	static std::string NO_MOD = LLTrans::getString("no_modify_lbl"); +	static std::string NO_XFER = LLTrans::getString("no_transfer_lbl");  	static std::string LINK = LLTrans::getString("link");  	static std::string BROKEN_LINK = LLTrans::getString("broken_link");  	std::string suffix; @@ -1982,17 +2041,20 @@ std::string LLItemBridge::getLabelSuffix() const  			BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());  			if (!copy)  			{ +                suffix += " ";  				suffix += NO_COPY;  			}  			BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());  			if (!mod)  			{ -				suffix += NO_MOD; +                suffix += suffix.empty() ? " " : ","; +                suffix += NO_MOD;  			}  			BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,  																gAgent.getID());  			if (!xfer)  			{ +                suffix += suffix.empty() ? " " : ",";  				suffix += NO_XFER;  			}  		} @@ -2161,6 +2223,21 @@ LLViewerInventoryItem* LLItemBridge::getItem() const  	return item;  } +const LLUUID& LLItemBridge::getThumbnailUUID() const +{ +    LLViewerInventoryItem* item = NULL; +    LLInventoryModel* model = getInventoryModel(); +    if(model) +    { +        item = (LLViewerInventoryItem*)model->getItem(mUUID); +    } +    if (item) +    { +        return item->getThumbnailUUID(); +    } +    return LLUUID::null; +} +  BOOL LLItemBridge::isItemPermissive() const  {  	LLViewerInventoryItem* item = getItem(); @@ -2244,13 +2321,32 @@ void LLFolderBridge::buildDisplayName() const  std::string LLFolderBridge::getLabelSuffix() const  {      static LLCachedControl<F32> folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); +    static LLCachedControl<bool> xui_debug(gSavedSettings, "DebugShowXUINames", 0);      if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay())      {          return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str());      }      std::string suffix = ""; -    if(mShowDescendantsCount) +    if (xui_debug) +    { +        LLInventoryModel::cat_array_t* cats; +        LLInventoryModel::item_array_t* items; +        gInventory.getDirectDescendentsOf(getUUID(), cats, items); +         +        LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID()); +        if (cat) +        { +            LLStringUtil::format_map_t args; +            args["[FOLDER_COUNT]"] = llformat("%d", cats->size()); +            args["[ITEMS_COUNT]"] = llformat("%d", items->size()); +            args["[VERSION]"] = llformat("%d", cat->getVersion()); +            args["[VIEWER_DESCENDANT_COUNT]"] = llformat("%d", cats->size() + items->size()); +            args["[SERVER_DESCENDANT_COUNT]"] = llformat("%d", cat->getDescendentCount()); +            suffix = " " + LLTrans::getString("InventoryFolderDebug", args); +        } +    } +    else if(mShowDescendantsCount)      {          LLInventoryModel::cat_array_t cat_array;          LLInventoryModel::item_array_t item_array; @@ -2274,6 +2370,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) @@ -2484,7 +2590,8 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,  											BOOL drop,  											std::string& tooltip_msg,  											BOOL is_link, -											BOOL user_confirm) +											BOOL user_confirm, +                                            LLPointer<LLInventoryCallback> cb)  {  	LLInventoryModel* model = getInventoryModel(); @@ -2501,8 +2608,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); @@ -2520,10 +2627,10 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,  	if (is_agent_inventory)  	{ -		const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); -		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 &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); +		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); @@ -2789,7 +2896,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,  			{  				// Category can contains objects,  				// create a new folder and populate it with links to original objects -				dropToMyOutfits(inv_cat); +				dropToMyOutfits(inv_cat, cb);  			}  			// if target is current outfit folder we use link  			else if (move_is_into_current_outfit && @@ -2799,14 +2906,16 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,  				// traverse category and add all contents to currently worn.  				BOOL append = true;  				LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append); +                if (cb) cb->fire(inv_cat->getUUID());  			}  			else if (move_is_into_marketplacelistings)  			{  				move_folder_to_marketplacelistings(inv_cat, mUUID); +                if (cb) cb->fire(inv_cat->getUUID());  			}  			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);  				} @@ -2818,6 +2927,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,  					(LLViewerInventoryCategory*)inv_cat,  					mUUID,  					move_is_into_trash); +                if (cb) cb->fire(inv_cat->getUUID());  			}              if (move_is_from_marketplacelistings)              { @@ -2836,14 +2946,20 @@ 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); +                    if (cb) cb->fire(inv_cat->getUUID());                  }              }  		} @@ -2857,7 +2973,22 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,  		}  		else  		{ -			accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, NULL, NULL, filter); +            // Todo: fix me. moving from task inventory doesn't have a completion callback, +            // yet making a copy creates new item id so this doesn't work right +            std::function<void(S32, void*, const LLMoveInv*)> callback = [cb](S32, void*, const LLMoveInv* move_inv) mutable +            { +                two_uuids_list_t::const_iterator move_it; +                for (move_it = move_inv->mMoveList.begin(); +                     move_it != move_inv->mMoveList.end(); +                     ++move_it) +                { +                    if (cb) +                    { +                        cb->fire(move_it->second); +                    } +                } +            }; +			accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, callback, NULL, filter);  		}  	}  	else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) @@ -2928,7 +3059,7 @@ void warn_move_inventory(LLViewerObject* object, boost::shared_ptr<LLMoveInv> mo  BOOL move_inv_category_world_to_agent(const LLUUID& object_id,  									  const LLUUID& category_id,  									  BOOL drop, -									  void (*callback)(S32, void*), +									  std::function<void(S32, void*, const LLMoveInv*)> callback,  									  void* user_data,  									  LLInventoryFilter* filter)  { @@ -3231,6 +3362,12 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)  		return;  	} +    else if ("thumbnail" == action) +    { +        LLSD data(mUUID); +        LLFloaterReg::showInstance("change_item_thumbnail", data); +        return; +    }  	else if ("paste" == action)  	{  		pasteFromClipboard(); @@ -3300,18 +3437,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))) -            { -                LLSD subs; -                subs["[ERROR_CODE]"] = mMessage; -                LLNotificationsUtil::add("MerchantListingFailed", subs); -            } -            else + +            LLMarketplaceValidator::getInstance()->validateMarketplaceListings( +                version_folder_id, +                [this](bool result)              { -                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;  	} @@ -3319,18 +3464,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)) -            { -                LLSD subs; -                subs["[ERROR_CODE]"] = mMessage; -                LLNotificationsUtil::add("MerchantFolderActivationFailed", subs); -            } -            else + +            LLMarketplaceValidator::getInstance()->validateMarketplaceListings( +                mUUID, +                [this](bool result)              { -                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;  	} @@ -3353,29 +3507,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) +            { +                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              { -                LLNotificationsUtil::add("MerchantForceValidateListing"); +                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) @@ -3419,7 +3588,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)));      }  } @@ -3669,7 +3838,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; @@ -3730,11 +3899,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); @@ -3745,6 +3914,13 @@ void LLFolderBridge::perform_pasteFromClipboard()  		std::vector<LLUUID> objects;  		LLClipboard::instance().pasteFromClipboard(objects); + +        LLPointer<LLInventoryCallback> cb = NULL; +        LLInventoryPanel* panel = mInventoryPanel.get(); +        if (panel->getRootFolder()->isSingleFolderMode() && panel->getRootFolderID() == mUUID) +        { +            cb = new LLPasteIntoFolderCallback(mInventoryPanel); +        }          LLViewerInventoryCategory * dest_folder = getCategory();  		if (move_is_into_marketplacelistings) @@ -3820,7 +3996,7 @@ void LLFolderBridge::perform_pasteFromClipboard()  				{  					if (!move_is_into_my_outfits && item && can_move_to_outfit(item, move_is_into_current_outfit))  					{ -						dropToOutfit(item, move_is_into_current_outfit); +						dropToOutfit(item, move_is_into_current_outfit, cb);  					}  					else if (move_is_into_my_outfits && LLAssetType::AT_CATEGORY == obj->getType())  					{ @@ -3828,7 +4004,7 @@ void LLFolderBridge::perform_pasteFromClipboard()  						U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");  						if (cat && can_move_to_my_outfits(model, cat, max_items_to_wear))  						{ -							dropToMyOutfits(cat); +							dropToMyOutfits(cat, cb);  						}  						else  						{ @@ -3844,7 +4020,7 @@ void LLFolderBridge::perform_pasteFromClipboard()  				{  					if (item && can_move_to_outfit(item, move_is_into_current_outfit))  					{ -						dropToOutfit(item, move_is_into_current_outfit); +						dropToOutfit(item, move_is_into_current_outfit, cb);  					}  					else  					{ @@ -3863,11 +4039,12 @@ void LLFolderBridge::perform_pasteFromClipboard()                              {                                  //changeItemParent() implicity calls dirtyFilter                                  changeItemParent(model, viitem, parent_id, FALSE); +                                if (cb) cb->fire(item_id);                              }                          }                          else                          { -                            dropToFavorites(item); +                            dropToFavorites(item, cb);                          }  					}  				} @@ -3895,6 +4072,7 @@ void LLFolderBridge::perform_pasteFromClipboard()                                  //changeCategoryParent() implicity calls dirtyFilter                                  changeCategoryParent(model, vicat, parent_id, FALSE);                              } +                            if (cb) cb->fire(item_id);  						}  					}  					else @@ -3916,6 +4094,7 @@ void LLFolderBridge::perform_pasteFromClipboard()                                  //changeItemParent() implicity calls dirtyFilter                                  changeItemParent(model, viitem, parent_id, FALSE);                              } +                            if (cb) cb->fire(item_id);                          }                      }  				} @@ -3936,6 +4115,7 @@ void LLFolderBridge::perform_pasteFromClipboard()                              {                                  copy_inventory_category(model, vicat, parent_id);                              } +                            if (cb) cb->fire(item_id);  						}  					}                      else @@ -3951,11 +4131,13 @@ void LLFolderBridge::perform_pasteFromClipboard()                                      // Stop pasting into the marketplace as soon as we get an error                                      break;                                  } +                                if (cb) cb->fire(item_id);                              }                              else if (item->getIsLinkType())                              { -                                link_inventory_object(parent_id, item_id, -                                    LLPointer<LLInventoryCallback>(NULL)); +                                link_inventory_object(parent_id, +                                                      item_id, +                                                      cb);                              }                              else                              { @@ -3965,7 +4147,7 @@ void LLFolderBridge::perform_pasteFromClipboard()                                                      item->getUUID(),                                                      parent_id,                                                      std::string(), -                                                    LLPointer<LLInventoryCallback>(NULL)); +                                                    cb);                              }                          }                      } @@ -3982,9 +4164,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); @@ -4001,6 +4183,14 @@ void LLFolderBridge::pasteLinkFromClipboard()  		std::vector<LLUUID> objects;  		LLClipboard::instance().pasteFromClipboard(objects); + +        LLPointer<LLInventoryCallback> cb = NULL; +        LLInventoryPanel* panel = mInventoryPanel.get(); +        if (panel->getRootFolder()->isSingleFolderMode()) +        { +            cb = new LLPasteIntoFolderCallback(mInventoryPanel); +        } +  		for (std::vector<LLUUID>::const_iterator iter = objects.begin();  			 iter != objects.end();  			 ++iter) @@ -4011,12 +4201,12 @@ void LLFolderBridge::pasteLinkFromClipboard()  				LLInventoryItem *item = model->getItem(object_id);  				if (item && can_move_to_outfit(item, move_is_into_current_outfit))  				{ -					dropToOutfit(item, move_is_into_current_outfit); +					dropToOutfit(item, move_is_into_current_outfit, cb);  				}  			}  			else if (LLConstPointer<LLInventoryObject> obj = model->getObject(object_id))  			{ -				link_inventory_object(parent_id, obj, LLPointer<LLInventoryCallback>(NULL)); +				link_inventory_object(parent_id, obj, cb);  			}  		}  		// Change mode to paste for next paste @@ -4054,8 +4244,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);  	if (outfits_id == mUUID)  	{ @@ -4077,12 +4267,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items  		}  		disabled_items.push_back(std::string("New Folder")); -		disabled_items.push_back(std::string("New Script")); -		disabled_items.push_back(std::string("New Note")); -		disabled_items.push_back(std::string("New Settings")); -		disabled_items.push_back(std::string("New Gesture")); -		disabled_items.push_back(std::string("New Clothes")); -		disabled_items.push_back(std::string("New Body Parts"));  		disabled_items.push_back(std::string("upload_def"));  	}  	if (favorites == mUUID) @@ -4105,11 +4289,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items      if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)      {          disabled_items.push_back(std::string("New Folder")); -		disabled_items.push_back(std::string("New Script")); -		disabled_items.push_back(std::string("New Note")); -		disabled_items.push_back(std::string("New Gesture")); -		disabled_items.push_back(std::string("New Clothes")); -		disabled_items.push_back(std::string("New Body Parts"));  		disabled_items.push_back(std::string("upload_def"));      }      if (marketplace_listings_id == mUUID) @@ -4119,14 +4298,14 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items          disabled_items.push_back(std::string("Cut"));          disabled_items.push_back(std::string("Delete"));      } + +	if (isPanelActive("Favorite Items")) +	{ +		disabled_items.push_back(std::string("Delete")); +	}  	if(trash_id == mUUID)  	{ -		bool is_recent_panel = false; -		LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); -		if (active_panel && (active_panel->getName() == "Recent Items")) -		{ -			is_recent_panel = true; -		} +		bool is_recent_panel = isPanelActive("Recent Items");  		// This is the trash.  		items.push_back(std::string("Empty Trash")); @@ -4145,6 +4324,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items  		{  			disabled_items.push_back(std::string("Empty Trash"));  		} + +        items.push_back(std::string("thumbnail"));  	}  	else if(isItemInTrash())  	{ @@ -4169,19 +4350,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items  				}                  if (!isMarketplaceListingsFolder())                  { -                    items.push_back(std::string("New Script")); -                    items.push_back(std::string("New Note")); -                    items.push_back(std::string("New Gesture")); -                    items.push_back(std::string("New Clothes")); -                    items.push_back(std::string("New Body Parts")); -                    items.push_back(std::string("New Settings"));                      items.push_back(std::string("upload_def")); - -                    if (!LLEnvironment::instance().isInventoryEnabled()) -                    { -                        disabled_items.push_back("New Settings"); -                    } -                  }  			}  			getClipboardEntries(false, items, disabled_items, flags); @@ -4192,6 +4361,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items  			if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT))  			{  				items.push_back(std::string("Rename")); +                items.push_back(std::string("thumbnail"));  				addDeleteContextMenuOptions(items, disabled_items);  				// EXT-4030: disallow deletion of currently worn outfit @@ -4206,6 +4376,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items  		if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID)  		{  			items.push_back(std::string("Copy outfit list to clipboard")); +            addOpenFolderMenuOptions(flags, items);  		}  		//Added by aura to force inventory pull on right-click to display folder options correctly. 07-17-06 @@ -4312,9 +4483,12 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags,   menuentry_vec_t&  	if(!category) return;  	const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); -	if (trash_id == mUUID) return; -	if (isItemInTrash()) return; -     +	if ((trash_id == mUUID) || isItemInTrash()) +    { +        addOpenFolderMenuOptions(flags, items); +        return; +    } +  	if (!isItemRemovable())  	{  		disabled_items.push_back(std::string("Delete")); @@ -4327,7 +4501,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags,   menuentry_vec_t&  	const bool is_agent_inventory = isAgentInventory();  	// Only enable calling-card related options for non-system folders. -	if (!is_system_folder && is_agent_inventory) +	if (!is_system_folder && is_agent_inventory && (mRoot != NULL))  	{  		LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);  		if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) @@ -4347,6 +4521,14 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags,   menuentry_vec_t&          disabled_items.push_back(std::string("New folder from selected"));      } +    //skip the rest options in single-folder mode +    if (mRoot == NULL) +    { +        return; +    } + +    addOpenFolderMenuOptions(flags, items); +  #ifndef LL_RELEASE_FOR_DOWNLOAD  	if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory)  	{ @@ -4372,26 +4554,29 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags,   menuentry_vec_t&  			if (type != LLFolderType::FT_OUTFIT)  			{  				items.push_back(std::string("Add To Outfit")); +                if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID)) +                { +                    disabled_items.push_back(std::string("Add To Outfit")); +                }  			}  			items.push_back(std::string("Replace Outfit")); +            if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) +            { +                disabled_items.push_back(std::string("Replace Outfit")); +            }  		}  		if (is_agent_inventory)  		{  			items.push_back(std::string("Folder Wearables Separator")); +            // Note: If user tries to unwear "My Inventory", it's going to deactivate everything including gestures +            // Might be safer to disable this for "My Inventory"  			items.push_back(std::string("Remove From Outfit")); -			if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) -			{ -					disabled_items.push_back(std::string("Remove From Outfit")); -			} -		} -		if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) -		{ -			disabled_items.push_back(std::string("Replace Outfit")); -		} -		if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID)) -		{ -			disabled_items.push_back(std::string("Add To Outfit")); +            if (type != LLFolderType::FT_ROOT_INVENTORY // Unless COF is empty, whih shouldn't be, warrantied to have worn items +                && !LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) // expensive from root! +            { +                disabled_items.push_back(std::string("Remove From Outfit")); +            }  		}  		items.push_back(std::string("Outfit Separator")); @@ -4424,6 +4609,20 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  	menu.arrangeAndClear();  } +void LLFolderBridge::addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items) +{ +    if ((flags & ITEM_IN_MULTI_SELECTION) == 0) +    { +        items.push_back(std::string("open_in_new_window")); +        items.push_back(std::string("Open Folder Separator")); +        items.push_back(std::string("Copy Separator")); +        if(isPanelActive("comb_single_folder_inv")) +        { +            items.push_back(std::string("open_in_current_window")); +        } +    } +} +  bool LLFolderBridge::hasChildren() const  {  	LLInventoryModel* model = getInventoryModel(); @@ -4440,6 +4639,18 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,  {  	LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; +    static LLPointer<LLInventoryCallback> drop_cb = NULL; +    LLInventoryPanel* panel = mInventoryPanel.get(); +    LLToolDragAndDrop* drop_tool = LLToolDragAndDrop::getInstance(); +    if (drop +        && panel->getRootFolder()->isSingleFolderMode() +        && panel->getRootFolderID() == mUUID +        && drop_tool->getCargoIndex() == 0) +    { +        drop_cb = new LLPasteIntoFolderCallback(mInventoryPanel); +    } + +  	//LL_INFOS() << "LLFolderBridge::dragOrDrop()" << LL_ENDL;  	BOOL accept = FALSE;  	switch(cargo_type) @@ -4457,7 +4668,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,  		case DAD_GESTURE:  		case DAD_MESH:          case DAD_SETTINGS: -			accept = dragItemIntoFolder(inv_item, drop, tooltip_msg); +			accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, TRUE, drop_cb);  			break;  		case DAD_LINK:  			// DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. @@ -4468,12 +4679,12 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,  				LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID());  				if (linked_category)  				{ -					accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE); +					accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE, TRUE, drop_cb);  				}  			}  			else  			{ -				accept = dragItemIntoFolder(inv_item, drop, tooltip_msg); +				accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, TRUE, drop_cb);  			}  			break;  		case DAD_CATEGORY: @@ -4483,7 +4694,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,  			}  			else  			{ -				accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg); +				accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg, FALSE, TRUE, drop_cb);  			}  			break;  		case DAD_ROOT_CATEGORY: @@ -4493,6 +4704,11 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,  			LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL;  			break;  	} + +    if (!drop || drop_tool->getCargoIndex() + 1 == drop_tool->getCargoCount()) +    { +        drop_cb = NULL; +    }  	return accept;  } @@ -4793,138 +5009,56 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response  	if (move_inv->mCallback)  	{ -		move_inv->mCallback(option, move_inv->mUserData); +		move_inv->mCallback(option, move_inv->mUserData, move_inv.get());  	}  	move_inv.reset(); //since notification will persist  	return false;  } -// Returns true if the item can be moved to Current Outfit or any outfit folder. -static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) -{ -	LLInventoryType::EType inv_type = inv_item->getInventoryType(); -	if ((inv_type != LLInventoryType::IT_WEARABLE) && -		(inv_type != LLInventoryType::IT_GESTURE) && -		(inv_type != LLInventoryType::IT_ATTACHMENT) && -		(inv_type != LLInventoryType::IT_OBJECT) && -		(inv_type != LLInventoryType::IT_SNAPSHOT) && -		(inv_type != LLInventoryType::IT_TEXTURE)) -	{ -		return FALSE; -	} - -	U32 flags = inv_item->getFlags(); -	if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS) -	{ -		return FALSE; -	} - -	if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT)) -	{ -		return !move_is_into_current_outfit; -	} - -	if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) -	{ -		return FALSE; -	} - -	return TRUE; -} - -// Returns true if folder's content can be moved to Current Outfit or any outfit folder. -static bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit) -{ -    LLInventoryModel::cat_array_t *cats; -    LLInventoryModel::item_array_t *items; -    model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items); - -    if (items->size() > wear_limit) -    { -        return false; -    } - -    if (items->size() == 0) -    { -        // Nothing to move(create) -        return false; -    } - -    if (cats->size() > 0) -    { -        // We do not allow subfolders in outfits of "My Outfits" yet -        return false; -    } - -    LLInventoryModel::item_array_t::iterator iter = items->begin(); -    LLInventoryModel::item_array_t::iterator end = items->end(); - -    while (iter != end) -    { -        LLViewerInventoryItem *item = *iter; -        if (!can_move_to_outfit(item, false)) -        { -            return false; -        } -        iter++; -    } - -    return true; -} - -// Returns TRUE if item is a landmark or a link to a landmark -// and can be moved to Favorites or Landmarks folder. -static BOOL can_move_to_landmarks(LLInventoryItem* inv_item) +void drop_to_favorites_cb(const LLUUID& id, LLPointer<LLInventoryCallback> cb1, LLPointer<LLInventoryCallback> cb2)  { -	// Need to get the linked item to know its type because LLInventoryItem::getType() -	// returns actual type AT_LINK for links, not the asset type of a linked item. -	if (LLAssetType::AT_LINK == inv_item->getType()) -	{ -		LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); -		if (linked_item) -		{ -			return LLAssetType::AT_LANDMARK == linked_item->getType(); -		} -	} - -	return LLAssetType::AT_LANDMARK == inv_item->getType(); +    cb1->fire(id); +    cb2->fire(id);  } -void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) +void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb)  {  	// use callback to rearrange favorite landmarks after adding  	// to have new one placed before target (on which it was dropped). See EXT-4312. -	LLPointer<AddFavoriteLandmarkCallback> cb = new AddFavoriteLandmarkCallback(); +	LLPointer<AddFavoriteLandmarkCallback> cb_fav = new AddFavoriteLandmarkCallback();  	LLInventoryPanel* panel = mInventoryPanel.get();  	LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL;  	LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast<LLFolderViewModelItemInventory*>(drag_over_item->getViewModelItem()) : NULL;  	if (view_model)  	{ -		cb.get()->setTargetLandmarkId(view_model->getUUID()); +		cb_fav.get()->setTargetLandmarkId(view_model->getUUID());  	} +    LLPointer <LLInventoryCallback> callback = cb_fav; +    if (cb) +    { +        callback = new LLBoostFuncInventoryCallback(boost::bind(drop_to_favorites_cb, _1, cb, cb_fav)); +    } +  	copy_inventory_item(  		gAgent.getID(),  		inv_item->getPermissions().getOwner(),  		inv_item->getUUID(),  		mUUID,  		std::string(), -		cb); +        callback);  } -void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb)  {  	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<LLFloaterOutfitPhotoPreview>("outfit_photo_preview", inv_item->getUUID()); -			if(photo_preview) -			{ -				photo_preview->setOutfitID(mUUID); -			} +            // Legacy: prior to thumbnails images in outfits were used for outfit gallery. +            LLNotificationsUtil::add("ThumbnailOutfitPhoto");  		}  		return;  	} @@ -4941,21 +5075,22 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c  	}  } -void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat) +void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb)  {      // make a folder in the My Outfits directory.      const LLUUID dest_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);      // Note: creation will take time, so passing folder id to callback is slightly unreliable,      // but so is collecting and passing descendants' ids -    inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1); +    inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb);      gInventory.createNewCategory(dest_id,                                   LLFolderType::FT_OUTFIT,                                   inv_cat->getName(), -                                 func); +                                 func, +                                 inv_cat->getThumbnailUUID());  } -void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id) +void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb)  {      LLInventoryModel::cat_array_t* categories;      LLInventoryModel::item_array_t* items; @@ -4986,7 +5121,6 @@ void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID ca      if (!link_array.empty())      { -        LLPointer<LLInventoryCallback> cb = NULL;          link_inventory_array(cat_dest_id, link_array, cb);      }  } @@ -5019,7 +5153,8 @@ void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, c  BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,  										BOOL drop,  										std::string& tooltip_msg, -                                        BOOL user_confirm) +                                        BOOL user_confirm, +                                        LLPointer<LLInventoryCallback> cb)  {  	LLInventoryModel* model = getInventoryModel(); @@ -5033,11 +5168,11 @@ 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 &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 ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); +	const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); +	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); @@ -5057,7 +5192,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()); @@ -5199,26 +5334,27 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,  			// (copy the item)  			else if (move_is_into_favorites)  			{ -				dropToFavorites(inv_item); +				dropToFavorites(inv_item, cb);  			}  			// CURRENT OUTFIT or OUTFIT folder  			// (link the item)  			else if (move_is_into_current_outfit || move_is_into_outfit)  			{ -				dropToOutfit(inv_item, move_is_into_current_outfit); +				dropToOutfit(inv_item, move_is_into_current_outfit, cb);  			}              // MARKETPLACE LISTINGS folder              // Move the item              else if (move_is_into_marketplacelistings)              {                  move_item_to_marketplacelistings(inv_item, mUUID); +                if (cb) cb->fire(inv_item->getUUID());              }  			// NORMAL or TRASH folder  			// (move the item, restamp if into trash)  			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());  				} @@ -5228,6 +5364,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,  					(LLViewerInventoryItem*)inv_item,  					mUUID,  					move_is_into_trash); +                if (cb) cb->fire(inv_item->getUUID());  			}              if (move_is_from_marketplacelistings) @@ -5236,11 +5373,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); +                        } +                    });                  }              } @@ -5308,11 +5449,16 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,  		if (accept && drop)  		{ +            LLUUID item_id = inv_item->getUUID();              boost::shared_ptr<LLMoveInv> move_inv (new LLMoveInv());  			move_inv->mObjectID = inv_item->getParentUUID(); -			two_uuids_t item_pair(mUUID, inv_item->getUUID()); +			two_uuids_t item_pair(mUUID, item_id);  			move_inv->mMoveList.push_back(item_pair); -			move_inv->mCallback = NULL; +            if (cb) +            { +                move_inv->mCallback = [item_id, cb](S32, void*, const LLMoveInv* move_inv) mutable +                    { cb->fire(item_id); }; +            }  			move_inv->mUserData = NULL;  			if(is_move)  			{ @@ -5404,13 +5550,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,  				// (copy the item)  				if (move_is_into_favorites)  				{ -					dropToFavorites(inv_item); +					dropToFavorites(inv_item, cb);  				}  				// CURRENT OUTFIT or OUTFIT folder  				// (link the item)  				else if (move_is_into_current_outfit || move_is_into_outfit)  				{ -					dropToOutfit(inv_item, move_is_into_current_outfit); +					dropToOutfit(inv_item, move_is_into_current_outfit, cb);  				}  				else  				{ @@ -5420,7 +5566,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,  						inv_item->getUUID(),  						mUUID,  						std::string(), -						LLPointer<LLInventoryCallback>(NULL)); +						cb);  				}  			}  		} @@ -5975,7 +6121,7 @@ std::string LLCallingCardBridge::getLabelSuffix() const  	LLViewerInventoryItem* item = getItem();  	if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )  	{ -		return LLItemBridge::getLabelSuffix() + " (online)"; +		return LLItemBridge::getLabelSuffix() + "  online";  	}  	else  	{ @@ -7558,16 +7704,26 @@ class LLObjectBridgeAction: public LLInvFVBridgeAction  public:  	virtual void doIt()  	{ -		/* -		  LLFloaterReg::showInstance("properties", mUUID); -		*/ -		LLInvFVBridgeAction::doIt(); +        attachOrDetach();  	}  	virtual ~LLObjectBridgeAction(){}  protected:  	LLObjectBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} +    void attachOrDetach();  }; +void LLObjectBridgeAction::attachOrDetach() +{ +    if (get_is_item_worn(mUUID)) +    { +        LLAppearanceMgr::instance().removeItemFromAvatar(mUUID); +    } +    else +    { +        LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding. +    } +} +  class LLLSLTextBridgeAction: public LLInvFVBridgeAction  {  	friend class LLInvFVBridgeAction; @@ -7626,7 +7782,17 @@ void LLWearableBridgeAction::wearOnAvatar()  	LLViewerInventoryItem* item = getItem();  	if(item)  	{ -		LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); +        if (get_is_item_worn(mUUID)) +        { +            if(item->getType() != LLAssetType::AT_BODYPART) +            { +                LLAppearanceMgr::instance().removeItemFromAvatar(item->getUUID()); +            } +        } +        else +        { +            LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); +        }  	}  } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index bdffecf1c6..decaee7db3 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -28,7 +28,6 @@  #define LL_LLINVENTORYBRIDGE_H  #include "llcallingcard.h" -#include "llfloaterproperties.h"  #include "llfolderviewmodel.h"  #include "llinventorymodel.h"  #include "llinventoryobserver.h" @@ -47,9 +46,11 @@ class LLMenuGL;  class LLCallingCardObserver;  class LLViewerJointAttachment;  class LLFolderView; +struct LLMoveInv;  typedef std::vector<std::string> menuentry_vec_t; - +typedef std::pair<LLUUID, LLUUID> two_uuids_t; +typedef std::list<two_uuids_t> two_uuids_list_t;  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLInvFVBridge  // @@ -84,6 +85,7 @@ public:  	// LLInvFVBridge functionality  	//--------------------------------------------------------------------  	virtual const LLUUID& getUUID() const { return mUUID; } +    virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null; }  	virtual void clearDisplayName() { mDisplayName.clear(); }  	virtual void restoreItem() {}  	virtual void restoreToWorld() {} @@ -107,6 +109,7 @@ public:  	virtual std::string getLabelSuffix() const { return LLStringUtil::null; }  	virtual void openItem() {}  	virtual void closeItem() {} +    virtual void navigateToFolder(bool new_window = false, bool change_mode = false);  	virtual void showProperties();  	virtual BOOL isItemRenameable() const { return TRUE; }  	virtual BOOL isMultiPreviewAllowed() { return TRUE; } @@ -114,6 +117,7 @@ public:  	virtual BOOL isItemRemovable() const;  	virtual BOOL isItemMovable() const;  	virtual BOOL isItemInTrash() const; +    virtual bool isItemInOutfits() const;  	virtual BOOL isLink() const;  	virtual BOOL isLibraryItem() const;  	//virtual BOOL removeItem() = 0; @@ -251,6 +255,7 @@ public:  	virtual LLUIImagePtr getIconOverlay() const;  	LLViewerInventoryItem* getItem() const; +    virtual const LLUUID& getThumbnailUUID() const;  protected:  	BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); @@ -275,8 +280,8 @@ public:  		mShowDescendantsCount(false)  	{} -	BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE); -	BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE); +	BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE, LLPointer<LLInventoryCallback> cb = NULL); +	BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE, LLPointer<LLInventoryCallback> cb = NULL);      void callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item);      void callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category); @@ -296,6 +301,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;} @@ -335,6 +341,7 @@ public:  protected:  	void buildContextMenuOptions(U32 flags, menuentry_vec_t& items,   menuentry_vec_t& disabled_items);  	void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items,   menuentry_vec_t& disabled_items); +    void addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items);  	//--------------------------------------------------------------------  	// Menu callbacks @@ -360,9 +367,9 @@ protected:  	void copyOutfitToClipboard();  	void determineFolderType(); -	void dropToFavorites(LLInventoryItem* inv_item); -	void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); -	void dropToMyOutfits(LLInventoryCategory* inv_cat); +	void dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb = NULL); +	void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb = NULL); +	void dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb = NULL);  	//--------------------------------------------------------------------  	// Messy hacks for handling folder options @@ -372,7 +379,7 @@ public:  	static void staticFolderOptionsMenu();  protected: -    void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id); +    void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer<LLInventoryCallback> cb);      void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response);      void perform_pasteFromClipboard();      void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level); @@ -741,7 +748,7 @@ void rez_attachment(LLViewerInventoryItem* item,  BOOL move_inv_category_world_to_agent(const LLUUID& object_id,   									  const LLUUID& category_id,  									  BOOL drop, -									  void (*callback)(S32, void*) = NULL, +									  std::function<void(S32, void*, const LLMoveInv *)> callback = NULL,  									  void* user_data = NULL,  									  LLInventoryFilter* filter = NULL); @@ -767,4 +774,16 @@ public:      bool canWearSelected(const uuid_vec_t& item_ids) const;  }; +struct LLMoveInv +{ +    LLUUID mObjectID; +    LLUUID mCategoryID; +    two_uuids_list_t mMoveList; +    std::function<void(S32, void*, const LLMoveInv*)> mCallback; +    void* mUserData; +}; + +void warn_move_inventory(LLViewerObject* object, boost::shared_ptr<LLMoveInv> move_inv); +bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, boost::shared_ptr<LLMoveInv>); +  #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index e3a6b2dc85..5cf6c3fb7d 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -63,6 +63,7 @@ LLInventoryFilter::FilterOps::FilterOps(const Params& p)  	mFilterTypes(p.types),  	mFilterUUID(p.uuid),  	mFilterLinks(p.links), +    mFilterThumbnails(p.thumbnails),  	mSearchVisibility(p.search_visibility)  {  } @@ -81,7 +82,8 @@ LLInventoryFilter::LLInventoryFilter(const Params& p)  	mCurrentGeneration(0),  	mFirstRequiredGeneration(0),  	mFirstSuccessGeneration(0), -	mSearchType(SEARCHTYPE_NAME) +	mSearchType(SEARCHTYPE_NAME), +    mSingleFolderMode(false)  {  	// copy mFilterOps into mDefaultFilterOps  	markDefault(); @@ -157,6 +159,8 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item)  	passed = passed && checkAgainstCreator(listener);  	passed = passed && checkAgainstSearchVisibility(listener); +    passed = passed && checkAgainstFilterThumbnails(listener->getUUID()); +  	return passed;  } @@ -194,17 +198,23 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const  	// when applying a filter, matching folders get their contents downloaded first  	// but make sure we are not interfering with pre-download  	if (isNotDefault() -		&& LLStartUp::getStartupState() > STATE_WEARABLES_WAIT) +		&& LLStartUp::getStartupState() > STATE_WEARABLES_WAIT +        && !LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress())      {          LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); -        if (!cat || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)) +        if ((!cat && folder_id.notNull()) || (cat && cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))          {              // 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);          }  	} +    if (!checkAgainstFilterThumbnails(folder_id)) +    { +        return false; +    } +  	// Marketplace folder filtering      const U32 filterTypes = mFilterOps.mFilterTypes;      const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE | @@ -565,6 +575,19 @@ bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewModelItemInven  	return TRUE;  } +bool LLInventoryFilter::checkAgainstFilterThumbnails(const LLUUID& object_id) const +{ +    const LLInventoryObject *object = gInventory.getObject(object_id); +    if (!object) return true; + +    const bool is_thumbnail = object->getThumbnailUUID().notNull(); +    if (is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS)) +        return false; +    if (!is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS)) +        return false; +    return true; +} +  bool LLInventoryFilter::checkAgainstCreator(const LLFolderViewModelItemInventory* listener) const  {  	if (!listener) return TRUE; @@ -595,6 +618,9 @@ bool LLInventoryFilter::checkAgainstSearchVisibility(const LLFolderViewModelItem  	if (is_link && ((mFilterOps.mSearchVisibility & VISIBILITY_LINKS) == 0))  		return FALSE; +    if (listener->isItemInOutfits() && ((mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS) == 0)) +        return FALSE; +  	if (listener->isItemInTrash() && ((mFilterOps.mSearchVisibility & VISIBILITY_TRASH) == 0))  		return FALSE; @@ -733,6 +759,32 @@ void LLInventoryFilter::setFilterSettingsTypes(U64 types)      mFilterOps.mFilterTypes |= FILTERTYPE_SETTINGS;  } +void LLInventoryFilter::setFilterThumbnails(U64 filter_thumbnails) +{ +    if (mFilterOps.mFilterThumbnails != filter_thumbnails) +    { +        if (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS +            && filter_thumbnails == FILTER_ONLY_THUMBNAILS) +        { +            setModified(FILTER_RESTART); +        } +        else if (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS +            && filter_thumbnails == FILTER_EXCLUDE_THUMBNAILS) +        { +            setModified(FILTER_RESTART); +        } +        else if (mFilterOps.mFilterThumbnails == FILTER_INCLUDE_THUMBNAILS) +        { +            setModified(FILTER_MORE_RESTRICTIVE); +        } +        else +        { +            setModified(FILTER_LESS_RESTRICTIVE); +        } +    } +    mFilterOps.mFilterThumbnails = filter_thumbnails; +} +  void LLInventoryFilter::setFilterEmptySystemFolders()  {  	mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS; @@ -791,6 +843,24 @@ void LLInventoryFilter::toggleSearchVisibilityLinks()  	}  } +void LLInventoryFilter::toggleSearchVisibilityOutfits() +{ +    bool hide_outfits = mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS; +    if (hide_outfits) +    { +        mFilterOps.mSearchVisibility &= ~VISIBILITY_OUTFITS; +    } +    else +    { +        mFilterOps.mSearchVisibility |= VISIBILITY_OUTFITS; +    } + +    if (hasFilterString()) +    { +        setModified(hide_outfits ? FILTER_MORE_RESTRICTIVE : FILTER_LESS_RESTRICTIVE); +    } +} +  void LLInventoryFilter::toggleSearchVisibilityTrash()  {  	bool hide_trash = mFilterOps.mSearchVisibility & VISIBILITY_TRASH; @@ -1503,6 +1573,11 @@ U64 LLInventoryFilter::getSearchVisibilityTypes() const  	return mFilterOps.mSearchVisibility;  } +U64 LLInventoryFilter::getFilterThumbnails() const +{ +    return mFilterOps.mFilterThumbnails; +} +  bool LLInventoryFilter::hasFilterString() const  {  	return mFilterSubString.size() > 0; @@ -1580,9 +1655,9 @@ void LLInventoryFilter::setDefaultEmptyLookupMessage(const std::string& message)  	mDefaultEmptyLookupMessage = message;  } -std::string LLInventoryFilter::getEmptyLookupMessage() const +std::string LLInventoryFilter::getEmptyLookupMessage(bool is_empty_folder) const  { -	if (isDefault() && !mDefaultEmptyLookupMessage.empty()) +	if ((isDefault() || is_empty_folder) && !mDefaultEmptyLookupMessage.empty())  	{  		return LLTrans::getString(mDefaultEmptyLookupMessage);  	} @@ -1605,7 +1680,7 @@ bool LLInventoryFilter::areDateLimitsSet()  bool LLInventoryFilter::showAllResults() const  { -	return hasFilterString(); +	return hasFilterString() && !mSingleFolderMode;  } diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 384de3e889..ada1d0f4b4 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -75,6 +75,13 @@ public:  		FILTERLINK_ONLY_LINKS		// only show links  	}; +    enum EFilterThumbnail +    { +        FILTER_INCLUDE_THUMBNAILS, +        FILTER_EXCLUDE_THUMBNAILS, +        FILTER_ONLY_THUMBNAILS +    }; +  	enum ESortOrderType  	{  		SO_NAME = 0,						// Sort inventory by name @@ -104,7 +111,8 @@ public:  		VISIBILITY_NONE = 0,  		VISIBILITY_TRASH = 0x1 << 0,  		VISIBILITY_LIBRARY = 0x1 << 1, -		VISIBILITY_LINKS	= 0x1 << 2 +		VISIBILITY_LINKS	= 0x1 << 2, +        VISIBILITY_OUTFITS    = 0x1 << 3  	};  	struct FilterOps @@ -139,12 +147,14 @@ public:  			Optional<EFolderShow>		show_folder_state;  			Optional<PermissionMask>	permissions;  			Optional<EFilterCreatorType> creator_type; +            Optional<EFilterThumbnail> thumbnails;  			Params()  			:	types("filter_types", FILTERTYPE_OBJECT),  				object_types("object_types", 0xffffFFFFffffFFFFULL),  				wearable_types("wearable_types", 0xffffFFFFffffFFFFULL),                  settings_types("settings_types", 0xffffFFFFffffFFFFULL), +                thumbnails("thumbnails", FILTER_INCLUDE_THUMBNAILS),  				category_types("category_types", 0xffffFFFFffffFFFFULL),  				links("links", FILTERLINK_INCLUDE_LINKS),  				search_visibility("search_visibility", 0xFFFFFFFF), @@ -165,6 +175,7 @@ public:  		U64				mFilterObjectTypes,   // For _OBJECT  						mFilterWearableTypes,                          mFilterSettingsTypes, // for _SETTINGS +                        mFilterThumbnails,  						mFilterLinks,  						mFilterCategoryTypes; // For _CATEGORY  		LLUUID      	mFilterUUID; 		  // for UUID @@ -207,6 +218,7 @@ public:  	U64					getFilterWearableTypes() const;  	U64					getFilterSettingsTypes() const;  	U64					getSearchVisibilityTypes() const; +    U64                 getFilterThumbnails() const;  	bool 				isFilterObjectTypesWith(LLInventoryType::EType t) const;  	void 				setFilterObjectTypes(U64 types); @@ -221,6 +233,7 @@ public:  	void				setFilterMarketplaceUnassociatedFolders();      void                setFilterMarketplaceListingFolders(bool select_only_listing_folders);      void                setFilterNoMarketplaceFolder(); +    void                setFilterThumbnails(U64 filter_thumbnails);  	void				updateFilterTypes(U64 types, U64& current_types);  	void 				setSearchType(ESearchType type);  	ESearchType			getSearchType() { return mSearchType; } @@ -228,6 +241,7 @@ public:  	void				toggleSearchVisibilityLinks();  	void				toggleSearchVisibilityTrash(); +    void                toggleSearchVisibilityOutfits();  	void				toggleSearchVisibilityLibrary();  	void 				setSearchVisibilityTypes(U32 types);  	void 				setSearchVisibilityTypes(const Params& params); @@ -237,6 +251,8 @@ public:  	const std::string& 	getFilterSubStringOrig() const { return mFilterSubStringOrig; }   	bool 				hasFilterString() const; +    void                setSingleFolderMode(bool is_single_folder) { mSingleFolderMode = is_single_folder; } +  	void 				setFilterPermissions(PermissionMask perms);  	PermissionMask 		getFilterPermissions() const; @@ -277,7 +293,7 @@ public:  	void 				setEmptyLookupMessage(const std::string& message);  	void				setDefaultEmptyLookupMessage(const std::string& message); -	std::string			getEmptyLookupMessage() const; +	std::string			getEmptyLookupMessage(bool is_empty_folder = false) const;  	// +-------------------------------------------------------------------+  	// + Status @@ -321,6 +337,8 @@ public:  	LLInventoryFilter& operator =(const LLInventoryFilter& other); +    bool checkAgainstFilterThumbnails(const LLUUID& object_id) const; +  private:  	bool				areDateLimitsSet();  	bool 				checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const; @@ -359,6 +377,8 @@ private:  	std::vector<std::string> mFilterTokens;  	std::string				 mExactToken; + +    bool mSingleFolderMode;  };  #endif diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 145814ab41..4aeacae6ed 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -46,13 +46,16 @@  #include "llappearancemgr.h"  #include "llappviewer.h"  #include "llavataractions.h" +#include "llavatarnamecache.h"  #include "llclipboard.h"  #include "lldirpicker.h"  #include "lldonotdisturbnotificationstorage.h" +#include "llfloatermarketplacelistings.h"  #include "llfloatersidepanelcontainer.h"  #include "llfocusmgr.h"  #include "llfolderview.h"  #include "llgesturemgr.h" +#include "llgiveinventory.h"  #include "lliconctrl.h"  #include "llimview.h"  #include "llinventorybridge.h" @@ -93,6 +96,7 @@  BOOL LLInventoryState::sWearNewClothing = FALSE;  LLUUID LLInventoryState::sWearNewClothingTransactionID;  std::list<LLUUID> LLInventoryAction::sMarketplaceFolders; +bool LLInventoryAction::sDeleteConfirmationDisplayed = false;  // Helper function : callback to update a folder after inventory action happened in the background  void update_folder_cb(const LLUUID& dest_folder) @@ -400,7 +404,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); @@ -426,14 +430,36 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s  }  void copy_inventory_category(LLInventoryModel* model, -							 LLViewerInventoryCategory* cat, -							 const LLUUID& parent_id, -							 const LLUUID& root_copy_id, -							 bool move_no_copy_items ) +                             LLViewerInventoryCategory* cat, +                             const LLUUID& parent_id, +                             const LLUUID& root_copy_id, +                             bool move_no_copy_items) +{ +    // Create the initial folder +    inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items](const LLUUID& new_id) +    { +        copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); +    }; +    gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); +} + +void copy_inventory_category(LLInventoryModel* model, +                             LLViewerInventoryCategory* cat, +                             const LLUUID& parent_id, +                             const LLUUID& root_copy_id, +                             bool move_no_copy_items, +                             inventory_func_type callback)  {  	// Create the initial folder -	inventory_func_type func = boost::bind(©_inventory_category_content, _1, model, cat, root_copy_id, move_no_copy_items); -	gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func); +    inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items, callback](const LLUUID &new_id) +    { +        copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); +        if (callback) +        { +            callback(new_id); +        } +    }; +	gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID());  }  void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items) @@ -558,11 +584,12 @@ BOOL get_is_item_worn(const LLUUID& id)  	const LLViewerInventoryItem* item = gInventory.getItem(id);  	if (!item)  		return FALSE; -     +      if (item->getIsLinkType() && !gInventory.getItem(item->getLinkedUUID()))      {          return FALSE;      } +  	// Consider the item as worn if it has links in COF.  	if (LLAppearanceMgr::instance().isLinkedInCOF(id))  	{ @@ -797,18 +824,37 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id)  void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id)  { -	LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", item_uuid).with("object", object_id)); +    LLSD params; +    params["id"] = item_uuid; +    params["object"] = object_id; +     +    LLFloaterReg::showInstance("item_properties", params);  }  void show_item_profile(const LLUUID& item_uuid)  {  	LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); -	LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", linked_uuid)); +    LLFloaterReg::showInstance("item_properties", LLSD().with("id", linked_uuid));  }  void show_item_original(const LLUUID& item_uuid)  { -    LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); +    static LLUICachedControl<bool> find_original_new_floater("FindOriginalOpenWindow", false); + +    //show in a new single-folder window +    if(find_original_new_floater) +    { +        const LLUUID& linked_item_uuid = gInventory.getLinkedItemID(item_uuid); +        const LLInventoryObject *obj = gInventory.getObject(linked_item_uuid); +        if (obj && obj->getParentUUID().notNull()) +        { +            LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), linked_item_uuid); +        } +    } +    //show in main Inventory +    else +    { +        LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory");      if (!floater_inventory)      {          LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; @@ -820,6 +866,10 @@ void show_item_original(const LLUUID& item_uuid)          LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel();          if (main_inventory)          { +            if(main_inventory->isSingleFolderMode()) +            { +                main_inventory->toggleViewMode(); +            }              main_inventory->resetAllItemsFilters();          }          reset_inventory_filter(); @@ -828,7 +878,6 @@ void show_item_original(const LLUUID& item_uuid)          {              LLFloaterReg::toggleInstanceOrBringToFront("inventory");          } -        sidepanel_inventory->showInventoryPanel();          const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX);          if (gInventory.isObjectDescendentOf(gInventory.getLinkedItemID(item_uuid), inbox_id)) @@ -848,6 +897,7 @@ void show_item_original(const LLUUID& item_uuid)              }          }      } +    }  } @@ -869,22 +919,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  // @@ -899,7 +933,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; @@ -1371,6 +1405,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 @@ -1410,55 +1445,119 @@ 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<void(const LLUUID&)> callback_create_stock = [copy, item_id](const LLUUID& new_cat_id) +            { +                if (new_cat_id.isNull()) +                { +                    LL_WARNS() << "Failed to create category" << LL_ENDL; +                    LLSD subs; +                    subs["[ERROR_CODE]"] = +                        LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); +                    LLNotificationsUtil::add("MerchantPasteFailed", subs); +                    return; +                } + +                // 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 || !viewer_inv_item) +                { +                    LL_WARNS() << "Move to marketplace: item or folder do not exist" << LL_ENDL; + +                    LLSD subs; +                    subs["[ERROR_CODE]"] = +                        LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); +                    LLNotificationsUtil::add("MerchantPasteFailed", subs); +                    return; +                } +                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<LLInventoryCallback> 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<void(const LLUUID&)> callback_dest_create = [item_id, callback_create_stock](const LLUUID& new_cat_id) +            { +                if (new_cat_id.isNull()) +                { +                    LL_WARNS() << "Failed to create category" << LL_ENDL; +                    LLSD subs; +                    subs["[ERROR_CODE]"] = +                        LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); +                    LLNotificationsUtil::add("MerchantPasteFailed", subs); +                    return; +                } + +                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](const LLUUID &new_cat_id) +                                            { +                                                if (new_cat_id.isNull()) +                                                { +                                                    LL_WARNS() << "Failed to create listing folder for marketpace" << LL_ENDL; +                                                    return; +                                                } +                                                LLViewerInventoryCategory *dest_cat = gInventory.getCategory(new_cat_id); +                                                if (!dest_cat) +                                                { +                                                    LL_WARNS() << "Failed to find freshly created listing folder" << LL_ENDL; +                                                    return; +                                                } +                                                // version folder +                                                gInventory.createNewCategory(new_cat_id, +                                                                             LLFolderType::FT_NONE, +                                                                             dest_cat->getName(), +                                                                             callback_dest_create); +                                            });              } -            if (depth == 1) +            else 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 -                LL_INFOS("SLM") << "Copy item '" << make_info(viewer_inv_item) << "' to '" << make_inventory_path(dest_folder) << "'" << LL_ENDL; -                LLPointer<LLInventoryCallback> 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); +                gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create);              }              else              { -                LL_INFOS("SLM") << "Move item '" << make_info(viewer_inv_item) << "' to '" << make_inventory_path(dest_folder) << "'" << LL_ENDL; -                // Reparent the item -                gInventory.changeItemParent(viewer_inv_item, dest_folder, true); +                callback_dest_create(dest_folder);              }          }          else @@ -1507,7 +1606,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 @@ -1532,32 +1631,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<void(S32 pending_callbacks, bool result)> 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(); @@ -1589,10 +1679,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);              }          }      } @@ -1602,26 +1692,46 @@ 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](const LLUUID &new_cat_id) +            { +                if (new_cat_id.isNull()) +                { +                    cb_result(0, false); +                    return; +                } +                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; +                validate_marketplacelistings(new_cat, cb_result, cb_msg, fix_hierarchy, depth + 1, true, 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);              }          }      } @@ -1652,10 +1762,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;          } @@ -1686,35 +1796,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);              }          }      } @@ -1722,10 +1832,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 @@ -1747,11 +1857,12 @@ 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()); +                    const LLUUID origin_uuid = 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) @@ -1762,30 +1873,71 @@ 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<LLUUID> 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, origin_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<LLUUID>::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(); + +                        if (origin_uuid != parent_uuid) +                        { +                            // We might have moved last item from a folder, check if it needs to be removed +                            LLViewerInventoryCategory* cat = gInventory.getCategory(origin_uuid); +                            if (cat->getDescendentCount() == 0) +                            { +                                // Remove previous folder if it ends up empty +                                if (cb_msg) +                                { +                                    std::string indent; +                                    for (int i = 1; i < depth; i++) +                                    { +                                        indent += "    "; +                                    } +                                    std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); +                                    cb_msg(message, depth, LLError::LEVEL_WARN); +                                } +                                gInventory.removeCategory(cat->getUUID()); +                                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++;                  }              } @@ -1799,11 +1951,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 @@ -1814,20 +1966,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 @@ -1842,21 +1994,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);                  }              }          } @@ -1865,17 +2017,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;          }      } @@ -1888,15 +2041,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) @@ -1996,9 +2149,346 @@ 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); +} + +// Returns true if the item can be moved to Current Outfit or any outfit folder. +bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +{ +    LLInventoryType::EType inv_type = inv_item->getInventoryType(); +    if ((inv_type != LLInventoryType::IT_WEARABLE) && +        (inv_type != LLInventoryType::IT_GESTURE) && +        (inv_type != LLInventoryType::IT_ATTACHMENT) && +        (inv_type != LLInventoryType::IT_OBJECT) && +        (inv_type != LLInventoryType::IT_SNAPSHOT) && +        (inv_type != LLInventoryType::IT_TEXTURE)) +    { +        return false; +    } + +    U32 flags = inv_item->getFlags(); +    if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS) +    { +        return false; +    } + +    if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT)) +    { +        return !move_is_into_current_outfit; +    } + +    if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) +    { +        return false; +    } + +    return true; +} + +// Returns TRUE if item is a landmark or a link to a landmark +// and can be moved to Favorites or Landmarks folder. +bool can_move_to_landmarks(LLInventoryItem* inv_item) +{ +    // Need to get the linked item to know its type because LLInventoryItem::getType() +    // returns actual type AT_LINK for links, not the asset type of a linked item. +    if (LLAssetType::AT_LINK == inv_item->getType()) +    { +        LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); +        if (linked_item) +        { +            return LLAssetType::AT_LANDMARK == linked_item->getType(); +        } +    } + +    return LLAssetType::AT_LANDMARK == inv_item->getType(); +} + +// Returns true if folder's content can be moved to Current Outfit or any outfit folder. +bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit) +{ +    LLInventoryModel::cat_array_t *cats; +    LLInventoryModel::item_array_t *items; +    model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items); + +    if (items->size() > wear_limit) +    { +        return false; +    } + +    if (items->size() == 0) +    { +        // Nothing to move(create) +        return false; +    } + +    if (cats->size() > 0) +    { +        // We do not allow subfolders in outfits of "My Outfits" yet +        return false; +    } + +    LLInventoryModel::item_array_t::iterator iter = items->begin(); +    LLInventoryModel::item_array_t::iterator end = items->end(); + +    while (iter != end) +    { +        LLViewerInventoryItem *item = *iter; +        if (!can_move_to_outfit(item, false)) +        { +            return false; +        } +        iter++; +    } + +    return true; +} + +std::string get_localized_folder_name(LLUUID cat_uuid) +{ +    std::string localized_root_name; +    const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); +    if (cat) +    { +        LLFolderType::EType preferred_type = cat->getPreferredType(); + +        // Translation of Accessories folder in Library inventory folder +        bool accessories = false; +        if(cat->getName() == "Accessories") +        { +            const LLUUID& parent_folder_id = cat->getParentUUID(); +            accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); +        } + +        //"Accessories" inventory category has folder type FT_NONE. So, this folder +        //can not be detected as protected with LLFolderType::lookupIsProtectedType +        localized_root_name.assign(cat->getName()); +        if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) +        { +            LLTrans::findString(localized_root_name, std::string("InvFolder ") + cat->getName(), LLSD()); +        } +    } +     +    return localized_root_name; +} + +void new_folder_window(const LLUUID& folder_id) +{ +    LLPanelMainInventory::newFolderWindow(folder_id); +} + +void ungroup_folder_items(const LLUUID& folder_id) +{ +    LLInventoryCategory* inv_cat = gInventory.getCategory(folder_id); +    if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) +    { +        return; +    } +    const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); +    LLInventoryModel::cat_array_t* cat_array; +    LLInventoryModel::item_array_t* item_array; +    gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); +    LLInventoryModel::cat_array_t cats = *cat_array; +    LLInventoryModel::item_array_t items = *item_array; + +    for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) +    { +        LLViewerInventoryCategory* cat = *cat_iter; +        if (cat) +        { +            gInventory.changeCategoryParent(cat, new_cat_uuid, false); +        } +    } +    for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) +    { +        LLViewerInventoryItem* item = *item_iter; +        if(item) +        { +            gInventory.changeItemParent(item, new_cat_uuid, false); +        } +    } +    gInventory.removeCategory(inv_cat->getUUID()); +    gInventory.notifyObservers(); +} + +std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id) +{ +    if (model) +    { +        const LLInventoryItem *item = model->getItem(item_id); +        if(item) +        { +            std::string desc = item->getDescription(); +            LLStringUtil::toUpper(desc); +            return desc; +        } +    } +    return LLStringUtil::null; +} + +std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id) +{ +    if (model) +    { +        const LLInventoryItem *item = model->getItem(item_id); +        if(item) +        { +            LLAvatarName av_name; +            if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) +            { +                std::string username = av_name.getUserName(); +                LLStringUtil::toUpper(username); +                return username; +            } +        } +    } +    return LLStringUtil::null; +} + +std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id) +{ +    if (model) +    { +        const LLViewerInventoryItem *item = model->getItem(item_id); +        if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery())) +        { +            std::string uuid = item->getAssetUUID().asString(); +            LLStringUtil::toUpper(uuid); +            return uuid; +        } +    } +    return LLStringUtil::null; +} + +bool can_share_item(const LLUUID& item_id) +{ +    bool can_share = false; + +    if (gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID())) +    { +            const LLViewerInventoryItem *item = gInventory.getItem(item_id); +            if (item) +            { +                if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) +                { +                    can_share = LLGiveInventory::isInventoryGiveAcceptable(item); +                } +            } +            else +            { +                can_share = (gInventory.getCategory(item_id) != NULL); +            } + +            const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); +            if ((item_id == trash_id) || gInventory.isObjectDescendentOf(item_id, trash_id)) +            { +                can_share = false; +            } +    } + +    return can_share; +} +///---------------------------------------------------------------------------- +/// LLMarketplaceValidator implementations +///---------------------------------------------------------------------------- + +LLMarketplaceValidator::LLMarketplaceValidator() +    : mPendingCallbacks(0) +    , mValidationInProgress(false) +{  } +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; + +    const ValidationRequest &first = mValidationQueue.front(); +    LLViewerInventoryCategory* cat = gInventory.getCategory(first.mCategoryId); +    if (!cat) +    { +        LL_WARNS() << "Tried to validate a folder that doesn't exist" << LL_ENDL; +        if (first.mCbDone) +        { +            first.mCbDone(false); +        } +        mValidationQueue.pop(); +        start(); +        return; +    } + +    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(); +        } +    }; + +    mPendingResult = true; +    mPendingCallbacks = 1; // do '1' in case something decides to callback immediately + +    S32 pending_calbacks = 0; +    bool result = true; +    validate_marketplacelistings( +        cat, +        result_callback, +        first.mCbMsg, +        first.mFixHierarchy, +        first.mDepth, +        true, +        pending_calbacks, +        result); + +    result_callback(pending_calbacks, result); +} + +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  ///---------------------------------------------------------------------------- @@ -2182,6 +2672,19 @@ bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat,  	}  } +bool LLFindBrokenLinks::operator()(LLInventoryCategory* cat, +    LLInventoryItem* item) +{ +    // only for broken links getType will be a link +    // otherwise it's supposed to have the type of an item +    // it is linked too +    if (item && LLAssetType::lookupIsLinkType(item->getType())) +    { +        return TRUE; +    } +    return FALSE; +} +  bool LLFindWearables::operator()(LLInventoryCategory* cat,  								 LLInventoryItem* item)  { @@ -2247,6 +2750,11 @@ void LLFindWearablesOfType::setType(LLWearableType::EType type)  	mWearableType = type;  } +bool LLIsTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ +    return item && (item->getType() == LLAssetType::AT_TEXTURE); +} +  bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item)  {  	if (item) @@ -2514,8 +3022,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  	if ("delete" == action)  	{ -		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<LLFolderViewItem*>::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it) @@ -2538,10 +3045,10 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  		}  		else  		{ -			if (!sDisplayedAtSession) // ask for the confirmation at least once per session +			if (!sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session  			{  				LLNotifications::instance().setIgnored("DeleteItems", false); -				sDisplayedAtSession = true; +				sDeleteConfirmationDisplayed = true;  			}  			LLSD args; @@ -2593,7 +3100,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  	LLMultiPreview* multi_previewp = NULL; -	LLMultiProperties* multi_propertiesp = NULL; +	LLMultiItemProperties* multi_itempropertiesp = nullptr;  	if (("task_open" == action  || "open" == action) && selected_items.size() > 1)  	{ @@ -2627,10 +3134,9 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  	}  	else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1)  	{ -		multi_propertiesp = new LLMultiProperties(); -		gFloaterView->addChild(multi_propertiesp); - -		LLFloater::setFloaterHost(multi_propertiesp); +		multi_itempropertiesp = new LLMultiItemProperties("item_properties"); +		gFloaterView->addChild(multi_itempropertiesp); +		LLFloater::setFloaterHost(multi_itempropertiesp);  	}  	std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); @@ -2640,7 +3146,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), @@ -2755,36 +3261,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root      {          if (ids.size() == 1)          { -            LLInventoryCategory* inv_cat = gInventory.getCategory(*ids.begin()); -            if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) -            { -                return; -            } -            const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); -            LLInventoryModel::cat_array_t* cat_array; -            LLInventoryModel::item_array_t* item_array; -            gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); -            LLInventoryModel::cat_array_t cats = *cat_array; -            LLInventoryModel::item_array_t items = *item_array; - -            for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) -            { -                LLViewerInventoryCategory* cat = *cat_iter; -                if (cat) -                { -                    gInventory.changeCategoryParent(cat, new_cat_uuid, false); -                } -            } -            for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) -            { -                LLViewerInventoryItem* item = *item_iter; -                if(item) -                { -                    gInventory.changeItemParent(item, new_cat_uuid, false); -                } -            } -            gInventory.removeCategory(inv_cat->getUUID()); -            gInventory.notifyObservers(); +            ungroup_folder_items(*ids.begin());          }      }      else @@ -2798,6 +3275,14 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root              if(!bridge) continue;              bridge->performAction(model, action);          } +        if(root->isSingleFolderMode() && selected_items.empty()) +        { +            LLInvFVBridge* bridge = (LLInvFVBridge*)root->getViewModelItem(); +            if(bridge) +            { +                bridge->performAction(model, action); +            } +        }      }      // Update the marketplace listings that have been affected by the operation @@ -2808,9 +3293,9 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  	{  		multi_previewp->openFloater(LLSD());  	} -	else if (multi_propertiesp) +	else if (multi_itempropertiesp)  	{ -		multi_propertiesp->openFloater(LLSD()); +		multi_itempropertiesp->openFloater(LLSD());  	}  } @@ -2898,7 +3383,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 8c8bd789c2..925217dda3 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -75,6 +75,7 @@ void update_all_marketplace_count();  void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);  void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false); +void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id, bool move_no_copy_items, inventory_func_type callback);  void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items); @@ -91,13 +92,11 @@ std::string make_info(const LLInventoryObject* object);  // Generates a string containing the path name and id of the object specified by id.  std::string make_inventory_info(const LLUUID& id); -typedef boost::function<void(std::string& validation_message, S32 depth, LLError::ELevel log_level)> 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); @@ -108,10 +107,65 @@ void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected  bool is_only_cats_selected(const uuid_vec_t& selected_uuids);  bool is_only_items_selected(const uuid_vec_t& selected_uuids); +bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); +bool can_move_to_landmarks(LLInventoryItem* inv_item); +bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit); +std::string get_localized_folder_name(LLUUID cat_uuid); +void new_folder_window(const LLUUID& folder_id); +void ungroup_folder_items(const LLUUID& folder_id); +std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id); +std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id); +std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id); +bool can_share_item(const LLUUID& item_id); +  /**                    Miscellaneous global functions   **                                                                            **   *******************************************************************************/ +class LLMarketplaceValidator: public LLSingleton<LLMarketplaceValidator> +{ +    LLSINGLETON(LLMarketplaceValidator); +    ~LLMarketplaceValidator(); +    LOG_CLASS(LLMarketplaceValidator); +public: + +    typedef boost::function<void(std::string& validation_message, S32 depth, LLError::ELevel log_level)> validation_msg_callback_t; +    typedef boost::function<void(bool result)> 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<ValidationRequest> mValidationQueue; +}; +  /********************************************************************************   **                                                                            **   **                    INVENTORY COLLECTOR FUNCTIONS @@ -328,6 +382,20 @@ public:  };  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindBrokenLinks +// +// Collects broken links +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindBrokenLinks : public LLInventoryCollectFunctor +{ +public: +    LLFindBrokenLinks() {} +    virtual ~LLFindBrokenLinks() {} +    virtual bool operator()(LLInventoryCategory* cat, +        LLInventoryItem* item); +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLFindByMask  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  class LLFindByMask : public LLInventoryCollectFunctor @@ -425,6 +493,15 @@ private:  	LLWearableType::EType mWearableType;  }; +class LLIsTextureType : public LLInventoryCollectFunctor +{ +public: +    LLIsTextureType() {} +    virtual ~LLIsTextureType() {} +    virtual bool operator()(LLInventoryCategory* cat, +        LLInventoryItem* item); +}; +  /** Filter out wearables-links */  class LLFindActualWearablesOfType : public LLFindWearablesOfType  { @@ -484,7 +561,7 @@ struct LLInventoryAction      static void saveMultipleTextures(const std::vector<std::string>& filenames, std::set<LLFolderViewItem*> selected_items, LLInventoryModel* model); -	static const int sConfirmOnDeleteItemsNumber; +    static bool sDeleteConfirmationDisplayed;  private:  	static void buildMarketplaceFolders(LLFolderView* root); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp new file mode 100644 index 0000000000..4838ba7a47 --- /dev/null +++ b/indra/newview/llinventorygallery.cpp @@ -0,0 +1,3824 @@ +/** + * @file llinventorygallery.cpp + * @brief LLInventoryGallery class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventorygallery.h" +#include "llinventorygallerymenu.h" + +#include "llclipboard.h" +#include "llcommonutils.h" +#include "lliconctrl.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llinventoryicon.h" +#include "llinventorymodel.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llthumbnailctrl.h" +#include "lltextbox.h" +#include "llviewerfoldertype.h" + +#include "llagent.h" +#include "llappearancemgr.h" +#include "llenvironment.h" +#include "llfriendcard.h" +#include "llgesturemgr.h" +#include "llmarketplacefunctions.h" +#include "llnotificationsutil.h" +#include "lloutfitobserver.h" +#include "lltrans.h" +#include "llviewerassettype.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llvoavatarself.h" + +static LLPanelInjector<LLInventoryGallery> t_inventory_gallery("inventory_gallery"); + +const S32 GALLERY_ITEMS_PER_ROW_MIN = 2; + +// Helper dnd functions +BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, BOOL is_link); +BOOL dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm); +void dropToMyOutfits(LLInventoryCategory* inv_cat); + +class LLGalleryPanel: public LLPanel +{ +public: + +    BOOL canFocusChildren() const override +    { +        // Tell Tab to not focus children +        return FALSE; +    } + +protected: + +    LLGalleryPanel(const LLPanel::Params& params): LLPanel(params) +    { +    }; + +    friend class LLUICtrlFactory; +}; + +//----------------------------- +// LLInventoryGallery +//----------------------------- + +LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p) +    : LLPanel(), +      mScrollPanel(NULL), +      mGalleryPanel(NULL), +      mLastRowPanel(NULL), +      mGalleryCreated(false), +      mRowCount(0), +      mItemsAddedCount(0), +      mRowPanelHeight(p.row_panel_height), +      mVerticalGap(p.vertical_gap), +      mHorizontalGap(p.horizontal_gap), +      mItemWidth(p.item_width), +      mItemHeight(p.item_height), +      mItemHorizontalGap(p.item_horizontal_gap), +      mItemsInRow(p.items_in_row), +      mRowPanWidthFactor(p.row_panel_width_factor), +      mGalleryWidthFactor(p.gallery_width_factor), +      mIsInitialized(false), +      mRootDirty(false), +      mNeedsArrange(false), +      mSearchType(LLInventoryFilter::SEARCHTYPE_NAME), +      mSortOrder(LLInventoryFilter::SO_DATE) +{ +    updateGalleryWidth(); +    mFilter = new LLInventoryFilter(); +    mCategoriesObserver = new LLInventoryCategoriesObserver(); +    mThumbnailsObserver = new LLThumbnailsObserver(); +    gInventory.addObserver(mThumbnailsObserver); + +    mGestureObserver = new LLGalleryGestureObserver(this); +    LLGestureMgr::instance().addObserver(mGestureObserver); + +    mUsername = gAgentUsername; +    LLStringUtil::toUpper(mUsername); +} + +LLInventoryGallery::Params::Params() +    : row_panel_height("row_panel_height", 180), +      vertical_gap("vertical_gap", 10), +      horizontal_gap("horizontal_gap", 10), +      item_width("item_width", 150), +      item_height("item_height", 175), +      item_horizontal_gap("item_horizontal_gap", 16), +      items_in_row("items_in_row", GALLERY_ITEMS_PER_ROW_MIN), +      row_panel_width_factor("row_panel_width_factor", 166), +      gallery_width_factor("gallery_width_factor", 163) +{ +    addSynonym(row_panel_height, "row_height"); +} + +const LLInventoryGallery::Params& LLInventoryGallery::getDefaultParams() +{ +    return LLUICtrlFactory::getDefaultParams<LLInventoryGallery>(); +} + +BOOL LLInventoryGallery::postBuild() +{ +    mScrollPanel = getChild<LLScrollContainer>("gallery_scroll_panel"); +    LLPanel::Params params = LLPanel::getDefaultParams(); +    mGalleryPanel = LLUICtrlFactory::create<LLPanel>(params); +    mMessageTextBox = getChild<LLTextBox>("empty_txt"); +    mInventoryGalleryMenu = new LLInventoryGalleryContextMenu(this); +    mRootGalleryMenu = new LLInventoryGalleryContextMenu(this); +    mRootGalleryMenu->setRootFolder(true); +    return TRUE; +} + +LLInventoryGallery::~LLInventoryGallery() +{ +    if (gEditMenuHandler == this) +    { +        gEditMenuHandler = NULL; +    } + +    delete mInventoryGalleryMenu; +    delete mRootGalleryMenu; +    delete mFilter; + +    gIdleCallbacks.deleteFunction(onIdle, (void*)this); + +    while (!mUnusedRowPanels.empty()) +    { +        LLPanel* panelp = mUnusedRowPanels.back(); +        mUnusedRowPanels.pop_back(); +        panelp->die(); +    } +    while (!mUnusedItemPanels.empty()) +    { +        LLPanel* panelp = mUnusedItemPanels.back(); +        mUnusedItemPanels.pop_back(); +        panelp->die(); +    } +    while (!mHiddenItems.empty()) +    { +        LLPanel* panelp = mHiddenItems.back(); +        mHiddenItems.pop_back(); +        panelp->die(); +    } +     + +    if (gInventory.containsObserver(mCategoriesObserver)) +    { +        gInventory.removeObserver(mCategoriesObserver); +    } +    delete mCategoriesObserver; + +    if (gInventory.containsObserver(mThumbnailsObserver)) +    { +        gInventory.removeObserver(mThumbnailsObserver); +    } +    delete mThumbnailsObserver; +     +    LLGestureMgr::instance().removeObserver(mGestureObserver); +    delete mGestureObserver; +} + +void LLInventoryGallery::setRootFolder(const LLUUID cat_id) +{ +    LLViewerInventoryCategory* category = gInventory.getCategory(cat_id); +    if(!category || (mFolderID == cat_id)) +    { +        return; +    } +    if(mFolderID.notNull()) +    { +        mBackwardFolders.push_back(mFolderID); +    } + +    gIdleCallbacks.deleteFunction(onIdle, (void*)this); + +    for (const LLUUID& id : mSelectedItemIDs) +    { +        if (mItemMap[id]) +        { +            mItemMap[id]->setSelected(FALSE); +        } +    } + +    mFolderID = cat_id; +    mItemsToSelect.clear(); +    mSelectedItemIDs.clear(); +    mItemBuildQuery.clear(); +    mNeedsArrange = false; +    dirtyRootFolder(); +} + +void LLInventoryGallery::dirtyRootFolder() +{ +    if (getVisible()) +    { +        updateRootFolder(); +    } +    else +    { +        mRootDirty = true; +    } +} + +void LLInventoryGallery::updateRootFolder() +{ +    llassert(mFolderID.notNull()); +    if (mIsInitialized && mFolderID.notNull()) +    { +        S32 count = mItemsAddedCount; +        for (S32 i = count - 1; i >= 0; i--) +        { +            updateRemovedItem(mItems[i]->getUUID()); +        } +        S32 hidden_count = mHiddenItems.size(); +        for (S32 i = hidden_count - 1; i >= 0; i--) +        { +            updateRemovedItem(mHiddenItems[i]->getUUID()); +        } +        mItemBuildQuery.clear(); +         +        if (gInventory.containsObserver(mCategoriesObserver)) +        { +            gInventory.removeObserver(mCategoriesObserver); +        } +        delete mCategoriesObserver; + +        mCategoriesObserver = new LLInventoryCategoriesObserver(); + +        if (gInventory.containsObserver(mThumbnailsObserver)) +        { +            gInventory.removeObserver(mThumbnailsObserver); +        } +        delete mThumbnailsObserver; +        mThumbnailsObserver = new LLThumbnailsObserver(); +        gInventory.addObserver(mThumbnailsObserver); +    } +    { +        mRootChangedSignal(); + +        gInventory.addObserver(mCategoriesObserver); +         +        // Start observing changes in selected category. +        mCategoriesObserver->addCategory(mFolderID, +            boost::bind(&LLInventoryGallery::refreshList, this, mFolderID)); + +        LLViewerInventoryCategory* category = gInventory.getCategory(mFolderID); +        //If not all items are fetched now +        // the observer will refresh the list as soon as the new items +        // arrive. +        category->fetch(); +         +        //refreshList(cat_id); +        LLInventoryModel::cat_array_t* cat_array; +        LLInventoryModel::item_array_t* item_array; + +        gInventory.getDirectDescendentsOf(mFolderID, cat_array, item_array); + +        // Creating a vector of newly collected sub-categories UUIDs. +        for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array->begin(); +            iter != cat_array->end(); +            iter++) +        { +            mItemBuildQuery.insert((*iter)->getUUID()); +        } +         +        for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin(); +            iter != item_array->end(); +            iter++) +        { +            mItemBuildQuery.insert((*iter)->getUUID()); +        } +        mIsInitialized = true; +        mRootDirty = false; + +        if (mScrollPanel) +        { +            mScrollPanel->goToTop(); +        } +    } + +    LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLInventoryGallery::onCOFChanged, this)); + +    if (!mGalleryCreated) +    { +        initGallery(); +    } + +    if (!mItemBuildQuery.empty()) +    { +        gIdleCallbacks.addFunction(onIdle, (void*)this); +    } +} + +void LLInventoryGallery::initGallery() +{ +    if (!mGalleryCreated) +    { +        uuid_vec_t cats; +        getCurrentCategories(cats); +        int n = cats.size(); +        buildGalleryPanel(n); +        mScrollPanel->addChild(mGalleryPanel); +        for (int i = 0; i < n; i++) +        { +            addToGallery(mItemMap[cats[i]]); +        } +        reArrangeRows(); +        mGalleryCreated = true; +    } +} + +void LLInventoryGallery::draw() +{ +    LLPanel::draw(); +    if (mGalleryCreated) +    { +        if(!updateRowsIfNeeded()) +        { +            handleModifiedFilter(); +        } +    } +} + +void LLInventoryGallery::onVisibilityChange(BOOL new_visibility) +{ +    if (new_visibility) +    { +        if (mRootDirty) +        { +            updateRootFolder(); +        } +        else if (mNeedsArrange) +        { +            gIdleCallbacks.addFunction(onIdle, (void*)this); +        } +    } +    LLPanel::onVisibilityChange(new_visibility); +} + +bool LLInventoryGallery::updateRowsIfNeeded() +{ +    S32 scroll_content_width = mScrollPanel ? mScrollPanel->getVisibleContentRect().getWidth() : getRect().getWidth(); +    if(((scroll_content_width - mRowPanelWidth) > mItemWidth) +       && mRowCount > 1) +    { +        reArrangeRows(1); +        return true; +    } +    else if((mRowPanelWidth > (scroll_content_width + mItemHorizontalGap)) +            && mItemsInRow > GALLERY_ITEMS_PER_ROW_MIN) +    { +        reArrangeRows(-1); +        return true; +    } +    return false; +} + +bool compareGalleryItem(LLInventoryGalleryItem* item1, LLInventoryGalleryItem* item2, bool sort_by_date, bool sort_folders_by_name) +{ +    if (item1->getSortGroup() != item2->getSortGroup()) +    { +        return (item1->getSortGroup() < item2->getSortGroup()); +    } + +    if(sort_folders_by_name && (item1->getSortGroup() != LLInventoryGalleryItem::SG_ITEM)) +    { +        std::string name1 = item1->getItemName(); +        std::string name2 = item2->getItemName(); + +        return (LLStringUtil::compareDict(name1, name2) < 0); +    } + +    if(((item1->isDefaultImage() && item2->isDefaultImage()) || (!item1->isDefaultImage() && !item2->isDefaultImage()))) +    { +        if(sort_by_date) +        { +            return item1->getCreationDate() > item2->getCreationDate(); +        } +        else +        { +            std::string name1 = item1->getItemName(); +            std::string name2 = item2->getItemName(); + +            return (LLStringUtil::compareDict(name1, name2) < 0); +        } +    } +    else +    { +        return item2->isDefaultImage(); +    } +} + +void LLInventoryGallery::reArrangeRows(S32 row_diff) +{ +    std::vector<LLInventoryGalleryItem*> buf_items = mItems; +    for (std::vector<LLInventoryGalleryItem*>::const_reverse_iterator it = buf_items.rbegin(); it != buf_items.rend(); ++it) +    { +        removeFromGalleryLast(*it, false); +    } +    for (std::vector<LLInventoryGalleryItem*>::const_reverse_iterator it = mHiddenItems.rbegin(); it != mHiddenItems.rend(); ++it) +    { +        buf_items.push_back(*it); +    } +    mHiddenItems.clear(); +     +    mItemsInRow+= row_diff; +    updateGalleryWidth(); + +    bool sort_by_date = (mSortOrder & LLInventoryFilter::SO_DATE); +    bool sort_folders_by_name = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME); +    std::sort(buf_items.begin(), buf_items.end(), [sort_by_date, sort_folders_by_name](LLInventoryGalleryItem* item1, LLInventoryGalleryItem* item2) +    { +        return compareGalleryItem(item1, item2, sort_by_date, sort_folders_by_name); +    }); +     +    for (std::vector<LLInventoryGalleryItem*>::const_iterator it = buf_items.begin(); it != buf_items.end(); ++it) +    { +        (*it)->setHidden(false); +        applyFilter(*it, mFilterSubString); +        addToGallery(*it); +    } +    mFilter->clearModified(); +    updateMessageVisibility(); +} + +void LLInventoryGallery::updateGalleryWidth() +{ +    mRowPanelWidth = mRowPanWidthFactor * mItemsInRow - mItemHorizontalGap; +    mGalleryWidth = mGalleryWidthFactor * mItemsInRow - mItemHorizontalGap; +} + +LLPanel* LLInventoryGallery::addLastRow() +{ +    mRowCount++; +    int row = 0; +    int vgap = mVerticalGap * row; +    LLPanel* result = buildRowPanel(0, row * mRowPanelHeight + vgap); +    mGalleryPanel->addChild(result); +    return result; +} + +void LLInventoryGallery::moveRowUp(int row) +{ +    moveRow(row, mRowCount - 1 - row + 1); +} + +void LLInventoryGallery::moveRowDown(int row) +{ +    moveRow(row, mRowCount - 1 - row - 1); +} + +void LLInventoryGallery::moveRow(int row, int pos) +{ +    int vgap = mVerticalGap * pos; +    moveRowPanel(mRowPanels[row], 0, pos * mRowPanelHeight + vgap); +} + +void LLInventoryGallery::removeLastRow() +{ +    mRowCount--; +    mGalleryPanel->removeChild(mLastRowPanel); +    mUnusedRowPanels.push_back(mLastRowPanel); +    mRowPanels.pop_back(); +    if (mRowPanels.size() > 0) +    { +        // Just removed last row +        mLastRowPanel = mRowPanels.back(); +    } +    else +    { +        mLastRowPanel = NULL; +    } +} + +LLPanel* LLInventoryGallery::addToRow(LLPanel* row_stack, LLInventoryGalleryItem* item, int pos, int hgap) +{ +    LLPanel* lpanel = buildItemPanel(pos * mItemWidth + hgap); +    lpanel->addChild(item); +    row_stack->addChild(lpanel); +    mItemPanels.push_back(lpanel); +    return lpanel; +} + +void LLInventoryGallery::addToGallery(LLInventoryGalleryItem* item) +{ +    if(item->isHidden()) +    { +        mHiddenItems.push_back(item); +        return; +    } +    mItemIndexMap[item] = mItemsAddedCount; +    mIndexToItemMap[mItemsAddedCount] = item; +    mItemsAddedCount++; +    int n = mItemsAddedCount; +    int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; +    int n_prev = n - 1; +    int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; + +    bool add_row = row_count != row_count_prev; +    int pos = 0; +    if (add_row) +    { +        for (int i = 0; i < row_count_prev; i++) +        { +            moveRowUp(i); +        } +        mLastRowPanel = addLastRow(); +        mRowPanels.push_back(mLastRowPanel); +    } +    pos = (n - 1) % mItemsInRow; +    mItems.push_back(item); +    addToRow(mLastRowPanel, item, pos, mHorizontalGap * pos); +    reshapeGalleryPanel(row_count); +} + + +void LLInventoryGallery::removeFromGalleryLast(LLInventoryGalleryItem* item, bool needs_reshape) +{ +    if(item->isHidden()) +    { +        mHiddenItems.pop_back(); +        // Note: item still exists!!! +        return; +    } +    int n_prev = mItemsAddedCount; +    int n = mItemsAddedCount - 1; +    int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; +    int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; +    mItemsAddedCount--; +    mIndexToItemMap.erase(mItemsAddedCount); + +    bool remove_row = row_count != row_count_prev; +    removeFromLastRow(mItems[mItemsAddedCount]); +    mItems.pop_back(); +    if (remove_row) +    { +        for (int i = 0; i < row_count_prev - 1; i++) +        { +            moveRowDown(i); +        } +        removeLastRow(); +    } +    if (needs_reshape) +    { +        reshapeGalleryPanel(row_count); +    } +} + + +void LLInventoryGallery::removeFromGalleryMiddle(LLInventoryGalleryItem* item) +{ +    if(item->isHidden()) +    { +        mHiddenItems.erase(std::remove(mHiddenItems.begin(), mHiddenItems.end(), item), mHiddenItems.end()); +        // item still exists and needs to be deleted or used!!! +        return; +    } +    int n = mItemIndexMap[item]; +    mItemIndexMap.erase(item); +    mIndexToItemMap.erase(n); +    std::vector<LLInventoryGalleryItem*> saved; +    for (int i = mItemsAddedCount - 1; i > n; i--) +    { +        saved.push_back(mItems[i]); +        removeFromGalleryLast(mItems[i]); +    } +    removeFromGalleryLast(mItems[n]); +    int saved_count = saved.size(); +    for (int i = 0; i < saved_count; i++) +    { +        addToGallery(saved.back()); +        saved.pop_back(); +    } +} + +void LLInventoryGallery::removeFromLastRow(LLInventoryGalleryItem* item) +{ +    mItemPanels.back()->removeChild(item); +    mLastRowPanel->removeChild(mItemPanels.back()); +    mUnusedItemPanels.push_back(mItemPanels.back()); +    mItemPanels.pop_back(); +} + +LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn) +{ +    LLInventoryGalleryItem::Params giparams; +    giparams.visible = true; +    giparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); +    giparams.rect(LLRect(0,mItemHeight, mItemWidth, 0)); +    LLInventoryGalleryItem* gitem = LLUICtrlFactory::create<LLInventoryGalleryItem>(giparams); +    gitem->setItemName(name); +    gitem->setUUID(item_id); +    gitem->setGallery(this); +    gitem->setType(type, inventory_type, flags, is_link); +    gitem->setThumbnail(thumbnail_id); +    gitem->setWorn(is_worn); +    gitem->setCreatorName(get_searchable_creator_name(&gInventory, item_id)); +    gitem->setDescription(get_searchable_description(&gInventory, item_id)); +    gitem->setAssetIDStr(get_searchable_UUID(&gInventory, item_id)); +    gitem->setCreationDate(creation_date); +    return gitem; +} + +void LLInventoryGallery::buildGalleryPanel(int row_count) +{ +    LLPanel::Params params; +    params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); +    params.visible = true; +    params.use_bounding_rect = false; +    mGalleryPanel = LLUICtrlFactory::create<LLGalleryPanel>(params); +    reshapeGalleryPanel(row_count); +} + +void LLInventoryGallery::reshapeGalleryPanel(int row_count) +{ +    int bottom = 0; +    int left = 0; +    int height = row_count * (mRowPanelHeight + mVerticalGap); +    LLRect rect = LLRect(left, bottom + height, left + mGalleryWidth, bottom); +    mGalleryPanel->setRect(rect); +    mGalleryPanel->reshape(mGalleryWidth, height); +} + +LLPanel* LLInventoryGallery::buildItemPanel(int left) +{ +    int top = 0; +    LLPanel* lpanel = NULL; +    if(mUnusedItemPanels.empty()) +    { +        LLPanel::Params lpparams; +        lpparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); +        lpparams.visible = true; +        lpparams.rect(LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top)); +        lpparams.use_bounding_rect = false; +        lpparams.focus_root = false; +        //lpparams.tab_stop = false; +        lpanel = LLUICtrlFactory::create<LLPanel>(lpparams); +    } +    else +    { +        lpanel = mUnusedItemPanels.back(); +        mUnusedItemPanels.pop_back(); + +        LLRect rect = LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top); +        lpanel->setShape(rect, false); +    } +    return lpanel; +} + +LLPanel* LLInventoryGallery::buildRowPanel(int left, int bottom) +{ +    LLPanel* stack = NULL; +    if(mUnusedRowPanels.empty()) +    { +        LLPanel::Params sparams; +        sparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); +        sparams.use_bounding_rect = false; +        sparams.visible = true; +        sparams.focus_root = false; +        //sparams.tab_stop = false; +        stack = LLUICtrlFactory::create<LLPanel>(sparams); +    } +    else +    { +        stack = mUnusedRowPanels.back(); +        mUnusedRowPanels.pop_back(); +    } +    moveRowPanel(stack, left, bottom); +    return stack; +} + +void LLInventoryGallery::moveRowPanel(LLPanel* stack, int left, int bottom) +{ +    LLRect rect = LLRect(left, bottom + mRowPanelHeight, left + mRowPanelWidth, bottom); +    stack->setRect(rect); +    stack->reshape(mRowPanelWidth, mRowPanelHeight); +} + +void LLInventoryGallery::setFilterSubString(const std::string& string) +{ +    mFilterSubString = string; +    mFilter->setFilterSubString(string); +     +    //reArrangeRows(); +} + +bool LLInventoryGallery::applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring) +{ +    if(item) +    { +        bool visible = checkAgainstFilters(item, filter_substring); +        item->setHidden(!visible); +        return visible; +    } +    return false; +} + +bool LLInventoryGallery::checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring) +{ +    if (!item) return false; + +    if (item->isFolder() && (mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS)) +    { +        return true; +    } + +    if(item->isLink() && ((mFilter->getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) == 0) && !filter_substring.empty()) +    { +        return false; +    } +     +    bool hidden = false; + +    if(mFilter->getFilterCreatorType() == LLInventoryFilter::FILTERCREATOR_SELF) +    { +        hidden = (item->getCreatorName() == mUsername) || item->isFolder(); +    } +    else if(mFilter->getFilterCreatorType() == LLInventoryFilter::FILTERCREATOR_OTHERS) +    { +        hidden = (item->getCreatorName() != mUsername) || item->isFolder(); +    } +    if(hidden) +    { +        return false; +    } + +    if(!mFilter->checkAgainstFilterThumbnails(item->getUUID())) +    { +        return false; +    } + +    if(!checkAgainstFilterType(item->getUUID())) +    { +        return false; +    } + +    std::string desc; +    switch(mSearchType) +    { +        case LLInventoryFilter::SEARCHTYPE_CREATOR: +            desc = item->getCreatorName(); +            break; +        case LLInventoryFilter::SEARCHTYPE_DESCRIPTION: +            desc = item->getDescription(); +            break; +        case LLInventoryFilter::SEARCHTYPE_UUID: +            desc = item->getAssetIDStr(); +            break; +        case LLInventoryFilter::SEARCHTYPE_NAME: +        default: +            desc = item->getItemName() + item->getItemNameSuffix(); +            break; +    } +     +    LLStringUtil::toUpper(desc); + +    std::string cur_filter = filter_substring; +    LLStringUtil::toUpper(cur_filter); + +    hidden = (std::string::npos == desc.find(cur_filter)); +    return !hidden; +} + +void LLInventoryGallery::onIdle(void* userdata) +{ +    LLInventoryGallery* self = (LLInventoryGallery*)userdata; + +    if (!self->mIsInitialized || !self->mGalleryCreated) +    { +        self->mNeedsArrange = false; +        return; +    } + +    bool visible = self->getVisible(); // In visible chain? +    const F64 MAX_TIME_VISIBLE = 0.020f; +    const F64 MAX_TIME_HIDDEN = 0.001f; // take it slow +    const F64 max_time = visible ? MAX_TIME_VISIBLE : MAX_TIME_HIDDEN; +    F64 curent_time = LLTimer::getTotalSeconds(); +    const F64 end_time = curent_time + max_time; + +    while (!self->mItemBuildQuery.empty() && end_time > curent_time) +    { +        uuid_set_t::iterator iter = self->mItemBuildQuery.begin(); +        LLUUID item_id = *iter; +        self->mNeedsArrange |= self->updateAddedItem(item_id); +        self->mItemBuildQuery.erase(iter); +        curent_time = LLTimer::getTotalSeconds(); +    } + +    if (self->mNeedsArrange && visible) +    { +        self->mNeedsArrange = false; +        self->reArrangeRows(); +        self->updateMessageVisibility(); +    } + +    if (!self->mItemsToSelect.empty() && !self->mNeedsArrange) +    { +        selection_deque selection_list(self->mItemsToSelect); +        self->mItemsToSelect.clear(); +        for (LLUUID & item_to_select : selection_list) +        { +            self->addItemSelection(item_to_select, true); +        } +    } + +    if (self->mItemsToSelect.empty() && self->mItemBuildQuery.empty()) +    { +        gIdleCallbacks.deleteFunction(onIdle, (void*)self); +    } +} + +void LLInventoryGallery::setSearchType(LLInventoryFilter::ESearchType type) +{ +    if(mSearchType != type) +    { +        mSearchType = type; +        if(!mFilterSubString.empty()) +        { +            reArrangeRows(); +        } +    } +} + +void LLInventoryGallery::getCurrentCategories(uuid_vec_t& vcur) +{ +    for (gallery_item_map_t::const_iterator iter = mItemMap.begin(); +        iter != mItemMap.end(); +        iter++) +    { +        if ((*iter).second != NULL) +        { +            vcur.push_back((*iter).first); +        } +    } +} + +bool LLInventoryGallery::updateAddedItem(LLUUID item_id) +{ +    LLInventoryObject* obj = gInventory.getObject(item_id); +    if (!obj) +    { +        LL_WARNS("InventoryGallery") << "Failed to find item: " << item_id << LL_ENDL; +        return false; +    } + +    std::string name = obj->getName(); +    LLUUID thumbnail_id = obj->getThumbnailUUID();; +    LLInventoryType::EType inventory_type(LLInventoryType::IT_CATEGORY); +    U32 misc_flags = 0; +    bool is_worn = false; +    LLInventoryItem* inv_item = gInventory.getItem(item_id); +    if (inv_item) +    { +        inventory_type = inv_item->getInventoryType(); +        misc_flags = inv_item->getFlags(); +        if (LLAssetType::AT_GESTURE == obj->getType()) +        { +            is_worn = LLGestureMgr::instance().isGestureActive(item_id); +        } +        else +        { +            is_worn = LLAppearanceMgr::instance().isLinkedInCOF(item_id); +        } +    } +    else if (LLAssetType::AT_CATEGORY == obj->getType()) +    { +        name = get_localized_folder_name(item_id); +        if(thumbnail_id.isNull()) +        { +            thumbnail_id = getOutfitImageID(item_id); +        } +    } + +    bool res = false; + +    LLInventoryGalleryItem* item = buildGalleryItem(name, item_id, obj->getType(), thumbnail_id, inventory_type, misc_flags, obj->getCreationDate(), obj->getIsLinkType(), is_worn); +    mItemMap.insert(LLInventoryGallery::gallery_item_map_t::value_type(item_id, item)); +    if (mGalleryCreated) +    { +        res = applyFilter(item, mFilterSubString); +        addToGallery(item); +    } + +    mThumbnailsObserver->addItem(item_id, +        boost::bind(&LLInventoryGallery::updateItemThumbnail, this, item_id)); +    return res; +} + +void LLInventoryGallery::updateRemovedItem(LLUUID item_id) +{ +    gallery_item_map_t::iterator item_iter = mItemMap.find(item_id); +    if (item_iter != mItemMap.end()) +    { +        mThumbnailsObserver->removeItem(item_id); + +        LLInventoryGalleryItem* item = item_iter->second; + +        deselectItem(item_id); +        mItemMap.erase(item_iter); +        removeFromGalleryMiddle(item); + +        // kill removed item +        if (item != NULL) +        { +            // Todo: instead of deleting, store somewhere to reuse later +            item->die(); +        } +    } + +    mItemBuildQuery.erase(item_id); +} + +void LLInventoryGallery::updateChangedItemName(LLUUID item_id, std::string name) +{ +    gallery_item_map_t::iterator iter = mItemMap.find(item_id); +    if (iter != mItemMap.end()) +    { +        LLInventoryGalleryItem* item = iter->second; +        if (item) +        { +            item->setItemName(name); +        } +    } +} + +void LLInventoryGallery::updateWornItem(LLUUID item_id, bool is_worn) +{ +    gallery_item_map_t::iterator iter = mItemMap.find(item_id); +    if (iter != mItemMap.end()) +    { +        LLInventoryGalleryItem* item = iter->second; +        if (item) +        { +            item->setWorn(is_worn); +        } +    } +} + +void LLInventoryGallery::updateItemThumbnail(LLUUID item_id) +{ +    LLInventoryObject* obj = gInventory.getObject(item_id); +    if (!obj) +    { +        return; +    } +    LLUUID thumbnail_id = obj->getThumbnailUUID(); + +    if ((LLAssetType::AT_CATEGORY == obj->getType()) && thumbnail_id.isNull()) +    { +        thumbnail_id = getOutfitImageID(item_id); +    } + +    if (mItemMap[item_id]) +    { +        mItemMap[item_id]->setThumbnail(thumbnail_id); + +        bool passes_filter = checkAgainstFilters(mItemMap[item_id], mFilterSubString); +        if((mItemMap[item_id]->isHidden() && passes_filter) +           || (!mItemMap[item_id]->isHidden() && !passes_filter)) +        { +            reArrangeRows(); +        } +    } +} + +BOOL LLInventoryGallery::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ +    if (mSelectedItemIDs.size() > 0) +    { +        setFocus(true); +    } +    mLastInteractedUUID = LLUUID::null; + +    // Scroll is going to always return true +    BOOL res = LLPanel::handleRightMouseDown(x, y, mask); + +    if (mLastInteractedUUID.isNull()) // no child were hit +    { +        clearSelection(); +        if (mInventoryGalleryMenu && mFolderID.notNull()) +        { +            uuid_vec_t selected_uuids; +            selected_uuids.push_back(mFolderID); +            mRootGalleryMenu->show(this, selected_uuids, x, y); +            return TRUE; +        } +    } +    return res; +} + + +BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask) +{ +    BOOL handled = FALSE; +    switch (key) +    { +        case KEY_RETURN: +            // Open selected items if enter key hit on the inventory panel +            if (mask == MASK_NONE && mInventoryGalleryMenu && mSelectedItemIDs.size() == 1) +            { +                selection_deque::iterator iter = mSelectedItemIDs.begin(); +                LLViewerInventoryCategory* category = gInventory.getCategory(*iter); +                if (category) +                { +                    setRootFolder(*iter); +                    handled = TRUE; +                } +                else +                { +                    LLViewerInventoryItem* item = gInventory.getItem(*iter); +                    if (item) +                    { +                        LLInvFVBridgeAction::doAction(item->getType(), *iter, &gInventory); +                    } +                } +            } +            handled = TRUE; +            break; +        case KEY_DELETE: +#if LL_DARWIN +        case KEY_BACKSPACE: +#endif +            // Delete selected items if delete or backspace key hit on the inventory panel +            // Note: on Mac laptop keyboards, backspace and delete are one and the same +            if (canDeleteSelection()) +            { +                deleteSelection(); +            } +            handled = TRUE; +            break; + +        case KEY_F2: +            mFilterSubString.clear(); +            if (mInventoryGalleryMenu && mSelectedItemIDs.size() == 1) +            { +                mInventoryGalleryMenu->rename(mSelectedItemIDs.front()); +            } +            handled = TRUE; +            break; + +        case KEY_PAGE_UP: +            mFilterSubString.clear(); +            if (mScrollPanel) +            { +                mScrollPanel->pageUp(30); +            } +            handled = TRUE; +            break; + +        case KEY_PAGE_DOWN: +            mFilterSubString.clear(); +            if (mScrollPanel) +            { +                mScrollPanel->pageDown(30); +            } +            handled = TRUE; +            break; + +        case KEY_HOME: +            mFilterSubString.clear(); +            if (mScrollPanel) +            { +                mScrollPanel->goToTop(); +            } +            handled = TRUE; +            break; + +        case KEY_END: +            mFilterSubString.clear(); +            if (mScrollPanel) +            { +                mScrollPanel->goToBottom(); +            } +            handled = TRUE; +            break; + +        case KEY_LEFT: +            moveLeft(mask); +            handled = TRUE; +            break; + +        case KEY_RIGHT: +            moveRight(mask); +            handled = TRUE; +            break; + +        case KEY_UP: +            moveUp(mask); +            handled = TRUE; +            break; + +        case KEY_DOWN: +            moveDown(mask); +            handled = TRUE; +            break; + +        default: +            break; +    } + +    if (handled) +    { +        mInventoryGalleryMenu->hide(); +    } + +    return handled; +} + +void LLInventoryGallery::moveUp(MASK mask) +{ +    mFilterSubString.clear(); + +    if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) +    { +        LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; +        if (item) +        { +            if (mask == MASK_NONE || mask == MASK_CONTROL) +            { +                S32 n = mItemIndexMap[item]; +                n -= mItemsInRow; +                if (n >= 0) +                { +                    item = mIndexToItemMap[n]; +                    LLUUID item_id = item->getUUID(); +                    if (mask == MASK_CONTROL) +                    { +                        addItemSelection(item_id, true); +                    } +                    else +                    { +                        changeItemSelection(item_id, true); +                    } +                    item->setFocus(TRUE); +                    claimEditHandler(); +                } +            } +            else if (mask == MASK_SHIFT) +            { +                S32 n = mItemIndexMap[item]; +                S32 target  = llmax(0, n - mItemsInRow); +                if (target != n) +                { +                    item = mIndexToItemMap[target]; +                    toggleSelectionRangeFromLast(item->getUUID()); +                    item->setFocus(TRUE); +                    claimEditHandler(); +                } +            } +        } +    } +} + +void LLInventoryGallery::moveDown(MASK mask) +{ +    mFilterSubString.clear(); + +    if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) +    { +        LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; +        if (item) +        { +            if (mask == MASK_NONE || mask == MASK_CONTROL) +            { +                S32 n = mItemIndexMap[item]; +                n += mItemsInRow; +                if (n < mItemsAddedCount) +                { +                    item = mIndexToItemMap[n]; +                    LLUUID item_id = item->getUUID(); +                    if (mask == MASK_CONTROL) +                    { +                        addItemSelection(item_id, true); +                    } +                    else +                    { +                        changeItemSelection(item_id, true); +                    } +                    item->setFocus(TRUE); +                    claimEditHandler(); +                } +            } +            else if (mask == MASK_SHIFT) +            { +                S32 n = mItemIndexMap[item]; +                S32 target = llmin(mItemsAddedCount - 1, n + mItemsInRow); +                if (target != n) +                { +                    item = mIndexToItemMap[target]; +                    toggleSelectionRangeFromLast(item->getUUID()); +                    item->setFocus(TRUE); +                    claimEditHandler(); +                } +            } +        } +    } +} + +void LLInventoryGallery::moveLeft(MASK mask) +{ +    mFilterSubString.clear(); + +    if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) +    { +        LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; +        if (mask == MASK_SHIFT) +        { +            item = mItemMap[mLastInteractedUUID]; +        } +        if (item) +        { +            // Might be better to get item from panel +            S32 n = mItemIndexMap[item]; +            n--; +            if (n < 0) +            { +                n = mItemsAddedCount - 1; +            } +            item = mIndexToItemMap[n]; +            LLUUID item_id = item->getUUID(); +            if (mask == MASK_CONTROL) +            { +                addItemSelection(item_id, true); +            } +            else if (mask == MASK_SHIFT) +            { +                if (item->isSelected()) +                { +                    toggleItemSelection(mLastInteractedUUID, true); +                } +                else +                { +                    toggleItemSelection(item_id, true); +                } +                mLastInteractedUUID = item_id; +            } +            else +            { +                changeItemSelection(item_id, true); +            } +            item->setFocus(TRUE); +            claimEditHandler(); +        } +    } +} + +void LLInventoryGallery::moveRight(MASK mask) +{ +    mFilterSubString.clear(); + +    if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) +    { +        LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; +        if (item) +        { +            S32 n = mItemIndexMap[item]; +            n++; +            if (n == mItemsAddedCount) +            { +                n = 0; +            } +            item = mIndexToItemMap[n]; +            LLUUID item_id = item->getUUID(); +            if (mask == MASK_CONTROL) +            { +                addItemSelection(item_id, true); +            } +            else if (mask == MASK_SHIFT) +            { +                if (item->isSelected()) +                { +                    toggleItemSelection(mLastInteractedUUID, true); +                } +                else +                { +                    toggleItemSelection(item_id, true); +                } +                mLastInteractedUUID = item_id; +            } +            else +            { +                changeItemSelection(item_id, true); +            } +            item->setFocus(TRUE); +            claimEditHandler(); +        } +    } +} + +void LLInventoryGallery::toggleSelectionRange(S32 start_idx, S32 end_idx) +{ +    LLInventoryGalleryItem* item = NULL; +    if (end_idx > start_idx) +    { +        for (S32 i = start_idx; i <= end_idx; i++) +        { +            item = mIndexToItemMap[i]; +            LLUUID item_id = item->getUUID(); +            toggleItemSelection(item_id, true); +        } +    } +    else +    { +        for (S32 i = start_idx; i >= end_idx; i--) +        { +            item = mIndexToItemMap[i]; +            LLUUID item_id = item->getUUID(); +            toggleItemSelection(item_id, true); +        } +    } +} + +void LLInventoryGallery::toggleSelectionRangeFromLast(const LLUUID target) +{ +    if (mLastInteractedUUID == target) +    { +        return; +    } +    LLInventoryGalleryItem* last_item = mItemMap[mLastInteractedUUID]; +    LLInventoryGalleryItem* next_item = mItemMap[target]; +    if (last_item && next_item) +    { +        S32 last_idx = mItemIndexMap[last_item]; +        S32 next_idx = mItemIndexMap[next_item]; +        if (next_item->isSelected()) +        { +            if (last_idx < next_idx) +            { +                toggleSelectionRange(last_idx, next_idx - 1); +            } +            else +            { +                toggleSelectionRange(last_idx, next_idx + 1); +            } +        } +        else +        { +            if (last_idx < next_idx) +            { +                toggleSelectionRange(last_idx + 1, next_idx); +            } +            else +            { +                toggleSelectionRange(last_idx - 1, next_idx); +            } +        } +    } +    mLastInteractedUUID = next_item->getUUID(); +} + +void LLInventoryGallery::onFocusLost() +{ +    // inventory no longer handles cut/copy/paste/delete +    if (gEditMenuHandler == this) +    { +        gEditMenuHandler = NULL; +    } + +    LLPanel::onFocusLost(); + +    for (const LLUUID& id : mSelectedItemIDs) +    { +        if (mItemMap[id]) +        { +            mItemMap[id]->setSelected(false); +        } +    } +} + +void LLInventoryGallery::onFocusReceived() +{ +    // inventory now handles cut/copy/paste/delete +    gEditMenuHandler = this; + +    // Tab support, when tabbing into this view, select first item +    if (mSelectedItemIDs.size() > 0) +    { +        LLInventoryGalleryItem* focus_item = NULL; +        for (const LLUUID& id : mSelectedItemIDs) +        { +            if (mItemMap[id]) +            { +                focus_item = mItemMap[id]; +                focus_item->setSelected(true); +            } +        } +        if (focus_item) +        { +            focus_item->setFocus(TRUE); +        } +    } +    else if (mIndexToItemMap.size() > 0 && mItemsToSelect.empty()) +    { +        // choose any items from visible rect +        S32 vert_offset = mScrollPanel->getDocPosVertical(); +        S32 panel_size = mVerticalGap + mRowPanelHeight; +        S32 n = llclamp((S32)(vert_offset / panel_size) * mItemsInRow, 0, (S32)(mIndexToItemMap.size() - 1) ); + +        LLInventoryGalleryItem* focus_item = mIndexToItemMap[n]; +        changeItemSelection(focus_item->getUUID(), true); +        focus_item->setFocus(TRUE); +    } + +    LLPanel::onFocusReceived(); +} + +void LLInventoryGallery::showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id) +{ +    if (mInventoryGalleryMenu && item_id.notNull()) +    { +        if (std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) == mSelectedItemIDs.end()) +        { +            changeItemSelection(item_id, false); +        } +        uuid_vec_t selected_uuids(mSelectedItemIDs.begin(), mSelectedItemIDs.end()); +        mInventoryGalleryMenu->show(ctrl, selected_uuids, x, y); +    } +} + +void LLInventoryGallery::changeItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ +    for (const LLUUID& id : mSelectedItemIDs) +    { +        if (mItemMap[id]) +        { +            mItemMap[id]->setSelected(FALSE); +        } +    } +    mSelectedItemIDs.clear(); +    mItemsToSelect.clear(); + +    if ((mItemMap.count(item_id) == 0) || mNeedsArrange) +    { +        mItemsToSelect.push_back(item_id); +        return; +    } +    if (mSelectedItemIDs.size() == 1 +        && std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) != mSelectedItemIDs.end()) +    { +        // Already selected +        mLastInteractedUUID = item_id; +        return; +    } + +    if (mItemMap[item_id]) +    { +        mItemMap[item_id]->setSelected(TRUE); +    } +    mSelectedItemIDs.push_back(item_id); +    signalSelectionItemID(item_id); +    mLastInteractedUUID = item_id; + +    if (scroll_to_selection) +    { +        scrollToShowItem(item_id); +    } +} + +void LLInventoryGallery::addItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ +    if ((mItemMap.count(item_id) == 0) || mNeedsArrange) +    { +        mItemsToSelect.push_back(item_id); +        return; +    } +    if (std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) != mSelectedItemIDs.end()) +    { +        // Already selected +        mLastInteractedUUID = item_id; +        return; +    } + +    if (mItemMap[item_id]) +    { +        mItemMap[item_id]->setSelected(TRUE); +    } +    mSelectedItemIDs.push_back(item_id); +    signalSelectionItemID(item_id); +    mLastInteractedUUID = item_id; + +    if (scroll_to_selection) +    { +        scrollToShowItem(item_id); +    } +} + +bool LLInventoryGallery::toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ +    bool result = false; +    if ((mItemMap.count(item_id) == 0) || mNeedsArrange) +    { +        mItemsToSelect.push_back(item_id); +        return result; +    } +    selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id); +    if (found != mSelectedItemIDs.end()) +    { +        if (mItemMap[item_id]) +        { +            mItemMap[item_id]->setSelected(FALSE); +        } +        mSelectedItemIDs.erase(found); +        result = false; +    } +    else +    { +        if (mItemMap[item_id]) +        { +            mItemMap[item_id]->setSelected(TRUE); +        } +        mSelectedItemIDs.push_back(item_id); +        signalSelectionItemID(item_id); +        result = true; +    } +    mLastInteractedUUID = item_id; + +    if (scroll_to_selection) +    { +        scrollToShowItem(item_id); +    } +    return result; +} + +void LLInventoryGallery::scrollToShowItem(const LLUUID& item_id) +{ +    LLInventoryGalleryItem* item = mItemMap[item_id]; +    if(item) +    { +        const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect(); + +        LLRect item_rect; +        item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel); +        LLRect overlap_rect(item_rect); +        overlap_rect.intersectWith(visible_content_rect); + +        //Scroll when the selected item is outside the visible area +        if (overlap_rect.getHeight() + 5 < item->getRect().getHeight()) +        { +            LLRect content_rect = mScrollPanel->getContentWindowRect(); +            LLRect constraint_rect; +            constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + +            LLRect item_doc_rect; +            item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel); + +            mScrollPanel->scrollToShowRect( item_doc_rect, constraint_rect ); +        } +    } +} + +LLInventoryGalleryItem* LLInventoryGallery::getFirstSelectedItem() +{ +    if (mSelectedItemIDs.size() > 0) +    { +        selection_deque::iterator iter = mSelectedItemIDs.begin(); +        return mItemMap[*iter]; +    } +    return NULL; +} + +void LLInventoryGallery::copy() +{ +    if (!getVisible() || !getEnabled()) +    { +        return; +    } + +    LLClipboard::instance().reset(); + +    for (const LLUUID& id : mSelectedItemIDs) +    { +        LLClipboard::instance().addToClipboard(id); +    } +    mFilterSubString.clear(); +} + +BOOL LLInventoryGallery::canCopy() const +{ +    if (!getVisible() || !getEnabled() || mSelectedItemIDs.empty()) +    { +        return FALSE; +    } + +    for (const LLUUID& id : mSelectedItemIDs) +    { +        if (!isItemCopyable(id)) +        { +            return FALSE; +        } +    } + +    return TRUE; +} + +void LLInventoryGallery::cut() +{ +    if (!getVisible() || !getEnabled()) +    { +        return; +    } + +    // clear the inventory clipboard +    LLClipboard::instance().reset(); +    LLClipboard::instance().setCutMode(true); +    for (const LLUUID& id : mSelectedItemIDs) +    { +        // todo: fade out selected item +        LLClipboard::instance().addToClipboard(id); +    } + +    mFilterSubString.clear(); +} + +BOOL LLInventoryGallery::canCut() const +{ +    if (!getVisible() || !getEnabled() || mSelectedItemIDs.empty()) +    { +        return FALSE; +    } + +    for (const LLUUID& id : mSelectedItemIDs) +    { +        LLViewerInventoryCategory* cat = gInventory.getCategory(id); +        if (cat) +        { +            if (!get_is_category_removable(&gInventory, id)) +            { +                return FALSE; +            } +        } +        else if (!get_is_item_removable(&gInventory, id)) +        { +            return FALSE; +        } +    } + +    return TRUE; +} + +void LLInventoryGallery::paste() +{ +    if (!LLClipboard::instance().hasContents()) +    { +        return; +    } + +    const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); +    if (mSelectedItemIDs.size() == 1 && gInventory.isObjectDescendentOf(*mSelectedItemIDs.begin(), marketplacelistings_id)) +    { +        return; +    } + +    bool is_cut_mode = LLClipboard::instance().isCutMode(); +    std::vector<LLUUID> objects; +    LLClipboard::instance().pasteFromClipboard(objects); + +    bool paste_into_root = mSelectedItemIDs.empty(); +    for (LLUUID& dest : mSelectedItemIDs) +    { +        LLInventoryObject* obj = gInventory.getObject(dest); +        if (!obj || (obj->getType() != LLAssetType::AT_CATEGORY)) +        { +            paste_into_root = true; +            continue; +        } + +        paste(dest, objects, is_cut_mode, marketplacelistings_id); +        is_cut_mode = false; +    } + +    if (paste_into_root) +    { +        for (const LLUUID& id : mSelectedItemIDs) +        { +            if (mItemMap[id]) +            { +                mItemMap[id]->setSelected(FALSE); +            } +        } +        mSelectedItemIDs.clear(); + +        paste(mFolderID, objects, is_cut_mode, marketplacelistings_id); +    } + +    LLClipboard::instance().setCutMode(false); +} + +void LLInventoryGallery::paste(const LLUUID& dest, +                               std::vector<LLUUID>& objects, +                               bool is_cut_mode, +                               const LLUUID& marketplacelistings_id) +{ +    LLHandle<LLPanel> handle = getHandle(); +    std::function <void(const LLUUID)> on_copy_callback = NULL; +    LLPointer<LLInventoryCallback> cb = NULL; +    if (dest == mFolderID) +    { +        on_copy_callback = [handle](const LLUUID& inv_item) +            { +                LLInventoryGallery* panel = (LLInventoryGallery*)handle.get(); +                if (panel) +                { +                    // Scroll to pasted item and highlight it +                    // Should it only highlight the last one? +                    panel->addItemSelection(inv_item, true); +                } +            }; +        cb = new LLBoostFuncInventoryCallback(on_copy_callback); +    } + +    for (std::vector<LLUUID>::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) +    { +        const LLUUID& item_id = (*iter); +        if (gInventory.isObjectDescendentOf(item_id, marketplacelistings_id) && (LLMarketplaceData::instance().isInActiveFolder(item_id) || +                                                                                 LLMarketplaceData::instance().isListedAndActive(item_id))) +        { +            return; +        } +        LLViewerInventoryCategory* cat = gInventory.getCategory(item_id); +        if (cat) +        { +            if (is_cut_mode) +            { +                gInventory.changeCategoryParent(cat, dest, false); +                if (dest == mFolderID) +                { +                    // Don't select immediately, wait for item to arrive +                    mItemsToSelect.push_back(item_id); +                } +            } +            else +            { +                copy_inventory_category(&gInventory, cat, dest, LLUUID::null, false, on_copy_callback); +            } +        } +        else +        { +            LLViewerInventoryItem* item = gInventory.getItem(item_id); +            if (item) +            { +                if (is_cut_mode) +                { +                    gInventory.changeItemParent(item, dest, false); +                    if (dest == mFolderID) +                    { +                        // Don't select immediately, wait for item to arrive +                        mItemsToSelect.push_back(item_id); +                    } +                } +                else +                { +                    if (item->getIsLinkType()) +                    { +                        link_inventory_object(dest, item_id, cb); +                    } +                    else +                    { +                        copy_inventory_item( +                            gAgent.getID(), +                            item->getPermissions().getOwner(), +                            item->getUUID(), +                            dest, +                            std::string(), +                            cb); +                    } +                } +            } +        } +    } + +    LLClipboard::instance().setCutMode(false); +} + +BOOL LLInventoryGallery::canPaste() const +{ +    // Return FALSE on degenerated cases: empty clipboard, no inventory, no agent +    if (!LLClipboard::instance().hasContents()) +    { +        return FALSE; +    } + +    // In cut mode, whatever is on the clipboard is always pastable +    if (LLClipboard::instance().isCutMode()) +    { +        return TRUE; +    } + +    // In normal mode, we need to check each element of the clipboard to know if we can paste or not +    std::vector<LLUUID> objects; +    LLClipboard::instance().pasteFromClipboard(objects); +    S32 count = objects.size(); +    for (S32 i = 0; i < count; i++) +    { +        const LLUUID& item_id = objects.at(i); + +        // Each item must be copyable to be pastable +        if (!isItemCopyable(item_id)) +        { +            return FALSE; +        } +    } +    return TRUE; +} + +void LLInventoryGallery::onDelete(const LLSD& notification, const LLSD& response, const selection_deque selected_ids) +{ +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +    if (option == 0) +    { +        for (const LLUUID& id : selected_ids) +        { +            LLInventoryObject* obj = gInventory.getObject(id); +            if (!obj) +            { +                return; +            } +            if (obj->getType() == LLAssetType::AT_CATEGORY) +            { +                if (get_is_category_removable(&gInventory, id)) +                { +                    gInventory.removeCategory(id); +                } +            } +            else +            { +                if (get_is_item_removable(&gInventory, id)) +                { +                    gInventory.removeItem(id); +                } +            } +        } +    } +} + +void LLInventoryGallery::deleteSelection() +{ +    if (!LLInventoryAction::sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session +    { +        LLNotifications::instance().setIgnored("DeleteItems", false); +        LLInventoryAction::sDeleteConfirmationDisplayed = true; +    } + +    LLSD args; +    args["QUESTION"] = LLTrans::getString("DeleteItem"); +    LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryGallery::onDelete, _1, _2, mSelectedItemIDs)); +} + +bool LLInventoryGallery::canDeleteSelection() +{ +    if (mSelectedItemIDs.empty()) +    { +        return false; +    } + +    const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); +    if (mFolderID == trash_id || gInventory.isObjectDescendentOf(mFolderID, trash_id)) +    { +        return false; +    } + +    for (const LLUUID& id : mSelectedItemIDs) +    { +        LLViewerInventoryCategory* cat = gInventory.getCategory(id); +        if (cat) +        { +            if (!get_is_category_removable(&gInventory, id)) +            { +                return false; +            } +        } +        else if (!get_is_item_removable(&gInventory, id)) +        { +            return false; +        } +    } + +    return true; +} + +void LLInventoryGallery::pasteAsLink() +{ +    if (!LLClipboard::instance().hasContents()) +    { +        return; +    } + +    const LLUUID& current_outfit_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); +    const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); +    const LLUUID& my_outifts_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + +    std::vector<LLUUID> objects; +    LLClipboard::instance().pasteFromClipboard(objects); + +    bool paste_into_root = mSelectedItemIDs.empty(); +    for (LLUUID& dest : mSelectedItemIDs) +    { +        LLInventoryObject* obj = gInventory.getObject(dest); +        if (!obj || obj->getType() != LLAssetType::AT_CATEGORY) +        { +            paste_into_root = true; +            continue; +        } + +        pasteAsLink(dest, objects, current_outfit_id, marketplacelistings_id, my_outifts_id); +    } + +    if (paste_into_root) +    { +        for (const LLUUID& id : mSelectedItemIDs) +        { +            if (mItemMap[id]) +            { +                mItemMap[id]->setSelected(FALSE); +            } +        } +        mSelectedItemIDs.clear(); + +        pasteAsLink(mFolderID, objects, current_outfit_id, marketplacelistings_id, my_outifts_id); +    } + +    LLClipboard::instance().setCutMode(false); +} + +void LLInventoryGallery::pasteAsLink(const LLUUID& dest, +                                     std::vector<LLUUID>& objects, +                                     const LLUUID& current_outfit_id, +                                     const LLUUID& marketplacelistings_id, +                                     const LLUUID& my_outifts_id) +{ +    const BOOL move_is_into_current_outfit = (dest == current_outfit_id); +    const BOOL move_is_into_my_outfits = (dest == my_outifts_id) || gInventory.isObjectDescendentOf(dest, my_outifts_id); +    const BOOL move_is_into_marketplacelistings = gInventory.isObjectDescendentOf(dest, marketplacelistings_id); + +    if (move_is_into_marketplacelistings || move_is_into_current_outfit || move_is_into_my_outfits) +    { +        return; +    } + +    LLPointer<LLInventoryCallback> cb = NULL; +    if (dest == mFolderID) +    { +        LLHandle<LLPanel> handle = getHandle(); +        std::function <void(const LLUUID)> on_link_callback = [handle](const LLUUID& inv_item) +            { +                LLInventoryGallery* panel = (LLInventoryGallery*)handle.get(); +                if (panel) +                { +                    // Scroll to pasted item and highlight it +                    // Should it only highlight the last one? +                    panel->addItemSelection(inv_item, true); +                } +            }; +        cb = new LLBoostFuncInventoryCallback(on_link_callback); +    } + +    for (std::vector<LLUUID>::const_iterator iter = objects.begin(); +         iter != objects.end(); +         ++iter) +    { +        const LLUUID& object_id = (*iter); +        if (LLConstPointer<LLInventoryObject> link_obj = gInventory.getObject(object_id)) +        { +            link_inventory_object(dest, link_obj, cb); +        } +    } +} + +void LLInventoryGallery::claimEditHandler() +{ +    gEditMenuHandler = this; +} + +void LLInventoryGallery::resetEditHandler() +{ +    if (gEditMenuHandler == this) +    { +        gEditMenuHandler = NULL; +    } +} + +bool LLInventoryGallery::isItemCopyable(const LLUUID & item_id) +{ +    const LLInventoryCategory* cat = gInventory.getCategory(item_id); +    if (cat) +    { +        // Folders are copyable if items in them are, recursively, copyable. +        // Get the content of the folder +        LLInventoryModel::cat_array_t* cat_array; +        LLInventoryModel::item_array_t* item_array; +        gInventory.getDirectDescendentsOf(item_id, cat_array, item_array); + +        // Check the items +        LLInventoryModel::item_array_t item_array_copy = *item_array; +        for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) +        { +            LLInventoryItem* item = *iter; +            if (!isItemCopyable(item->getUUID())) +            { +                return false; +            } +        } + +        // Check the folders +        LLInventoryModel::cat_array_t cat_array_copy = *cat_array; +        for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) +        { +            LLViewerInventoryCategory* category = *iter; +            if (!isItemCopyable(category->getUUID())) +            { +                return false; +            } +        } + +        return true; +    } + +    LLViewerInventoryItem* item = gInventory.getItem(item_id); +    if (item) +    { +        // Can't copy worn objects. +        // Worn objects are tied to their inworld conterparts +        // Copy of modified worn object will return object with obsolete asset and inventory +        if (get_is_item_worn(item_id)) +        { +            return false; +        } + +        static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); +        return (item->getIsLinkType() && inventory_linking) +            || item->getPermissions().allowCopyBy(gAgent.getID()); +    } + +    return false; +} + +void LLInventoryGallery::updateMessageVisibility() +{ + +    mMessageTextBox->setVisible(mItems.empty()); +    if(mItems.empty()) +    { +        mMessageTextBox->setText(hasDescendents(mFolderID) ? LLTrans::getString("InventorySingleFolderEmpty") : LLTrans::getString("InventorySingleFolderNoMatches")); +    } + +    mScrollPanel->setVisible(!mItems.empty()); +} + +void LLInventoryGallery::refreshList(const LLUUID& category_id) +{ +    LLInventoryModel::cat_array_t* cat_array; +    LLInventoryModel::item_array_t* item_array; + +    gInventory.getDirectDescendentsOf(category_id, cat_array, item_array); +    uuid_vec_t vadded; +    uuid_vec_t vremoved; + +    // Create added and removed items vectors. +    computeDifference(*cat_array, *item_array, vadded, vremoved); + +    // Handle added tabs. +    for (uuid_vec_t::const_iterator iter = vadded.begin(); +        iter != vadded.end(); +        ++iter) +    { +        const LLUUID cat_id = (*iter); +        updateAddedItem(cat_id); +        mNeedsArrange = true; +    } + +    // Handle removed tabs. +    for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter) +    { +        const LLUUID cat_id = (*iter); +        updateRemovedItem(cat_id); +    } + +    const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs(); +    for (LLInventoryModel::changed_items_t::const_iterator items_iter = changed_items.begin(); +        items_iter != changed_items.end(); +        ++items_iter) +    { +        LLInventoryObject* obj = gInventory.getObject(*items_iter); +        if(!obj) +        { +            return; +        } + +        updateChangedItemName(*items_iter, obj->getName()); +        mNeedsArrange = true; +    } + +    if(mNeedsArrange || !mItemsToSelect.empty()) +    { +        // Don't scroll to target/arrange immediately +        // since more updates might be pending +        gIdleCallbacks.addFunction(onIdle, (void*)this); +    } +    updateMessageVisibility(); +} + +void LLInventoryGallery::computeDifference( +    const LLInventoryModel::cat_array_t vcats, +    const LLInventoryModel::item_array_t vitems, +    uuid_vec_t& vadded, +    uuid_vec_t& vremoved) +{ +    uuid_vec_t vnew; +    // Creating a vector of newly collected UUIDs. +    for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin(); +        iter != vcats.end(); +        iter++) +    { +        vnew.push_back((*iter)->getUUID()); +    } +    for (LLInventoryModel::item_array_t::const_iterator iter = vitems.begin(); +        iter != vitems.end(); +        iter++) +    { +        vnew.push_back((*iter)->getUUID()); +    } + +    uuid_vec_t vcur; +    getCurrentCategories(vcur); +    std::copy(mItemBuildQuery.begin(), mItemBuildQuery.end(), std::back_inserter(vcur)); + +    LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved); +} + +void LLInventoryGallery::onCOFChanged() +{ +    LLInventoryModel::cat_array_t cat_array; +    LLInventoryModel::item_array_t item_array; + +    gInventory.collectDescendents( +        LLAppearanceMgr::instance().getCOF(), +        cat_array, +        item_array, +        LLInventoryModel::EXCLUDE_TRASH); + +    uuid_vec_t vnew; +    uuid_vec_t vadded; +    uuid_vec_t vremoved; + +    for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); +        iter != item_array.end(); +        ++iter) +    { +        vnew.push_back((*iter)->getLinkedUUID()); +    } + +    // We need to update only items that were added or removed from COF. +    LLCommonUtils::computeDifference(vnew, mCOFLinkedItems, vadded, vremoved); + +    mCOFLinkedItems = vnew; +     +    for (uuid_vec_t::const_iterator iter = vadded.begin(); +        iter != vadded.end(); +        ++iter) +    { +        updateWornItem(*iter, true); +    } + +    for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter) +    { +        updateWornItem(*iter, false); +    } +} + +void LLInventoryGallery::onGesturesChanged() +{ +    uuid_vec_t vnew; +    uuid_vec_t vadded; +    uuid_vec_t vremoved; + +    const LLGestureMgr::item_map_t& active_gestures = LLGestureMgr::instance().getActiveGestures(); +    for (LLGestureMgr::item_map_t::const_iterator iter = active_gestures.begin(); +        iter != active_gestures.end(); +        ++iter) +    { +        vnew.push_back(iter->first); +    } + +    LLCommonUtils::computeDifference(vnew, mActiveGestures, vadded, vremoved); + +    mActiveGestures = vnew; +     +    for (uuid_vec_t::const_iterator iter = vadded.begin(); +        iter != vadded.end(); +        ++iter) +    { +        updateWornItem(*iter, true); +    } + +    for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter) +    { +        updateWornItem(*iter, false); +    } +} + +void LLInventoryGallery::deselectItem(const LLUUID& category_id) +{ +    // Reset selection if the item is selected. +    LLInventoryGalleryItem* item = mItemMap[category_id]; +    if (item && item->isSelected()) +    { +        mItemMap[category_id]->setSelected(FALSE); +        setFocus(true); +        // Todo: support multiselect +        // signalSelectionItemID(LLUUID::null); +    } + +    selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), category_id); +    if (found != mSelectedItemIDs.end()) +    { +        mSelectedItemIDs.erase(found); +    } +} + +void LLInventoryGallery::clearSelection() +{ +    for (const LLUUID& id: mSelectedItemIDs) +    { +        if (mItemMap[id]) +        { +            mItemMap[id]->setSelected(FALSE); +        } +    } +    if (!mSelectedItemIDs.empty()) +    { +        mSelectedItemIDs.clear(); +        // BUG: wrong, item can be null +        signalSelectionItemID(LLUUID::null); +    } +} + +void LLInventoryGallery::signalSelectionItemID(const LLUUID& category_id) +{ +    mSelectionChangeSignal(category_id); +} + +boost::signals2::connection LLInventoryGallery::setSelectionChangeCallback(selection_change_callback_t cb) +{ +    return mSelectionChangeSignal.connect(cb); +} + +LLUUID LLInventoryGallery::getFirstSelectedItemID() +{ +    if (mSelectedItemIDs.size() > 0) +    { +        return *mSelectedItemIDs.begin(); +    } +    return LLUUID::null; +} + +LLUUID LLInventoryGallery::getOutfitImageID(LLUUID outfit_id) +{ +    LLUUID thumbnail_id; +    LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id); +    if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) +    { +        LLInventoryModel::cat_array_t cats; +        LLInventoryModel::item_array_t items; +        // Not LLIsOfAssetType, because we allow links +        LLIsTextureType f; +        gInventory.getDirectDescendentsOf(outfit_id, cats, items, f); + +        // Exactly one texture found => show the texture as thumbnail +        if (1 == items.size()) +        { +            LLViewerInventoryItem* item = items.front(); +            if (item && item->getIsLinkType()) +            { +                item = item->getLinkedItem(); +            } +            if (item) +            { +                thumbnail_id = item->getAssetUUID(); +            } +        } +    } +    return thumbnail_id; +} + +boost::signals2::connection LLInventoryGallery::setRootChangedCallback(callback_t cb) +{ +    return mRootChangedSignal.connect(cb); +} + +void LLInventoryGallery::onForwardFolder() +{ +    if(isForwardAvailable()) +    { +        mBackwardFolders.push_back(mFolderID); +        mFolderID = mForwardFolders.back(); +        mForwardFolders.pop_back(); +        dirtyRootFolder(); +    } +} + +void LLInventoryGallery::onBackwardFolder() +{ +    if(isBackwardAvailable()) +    { +        mForwardFolders.push_back(mFolderID); +        mFolderID = mBackwardFolders.back(); +        mBackwardFolders.pop_back(); +        dirtyRootFolder(); +    } +} + +void LLInventoryGallery::clearNavigationHistory() +{ +    mForwardFolders.clear(); +    mBackwardFolders.clear(); +} + +bool LLInventoryGallery::isBackwardAvailable() +{ +    return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back())); +} + +bool LLInventoryGallery::isForwardAvailable() +{ +    return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back())); +} + +BOOL LLInventoryGallery::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +                                           EDragAndDropType cargo_type, void* cargo_data, +                                           EAcceptance* accept, std::string& tooltip_msg) +{ +    // have children handle it first +    BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, +                                            accept, tooltip_msg); + +    // when drop is not handled by child, it should be handled by the root folder . +    if (!handled || (*accept == ACCEPT_NO)) +    { +        handled = baseHandleDragAndDrop(mFolderID, drop, cargo_type, cargo_data, accept, tooltip_msg); +    } + +    return handled; +} + +void LLInventoryGallery::startDrag() +{ +    std::vector<EDragAndDropType> types; +    uuid_vec_t ids; +    LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_AGENT; +    for (LLUUID& selected_id : mSelectedItemIDs) +    { +        const LLInventoryItem* item = gInventory.getItem(selected_id); +        if (item) +        { +            if (item->getPermissions().getOwner() == ALEXANDRIA_LINDEN_ID) +            { +                src = LLToolDragAndDrop::SOURCE_LIBRARY; +            } + +            EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(item->getType()); +            types.push_back(type); +            ids.push_back(selected_id); +        } + +        const LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id);         +        if (cat && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID()) +            && !LLFolderType::lookupIsProtectedType((cat)->getPreferredType())) +        { +            if (cat->getOwnerID() == ALEXANDRIA_LINDEN_ID) +            { +                src = LLToolDragAndDrop::SOURCE_LIBRARY; +            } + +            EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType()); +            types.push_back(type); +            ids.push_back(selected_id); +        } +    } +    LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, LLToolDragAndDrop::SOURCE_AGENT); +} + +bool LLInventoryGallery::areViewsInitialized() +{ +    return mGalleryCreated && mItemBuildQuery.empty(); +} + +bool LLInventoryGallery::hasDescendents(const LLUUID& cat_id) +{ +    LLInventoryModel::cat_array_t* cats; +    LLInventoryModel::item_array_t* items; +    gInventory.getDirectDescendentsOf(cat_id, cats, items); + +    return (cats->empty() && items->empty()); +} + +bool LLInventoryGallery::checkAgainstFilterType(const LLUUID& object_id) +{ +    const LLInventoryObject *object = gInventory.getObject(object_id); +    if(!object) return false; + +    LLInventoryType::EType object_type = LLInventoryType::IT_CATEGORY; +    LLInventoryItem* inv_item = gInventory.getItem(object_id); +    if (inv_item) +    { +        object_type = inv_item->getInventoryType(); +    } +    const U32 filterTypes = mFilter->getFilterTypes(); + +    if ((filterTypes & LLInventoryFilter::FILTERTYPE_OBJECT) && inv_item) +    { +        switch (object_type) +        { +        case LLInventoryType::IT_NONE: +            // If it has no type, pass it, unless it's a link. +            if (object && object->getIsLinkType()) +            { +                return false; +            } +            break; +        case LLInventoryType::IT_UNKNOWN: +            { +                // Unknows are only shown when we show every type. +                // Unknows are 255 and won't fit in 64 bits. +                if (mFilter->getFilterObjectTypes() != 0xffffffffffffffffULL) +                { +                    return false; +                } +                break; +            } +        default: +            if ((1LL << object_type & mFilter->getFilterObjectTypes()) == U64(0)) +            { +                return false; +            } +            break; +        } +    } +     +    if (filterTypes & LLInventoryFilter::FILTERTYPE_DATE) +    { +        const U16 HOURS_TO_SECONDS = 3600; +        time_t earliest = time_corrected() - mFilter->getHoursAgo() * HOURS_TO_SECONDS; + +        if (mFilter->getMinDate() > time_min() && mFilter->getMinDate() < earliest) +        { +            earliest = mFilter->getMinDate(); +        } +        else if (!mFilter->getHoursAgo()) +        { +            earliest = 0; +        } + +        if (LLInventoryFilter::FILTERDATEDIRECTION_NEWER == mFilter->getDateSearchDirection() || mFilter->isSinceLogoff()) +        { +            if (object->getCreationDate() < earliest || +                object->getCreationDate() > mFilter->getMaxDate()) +                return false; +        } +        else +        { +            if (object->getCreationDate() > earliest || +                object->getCreationDate() > mFilter->getMaxDate()) +                return false; +        } +    } +    return true; +} + +bool LLInventoryGallery::hasVisibleItems() +{ +    return mItemsAddedCount > 0; +} + +void LLInventoryGallery::handleModifiedFilter() +{ +    if(mFilter->isModified()) +    { +        reArrangeRows(); +    } +} + +void LLInventoryGallery::setSortOrder(U32 order, bool update) +{ +    bool dirty = (mSortOrder != order); + +    mSortOrder = order; +    if(update && dirty) +    { +        mNeedsArrange = true; +        gIdleCallbacks.addFunction(onIdle, (void*)this); +    } +} +//----------------------------- +// LLInventoryGalleryItem +//----------------------------- + +static LLDefaultChildRegistry::Register<LLInventoryGalleryItem> r("inventory_gallery_item"); + +LLInventoryGalleryItem::LLInventoryGalleryItem(const Params& p) +    : LLPanel(p), +    mSelected(false), +    mDefaultImage(true), +    mItemName(""), +    mWornSuffix(""), +    mPermSuffix(""), +    mUUID(LLUUID()), +    mIsFolder(true), +    mIsLink(false), +    mHidden(false), +    mGallery(NULL), +    mType(LLAssetType::AT_NONE), +    mSortGroup(SG_ITEM), +    mCutGeneration(0), +    mSelectedForCut(false) +{ +    buildFromFile("panel_inventory_gallery_item.xml"); +} + +LLInventoryGalleryItem::~LLInventoryGalleryItem() +{ +} + +BOOL LLInventoryGalleryItem::postBuild() +{ +    mNameText = getChild<LLTextBox>("item_name"); +    mTextBgPanel = getChild<LLPanel>("text_bg_panel"); + +    return TRUE; +} + +void LLInventoryGalleryItem::setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link) +{ +    mType = type; +    mIsFolder = (mType == LLAssetType::AT_CATEGORY); +    mIsLink = is_link; + +    std::string icon_name = LLInventoryIcon::getIconName(mType, inventory_type, flags); +    if(mIsFolder) +    { +        mSortGroup = SG_NORMAL_FOLDER; +        LLUUID folder_id = mUUID; +        if(mIsLink) +        { +            LLInventoryObject* obj = gInventory.getObject(mUUID); +            if (obj) +            { +                folder_id = obj->getLinkedUUID(); +            } +        } +        LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); +        if (cat) +        { +            LLFolderType::EType preferred_type = cat->getPreferredType(); +            icon_name = LLViewerFolderType::lookupIconName(preferred_type); + +            if (preferred_type == LLFolderType::FT_TRASH) +            { +                mSortGroup = SG_TRASH_FOLDER; +            } +            else if(LLFolderType::lookupIsProtectedType(cat->getPreferredType())) +            { +                mSortGroup = SG_SYSTEM_FOLDER; +            } +        } +    } +    else +    { +        const LLInventoryItem *item = gInventory.getItem(mUUID); +        if(item && (LLAssetType::AT_CALLINGCARD != item->getType()) && !mIsLink) +        { +            std::string delim(" --"); +            bool copy = item->getPermissions().allowCopyBy(gAgent.getID()); +            if (!copy) +            { +                mPermSuffix += delim; +                mPermSuffix += LLTrans::getString("no_copy_lbl"); +            } +            bool mod = item->getPermissions().allowModifyBy(gAgent.getID()); +            if (!mod) +            { +                mPermSuffix += mPermSuffix.empty() ? delim : ","; +                mPermSuffix += LLTrans::getString("no_modify_lbl"); +            } +            bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); +            if (!xfer) +            { +                mPermSuffix += mPermSuffix.empty() ? delim : ","; +                mPermSuffix += LLTrans::getString("no_transfer_lbl"); +            } +        } +    } + +    getChild<LLIconCtrl>("item_type")->setValue(icon_name); +    getChild<LLIconCtrl>("link_overlay")->setVisible(is_link); +} + +void LLInventoryGalleryItem::setThumbnail(LLUUID id) +{ +    mDefaultImage = id.isNull(); +    if(mDefaultImage) +    { +        getChild<LLThumbnailCtrl>("preview_thumbnail")->clearTexture(); +    } +    else +    { +        getChild<LLThumbnailCtrl>("preview_thumbnail")->setValue(id); +    } +} + +void LLInventoryGalleryItem::draw() +{ +    if (isFadeItem()) +    { +        // Fade out to indicate it's being cut +        LLViewDrawContext context(0.5f); +        LLPanel::draw(); +    } +    else +    { +        LLPanel::draw(); + +        // Draw border +        LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "MenuItemHighlightBgColor" : "TextFgTentativeColor", LLColor4::white); +        LLRect border = getChildView("preview_thumbnail")->getRect(); +        border.mRight = border.mRight + 1; +        border.mTop = border.mTop + 1; +        gl_rect_2d(border, border_color.get(), FALSE); +    } +} + +void LLInventoryGalleryItem::setItemName(std::string name) +{ +    mItemName = name; +    updateNameText(); +} + +void LLInventoryGalleryItem::setSelected(bool value) +{ +    mSelected = value; +    mTextBgPanel->setBackgroundVisible(value); + +    if(mSelected) +    { +        LLViewerInventoryItem* item = gInventory.getItem(mUUID); +        if(item && !item->isFinished()) +        { +            LLInventoryModelBackgroundFetch::instance().start(mUUID, false); +        } +    } +} + +BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask) +{ +    // call changeItemSelection directly, before setFocus +    // to avoid autoscroll from LLInventoryGallery::onFocusReceived() +    if (mask == MASK_CONTROL) +    { +        mGallery->addItemSelection(mUUID, false); +    } +    else if (mask == MASK_SHIFT) +    { +        mGallery->toggleSelectionRangeFromLast(mUUID); +    } +    else +    { +        mGallery->changeItemSelection(mUUID, false); +    } +    setFocus(TRUE); +    mGallery->claimEditHandler(); + +    gFocusMgr.setMouseCapture(this); +    S32 screen_x; +    S32 screen_y; +    localPointToScreen(x, y, &screen_x, &screen_y ); +    LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y); +    return TRUE; +} + +BOOL LLInventoryGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ +    if (!isSelected()) +    { +        mGallery->changeItemSelection(mUUID, false); +    } +    else +    { +        // refresh last interacted +        mGallery->addItemSelection(mUUID, false); +    } +    setFocus(TRUE); +    mGallery->claimEditHandler(); +    mGallery->showContextMenu(this, x, y, mUUID); + +    LLUICtrl::handleRightMouseDown(x, y, mask); +    return TRUE; +} + +BOOL LLInventoryGalleryItem::handleMouseUp(S32 x, S32 y, MASK mask) +{ +    if(hasMouseCapture()) +    { +        gFocusMgr.setMouseCapture(NULL); +        return TRUE; +    } +    return LLPanel::handleMouseUp(x, y, mask); +} + +BOOL LLInventoryGalleryItem::handleHover(S32 x, S32 y, MASK mask) +{ +    if(hasMouseCapture()) +    { +        S32 screen_x; +        S32 screen_y; +        localPointToScreen(x, y, &screen_x, &screen_y ); + +        if(LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y) && mGallery) +        { +            mGallery->startDrag(); +            return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask); +        } +    } +    return LLUICtrl::handleHover(x,y,mask); +} + +BOOL LLInventoryGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask) +{ +    if (mIsFolder && mGallery) +    { +        // setRootFolder can destroy this item. +        // Delay it until handleDoubleClick processing is complete +        // or make gallery handle doubleclicks. +        LLHandle<LLPanel> handle = mGallery->getHandle(); +        LLUUID navigate_to = mUUID; +        doOnIdleOneTime([handle, navigate_to]() +                        { +                            LLInventoryGallery* gallery = (LLInventoryGallery*)handle.get(); +                            if (gallery) +                            { +                                gallery->setRootFolder(navigate_to); +                            } +                        }); +    } +    else +    { +        LLInvFVBridgeAction::doAction(mUUID, &gInventory); +    } + +    return TRUE; +} + +BOOL LLInventoryGalleryItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +                       EDragAndDropType cargo_type, +                       void* cargo_data, +                       EAcceptance* accept, +                       std::string& tooltip_msg) +{ +    if (!mIsFolder) +    { +        return FALSE; +    } +    return mGallery->baseHandleDragAndDrop(mUUID, drop, cargo_type, cargo_data, accept, tooltip_msg); +} + +BOOL LLInventoryGalleryItem::handleKeyHere(KEY key, MASK mask) +{ +    if (!mGallery) +    { +        return FALSE; +    } + +    BOOL handled = FALSE; +    switch (key) +    { +        case KEY_LEFT: +            mGallery->moveLeft(mask); +            handled = true; +            break; + +        case KEY_RIGHT: +            mGallery->moveRight(mask); +            handled = true; +            break; + +        case KEY_UP: +            mGallery->moveUp(mask); +            handled = true; +            break; + +        case KEY_DOWN: +            mGallery->moveDown(mask); +            handled = true; +            break; + +        default: +            break; +    } +    return handled; +} + +void LLInventoryGalleryItem::onFocusLost() +{ +    // inventory no longer handles cut/copy/paste/delete +    mGallery->resetEditHandler(); + +    LLPanel::onFocusLost(); +} + +void LLInventoryGalleryItem::onFocusReceived() +{ +    // inventory now handles cut/copy/paste/delete +    mGallery->claimEditHandler(); + +    LLPanel::onFocusReceived(); +} + +void LLInventoryGalleryItem::setWorn(bool value) +{ +    mWorn = value; + +    if(mWorn) +    { +        mWornSuffix = (mType == LLAssetType::AT_GESTURE) ? LLTrans::getString("active") : LLTrans::getString("worn"); +    } +    else +    { +        mWornSuffix = ""; +    } + +    updateNameText(); +} + +LLFontGL* LLInventoryGalleryItem::getTextFont() +{ +    if(mWorn) +    { +        return LLFontGL::getFontSansSerifSmallBold(); +    } +    return mIsLink ? LLFontGL::getFontSansSerifSmallItalic() : LLFontGL::getFontSansSerifSmall(); +} + +void LLInventoryGalleryItem::updateNameText() +{ +    mNameText->setFont(getTextFont()); +    mNameText->setText(mItemName + mPermSuffix + mWornSuffix); +    mNameText->setToolTip(mItemName + mPermSuffix + mWornSuffix); +    getChild<LLThumbnailCtrl>("preview_thumbnail")->setToolTip(mItemName + mPermSuffix + mWornSuffix); +} + +bool LLInventoryGalleryItem::isFadeItem() +{ +    LLClipboard& clipboard = LLClipboard::instance(); +    if (mCutGeneration == clipboard.getGeneration()) +    { +        return mSelectedForCut; +    } + +    mCutGeneration = clipboard.getGeneration(); +    mSelectedForCut = clipboard.isCutMode() && clipboard.isOnClipboard(mUUID); +    return mSelectedForCut; +} + +//----------------------------- +// LLThumbnailsObserver +//----------------------------- + +void LLThumbnailsObserver::changed(U32 mask) +{ +    std::vector<LLUUID> deleted_ids; +    for (item_map_t::iterator iter = mItemMap.begin(); +         iter != mItemMap.end(); +         ++iter) +    { +        const LLUUID& obj_id = (*iter).first; +        LLItemData& data = (*iter).second; +         +        LLInventoryObject* obj = gInventory.getObject(obj_id); +        if (!obj) +        { +            deleted_ids.push_back(obj_id); +            continue; +        } + +        const LLUUID thumbnail_id = obj->getThumbnailUUID(); +        if (data.mThumbnailID != thumbnail_id) +        { +            data.mThumbnailID = thumbnail_id; +            data.mCallback(); +        } +    } + +    // Remove deleted items from the list +    for (std::vector<LLUUID>::iterator deleted_id = deleted_ids.begin(); deleted_id != deleted_ids.end(); ++deleted_id) +    { +        removeItem(*deleted_id); +    } +} + +bool LLThumbnailsObserver::addItem(const LLUUID& obj_id, callback_t cb) +{ +    LLInventoryObject* obj = gInventory.getObject(obj_id); +    if (obj) +    { +        mItemMap.insert(item_map_value_t(obj_id, LLItemData(obj_id, obj->getThumbnailUUID(), cb))); +        return true; +    } +    return false; +} + +void LLThumbnailsObserver::removeItem(const LLUUID& obj_id) +{ +    mItemMap.erase(obj_id); +} + +//----------------------------- +// Helper drag&drop functions +//----------------------------- + +BOOL LLInventoryGallery::baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, +                       EDragAndDropType cargo_type, +                       void* cargo_data, +                       EAcceptance* accept, +                       std::string& tooltip_msg) +{ +    LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + +    if (drop && LLToolDragAndDrop::getInstance()->getCargoIndex() == 0) +    { +        clearSelection(); +    } + +    BOOL accepted = FALSE; +    switch(cargo_type) +    { +        case DAD_TEXTURE: +        case DAD_SOUND: +        case DAD_CALLINGCARD: +        case DAD_LANDMARK: +        case DAD_SCRIPT: +        case DAD_CLOTHING: +        case DAD_OBJECT: +        case DAD_NOTECARD: +        case DAD_BODYPART: +        case DAD_ANIMATION: +        case DAD_GESTURE: +        case DAD_MESH: +        case DAD_SETTINGS: +            accepted = dragItemIntoFolder(dest_id, inv_item, drop, tooltip_msg, true); +            if (accepted && drop) +            { +                // Don't select immediately, wait for item to arrive +                mItemsToSelect.push_back(inv_item->getUUID()); +            } +            break; +        case DAD_LINK: +            // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. +            // If we have an item of AT_LINK_FOLDER type we should process the linked +            // category being dragged or dropped into folder. +            if (inv_item && LLAssetType::AT_LINK_FOLDER == inv_item->getActualType()) +            { +                LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); +                if (linked_category) +                { +                    accepted = dragCategoryIntoFolder(dest_id, (LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE); +                } +            } +            else +            { +                accepted = dragItemIntoFolder(dest_id, inv_item, drop, tooltip_msg, TRUE); +            } +            if (accepted && drop && inv_item) +            { +                mItemsToSelect.push_back(inv_item->getUUID()); +            } +            break; +        case DAD_CATEGORY: +            if (LLFriendCardsManager::instance().isAnyFriendCategory(dest_id)) +            { +                accepted = FALSE; +            } +            else +            { +                LLInventoryCategory* cat_ptr = (LLInventoryCategory*)cargo_data; +                accepted = dragCategoryIntoFolder(dest_id, cat_ptr, drop, tooltip_msg, FALSE); +                if (accepted && drop) +                { +                    mItemsToSelect.push_back(cat_ptr->getUUID()); +                } +            } +            break; +        case DAD_ROOT_CATEGORY: +        case DAD_NONE: +            break; +        default: +            LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL; +            break; +    } +    if (accepted) +    { +        *accept = ACCEPT_YES_MULTI; +    } +    else +    { +        *accept = ACCEPT_NO; +    } +    return accepted; +} + +// copy of LLFolderBridge::dragItemIntoFolder +BOOL dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm) +{ +    LLViewerInventoryCategory * cat = gInventory.getCategory(folder_id); +    if (!cat) +    { +        return FALSE; +    } +    LLInventoryModel* model = &gInventory; + +    if (!model || !inv_item) return FALSE; + +    // cannot drag into library +    if((gInventory.getRootFolderID() != folder_id) && !model->isObjectDescendentOf(folder_id, gInventory.getRootFolderID())) +    { +        return FALSE; +    } +    if (!isAgentAvatarValid()) return FALSE; + +    const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); +    const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); +    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 BOOL move_is_into_current_outfit = (folder_id == current_outfit_id); +    const BOOL move_is_into_favorites = (folder_id == favorites_id); +    const BOOL move_is_into_my_outfits = (folder_id == my_outifts_id) || model->isObjectDescendentOf(folder_id, my_outifts_id); +    const BOOL move_is_into_outfit = move_is_into_my_outfits || (cat && cat->getPreferredType()==LLFolderType::FT_OUTFIT); +    const BOOL move_is_into_landmarks = (folder_id == landmarks_id) || model->isObjectDescendentOf(folder_id, landmarks_id); +    const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(folder_id, marketplacelistings_id); +    const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id); + +    LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); +    BOOL accept = FALSE; +    LLViewerObject* object = NULL; +    if(LLToolDragAndDrop::SOURCE_AGENT == source) +    { +        const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + +        const BOOL move_is_into_trash = (folder_id == trash_id) || model->isObjectDescendentOf(folder_id, trash_id); +        const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID()); + +        //-------------------------------------------------------------------------------- +        // Determine if item can be moved. +        // + +        BOOL is_movable = TRUE; + +        switch (inv_item->getActualType()) +        { +            case LLAssetType::AT_CATEGORY: +                is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType()); +                break; +            default: +                break; +        } +        // Can't explicitly drag things out of the COF. +        if (move_is_outof_current_outfit) +        { +            is_movable = FALSE; +        } +        if (move_is_into_trash) +        { +            is_movable &= inv_item->getIsLinkType() || !get_is_item_worn(inv_item->getUUID()); +        } +        if (is_movable) +        { +            // Don't allow creating duplicates in the Calling Card/Friends +            // subfolders, see bug EXT-1599. Check is item direct descendent +            // of target folder and forbid item's movement if it so. +            // Note: isItemDirectDescendentOfCategory checks if +            // passed category is in the Calling Card/Friends folder +            is_movable &= !LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(inv_item, cat); +        } + +        // +        //-------------------------------------------------------------------------------- +         +        //-------------------------------------------------------------------------------- +        // Determine if item can be moved & dropped +        // Note: if user_confirm is false, we already went through those accept logic test and can skip them + +        accept = TRUE; + +        if (user_confirm && !is_movable) +        { +            accept = FALSE; +        } +        else if (user_confirm && (folder_id == inv_item->getParentUUID()) && !move_is_into_favorites) +        { +            accept = FALSE; +        } +        else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit)) +        { +            accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); +        } +        else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks)) +        { +            accept = can_move_to_landmarks(inv_item); +        } +        else if (user_confirm && move_is_into_marketplacelistings) +        { +            //disable dropping in or out of marketplace for now +            return FALSE; +             +            /*const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, folder_id); +            LLViewerInventoryCategory * dest_folder = cat; +            accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex());*/ +        } + +        // Check that the folder can accept this item based on folder/item type compatibility (e.g. stock folder compatibility) +        if (user_confirm && accept) +        { +            LLViewerInventoryCategory * dest_folder = cat; +            accept = dest_folder->acceptItem(inv_item); +        } +         +        LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + +        if (accept && drop) +        { +            if (inv_item->getType() == LLAssetType::AT_GESTURE +                && LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash) +            { +                LLGestureMgr::instance().deactivateGesture(inv_item->getUUID()); +            } +            // If an item is being dragged between windows, unselect everything in the active window +            // so that we don't follow the selection to its new location (which is very annoying). +                        // RN: a better solution would be to deselect automatically when an   item is moved +            // and then select any item that is dropped only in the panel that it   is dropped in +            if (active_panel) +            { +                active_panel->unSelectAll(); +            } +            // Dropping in or out of marketplace needs (sometimes) confirmation +            if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) +            { +                //disable dropping in or out of marketplace for now +                return FALSE; +            } + +            //-------------------------------------------------------------------------------- +            // Destination folder logic +            // + +            // FAVORITES folder +            // (copy the item) +            else if (move_is_into_favorites) +            { +                copy_inventory_item( +                    gAgent.getID(), +                    inv_item->getPermissions().getOwner(), +                    inv_item->getUUID(), +                    folder_id, +                    std::string(), +                    LLPointer<LLInventoryCallback>(NULL)); +            } +            // CURRENT OUTFIT or OUTFIT folder +            // (link the item) +            else if (move_is_into_current_outfit || move_is_into_outfit) +            { +                if (move_is_into_current_outfit) +                { +                    LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true); +                } +                else +                { +                    LLPointer<LLInventoryCallback> cb = NULL; +                    link_inventory_object(folder_id, LLConstPointer<LLInventoryObject>(inv_item), cb); +                } +            } +            // MARKETPLACE LISTINGS folder +            // Move the item +            else if (move_is_into_marketplacelistings) +            { +                //move_item_to_marketplacelistings(inv_item, mUUID); +                return FALSE; +            } +            // NORMAL or TRASH folder +            // (move the item, restamp if into trash) +            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))) +                { +                    set_dad_inbox_object(inv_item->getUUID()); +                } + +                gInventory.changeItemParent((LLViewerInventoryItem*)inv_item, folder_id, move_is_into_trash); +            } +             +            if (move_is_from_marketplacelistings) +            { +                // If we move from an active (listed) listing, checks that it's still valid, if not, unlist +                /*LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); +                if (version_folder_id.notNull()) +                { +                    LLMarketplaceValidator::getInstance()->validateMarketplaceListings( +                        version_folder_id, +                        [version_folder_id](bool result) +                    { +                        if (!result) +                        { +                            LLMarketplaceData::instance().activateListing(version_folder_id, false); +                        } +                    }); +                }*/ +                return FALSE; +            } + +            // +            //-------------------------------------------------------------------------------- +        } +    } +    else if (LLToolDragAndDrop::SOURCE_WORLD == source) +    { +        // Make sure the object exists. If we allowed dragging from +        // anonymous objects, it would be possible to bypass +        // permissions. +        object = gObjectList.findObject(inv_item->getParentUUID()); +        if (!object) +        { +            LL_INFOS() << "Object not found for drop." << LL_ENDL; +            return FALSE; +        } + +        // coming from a task. Need to figure out if the person can +        // move/copy this item. +        LLPermissions perm(inv_item->getPermissions()); +        bool is_move = false; +        if ((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) +            && perm.allowTransferTo(gAgent.getID()))) +            // || gAgent.isGodlike()) +        { +            accept = TRUE; +        } +        else if(object->permYouOwner()) +        { +            // If the object cannot be copied, but the object the +            // inventory is owned by the agent, then the item can be +            // moved from the task to agent inventory. +            is_move = true; +            accept = TRUE; +        } + +        // Don't allow placing an original item into Current Outfit or an outfit folder +        // because they must contain only links to wearable items. +        if (move_is_into_current_outfit || move_is_into_outfit) +        { +            accept = FALSE; +        } +        // Don't allow to move a single item to Favorites or Landmarks +        // if it is not a landmark or a link to a landmark. +        else if ((move_is_into_favorites || move_is_into_landmarks) +                 && !can_move_to_landmarks(inv_item)) +        { +            accept = FALSE; +        } +        else if (move_is_into_marketplacelistings) +        { +            tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); +            accept = FALSE; +        } + +        if (accept && drop) +        { +            boost::shared_ptr<LLMoveInv> move_inv (new LLMoveInv()); +            move_inv->mObjectID = inv_item->getParentUUID(); +            std::pair<LLUUID, LLUUID> item_pair(folder_id, inv_item->getUUID()); +            move_inv->mMoveList.push_back(item_pair); +            move_inv->mCallback = NULL; +            move_inv->mUserData = NULL; +            if(is_move) +            { +                warn_move_inventory(object, move_inv); +            } +            else +            { +                // store dad inventory item to select added one later. See EXT-4347 +                set_dad_inventory_item(inv_item, folder_id); + +                LLNotification::Params params("MoveInventoryFromObject"); +                params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv)); +                LLNotifications::instance().forceResponse(params, 0); +            } +        } +    } +    else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) +    { +        if (move_is_into_marketplacelistings) +        { +            tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); +            accept = FALSE; +        } +        else if ((inv_item->getActualType() == LLAssetType::AT_SETTINGS) && !LLEnvironment::instance().isInventoryEnabled()) +        { +            tooltip_msg = LLTrans::getString("NoEnvironmentSettings"); +            accept = FALSE; +        } +        else +        { +            // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder +            // because they must contain only links to wearable items. +            accept = !(move_is_into_current_outfit || move_is_into_outfit); +        } + +        if (accept && drop) +        { +            copy_inventory_from_notecard(folder_id,  // Drop to the chosen destination folder +                                         LLToolDragAndDrop::getInstance()->getObjectID(), +                                         LLToolDragAndDrop::getInstance()->getSourceID(), +                                         inv_item); +        } +    } +    else if(LLToolDragAndDrop::SOURCE_LIBRARY == source) +    { +        LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item; +        if(item && item->isFinished()) +        { +            accept = TRUE; + +            if (move_is_into_marketplacelistings) +            { +                tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); +                accept = FALSE; +            } +            else if (move_is_into_current_outfit || move_is_into_outfit) +            { +                accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); +            } +            // Don't allow to move a single item to Favorites or Landmarks +            // if it is not a landmark or a link to a landmark. +            else if (move_is_into_favorites || move_is_into_landmarks) +            { +                accept = can_move_to_landmarks(inv_item); +            } + +            if (accept && drop) +            { +                // FAVORITES folder +                // (copy the item) +                if (move_is_into_favorites) +                { +                    copy_inventory_item( +                        gAgent.getID(), +                        inv_item->getPermissions().getOwner(), +                        inv_item->getUUID(), +                        folder_id, +                        std::string(), +                        LLPointer<LLInventoryCallback>(NULL)); +                } +                // CURRENT OUTFIT or OUTFIT folder +                // (link the item) +                else if (move_is_into_current_outfit || move_is_into_outfit) +                { +                    if (move_is_into_current_outfit) +                    { +                        LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true); +                    } +                    else +                    { +                        LLPointer<LLInventoryCallback> cb = NULL; +                        link_inventory_object(folder_id, LLConstPointer<LLInventoryObject>(inv_item), cb); +                    } +                } +                else +                { +                    copy_inventory_item( +                        gAgent.getID(), +                        inv_item->getPermissions().getOwner(), +                        inv_item->getUUID(), +                        folder_id, +                        std::string(), +                        LLPointer<LLInventoryCallback>(NULL)); +                } +            } +        } +    } +    else +    { +        LL_WARNS() << "unhandled drag source" << LL_ENDL; +    } +    return accept; +} + +// copy of LLFolderBridge::dragCategoryIntoFolder +BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, +                            BOOL drop, std::string& tooltip_msg, BOOL is_link) +{ +    BOOL user_confirm = TRUE; +    LLInventoryModel* model = &gInventory; +    LLViewerInventoryCategory * dest_cat = gInventory.getCategory(dest_id); +    if (!dest_cat) +    { +        return FALSE; +    } + +    if (!inv_cat) return FALSE; // shouldn't happen, but in case item is incorrectly parented in which case inv_cat will be NULL + +    if (!isAgentAvatarValid()) return FALSE; +    // cannot drag into library +    if((gInventory.getRootFolderID() != dest_id) && !model->isObjectDescendentOf(dest_id, gInventory.getRootFolderID())) +    { +        return FALSE; +    } + +    const LLUUID &cat_id = inv_cat->getUUID(); +    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 = (dest_id == current_outfit_id); +    const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(dest_id, marketplacelistings_id); +    const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id); + +    // check to make sure source is agent inventory, and is represented there. +    LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); +    const BOOL is_agent_inventory = (model->getCategory(cat_id) != NULL) +        && (LLToolDragAndDrop::SOURCE_AGENT == source); + +    BOOL accept = FALSE; + +    if (is_agent_inventory) +    { +        const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); +        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 = (dest_id == trash_id) || model->isObjectDescendentOf(dest_id, trash_id); +        const BOOL move_is_into_my_outfits = (dest_id == my_outifts_id) || model->isObjectDescendentOf(dest_id, my_outifts_id); +        const BOOL move_is_into_outfit = move_is_into_my_outfits || (dest_cat && dest_cat->getPreferredType()==LLFolderType::FT_OUTFIT); +        const BOOL move_is_into_current_outfit = (dest_cat && dest_cat->getPreferredType()==LLFolderType::FT_CURRENT_OUTFIT); +        const BOOL move_is_into_landmarks = (dest_id == landmarks_id) || model->isObjectDescendentOf(dest_id, landmarks_id); +        const BOOL move_is_into_lost_and_found = model->isObjectDescendentOf(dest_id, lost_and_found_id); + +        //-------------------------------------------------------------------------------- +        // Determine if folder can be moved. +        // + +        BOOL is_movable = TRUE; + +        if (is_movable && (marketplacelistings_id == cat_id)) +        { +            is_movable = FALSE; +            tooltip_msg = LLTrans::getString("TooltipOutboxCannotMoveRoot"); +        } +        if (is_movable && move_is_from_marketplacelistings) +            //&& LLMarketplaceData::instance().getActivationState(cat_id)) +        { +            // If the incoming folder is listed and active (and is therefore either the listing or the version folder), +            // then moving is *not* allowed +            is_movable = FALSE; +            tooltip_msg = LLTrans::getString("TooltipOutboxDragActive"); +        } +        if (is_movable && (dest_id == cat_id)) +        { +            is_movable = FALSE; +            tooltip_msg = LLTrans::getString("TooltipDragOntoSelf"); +        } +        if (is_movable && (model->isObjectDescendentOf(dest_id, cat_id))) +        { +            is_movable = FALSE; +            tooltip_msg = LLTrans::getString("TooltipDragOntoOwnChild"); +        } +        if (is_movable && LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) +        { +            is_movable = FALSE; +            // tooltip? +        } + +        U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit"); +        if (is_movable && move_is_into_outfit) +        { +            if (dest_id == my_outifts_id) +            { +                if (source != LLToolDragAndDrop::SOURCE_AGENT || move_is_from_marketplacelistings) +                { +                    tooltip_msg = LLTrans::getString("TooltipOutfitNotInInventory"); +                    is_movable = false; +                } +                else if (can_move_to_my_outfits(model, inv_cat, max_items_to_wear)) +                { +                    is_movable = true; +                } +                else +                { +                    tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit"); +                    is_movable = false; +                } +            } +            else if(dest_cat && dest_cat->getPreferredType() == LLFolderType::FT_NONE) +            { +                is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)); +            } +            else +            { +                is_movable = false; +            } +        } +        if(is_movable && move_is_into_current_outfit && is_link) +        { +            is_movable = FALSE; +        } +        if (is_movable && move_is_into_lost_and_found) +        { +            is_movable = FALSE; +        } +        if (is_movable && (dest_id == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE))) +        { +            is_movable = FALSE; +            // tooltip? +        } +        if (is_movable && (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)) +        { +            // One cannot move a folder into a stock folder +            is_movable = FALSE; +            // tooltip? +        } +         +        LLInventoryModel::cat_array_t descendent_categories; +        LLInventoryModel::item_array_t descendent_items; +        if (is_movable) +        { +            model->collectDescendents(cat_id, descendent_categories, descendent_items, FALSE); +            for (S32 i=0; i < descendent_categories.size(); ++i) +            { +                LLInventoryCategory* category = descendent_categories[i]; +                if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) +                { +                    // Can't move "special folders" (e.g. Textures Folder). +                    is_movable = FALSE; +                    break; +                } +            } +        } +        if (is_movable +            && move_is_into_current_outfit +            && descendent_items.size() > max_items_to_wear) +        { +            LLInventoryModel::cat_array_t cats; +            LLInventoryModel::item_array_t items; +            LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); +            gInventory.collectDescendentsIf(cat_id, +                cats, +                items, +                LLInventoryModel::EXCLUDE_TRASH, +                not_worn); + +            if (items.size() > max_items_to_wear) +            { +                // Can't move 'large' folders into current outfit: MAINT-4086 +                is_movable = FALSE; +                LLStringUtil::format_map_t args; +                args["AMOUNT"] = llformat("%d", max_items_to_wear); +                tooltip_msg = LLTrans::getString("TooltipTooManyWearables",args); +            } +        } +        if (is_movable && move_is_into_trash) +        { +            for (S32 i=0; i < descendent_items.size(); ++i) +            { +                LLInventoryItem* item = descendent_items[i]; +                if (get_is_item_worn(item->getUUID())) +                { +                    is_movable = FALSE; +                    break; // It's generally movable, but not into the trash. +                } +            } +        } +        if (is_movable && move_is_into_landmarks) +        { +            for (S32 i=0; i < descendent_items.size(); ++i) +            { +                LLViewerInventoryItem* item = descendent_items[i]; + +                // Don't move anything except landmarks and categories into Landmarks folder. +                // We use getType() instead of getActua;Type() to allow links to landmarks and folders. +                if (LLAssetType::AT_LANDMARK != item->getType() && LLAssetType::AT_CATEGORY != item->getType()) +                { +                    is_movable = FALSE; +                    break; // It's generally movable, but not into Landmarks. +                } +            } +        } +         +        if (is_movable && move_is_into_marketplacelistings) +        { +            const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, dest_id); +            LLViewerInventoryCategory * dest_folder = dest_cat; +            S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount()); +            is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size); +        } + +        // +        //-------------------------------------------------------------------------------- + +        accept = is_movable; + +        if (accept && drop) +        { +            // Dropping in or out of marketplace needs (sometimes) confirmation +            if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) +            { +                //disable dropping in or out of marketplace for now +                return FALSE; +            } +            // Look for any gestures and deactivate them +            if (move_is_into_trash) +            { +                for (S32 i=0; i < descendent_items.size(); i++) +                { +                    LLInventoryItem* item = descendent_items[i]; +                    if (item->getType() == LLAssetType::AT_GESTURE +                        && LLGestureMgr::instance().isGestureActive(item->getUUID())) +                    { +                        LLGestureMgr::instance().deactivateGesture(item->getUUID()); +                    } +                } +            } + +            if (dest_id == my_outifts_id) +            { +                // Category can contains objects, +                // create a new folder and populate it with links to original objects +                dropToMyOutfits(inv_cat); +            } +            // if target is current outfit folder we use link +            else if (move_is_into_current_outfit && +                (inv_cat->getPreferredType() == LLFolderType::FT_NONE || +                inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)) +            { +                // traverse category and add all contents to currently worn. +                BOOL append = true; +                LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append); +            } +            else if (move_is_into_marketplacelistings) +            { +                //move_folder_to_marketplacelistings(inv_cat, dest_id); +            } +            else +            { +                if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX))) +                { +                    set_dad_inbox_object(cat_id); +                } + +                // Reparent the folder and restamp children if it's moving +                // into trash. +                gInventory.changeCategoryParent( +                    (LLViewerInventoryCategory*)inv_cat, +                    dest_id, +                    move_is_into_trash); +            } +            if (move_is_from_marketplacelistings) +            { +                //disable dropping in or out of marketplace for now +                return FALSE; +                 +                // If we are moving a folder at the listing folder level (i.e. its parent is the marketplace listings folder) +                /*if (from_folder_uuid == marketplacelistings_id) +                { +                    // Clear the folder from the marketplace in case it is a listing folder +                    if (LLMarketplaceData::instance().isListed(cat_id)) +                    { +                        LLMarketplaceData::instance().clearListing(cat_id); +                    } +                } +                else +                { +                    // If we move from within an active (listed) listing, checks that it's still valid, if not, unlist +                    LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); +                    if (version_folder_id.notNull()) +                    { +                        LLMarketplaceValidator::getInstance()->validateMarketplaceListings( +                            version_folder_id, +                            [version_folder_id](bool result) +                        { +                            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); +                }*/ +            } +        } +    } +    else if (LLToolDragAndDrop::SOURCE_WORLD == source) +    { +        if (move_is_into_marketplacelistings) +        { +            tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); +            accept = FALSE; +        } +        else +        { +            accept = move_inv_category_world_to_agent(cat_id, dest_id, drop); +        } +    } +    else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) +    { +        if (move_is_into_marketplacelistings) +        { +            tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); +            accept = FALSE; +        } +        else +        { +            // Accept folders that contain complete outfits. +            accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(cat_id); +        } + +        if (accept && drop) +        { +            LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, true, false); +        } +    } + +    return accept; +} + +void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id) +{ +    LLInventoryModel::cat_array_t* categories; +    LLInventoryModel::item_array_t* items; +    gInventory.getDirectDescendentsOf(cat_source_id, categories, items); + +    LLInventoryObject::const_object_list_t link_array; + + +    LLInventoryModel::item_array_t::iterator iter = items->begin(); +    LLInventoryModel::item_array_t::iterator end = items->end(); +    while (iter!=end) +    { +        const LLViewerInventoryItem* item = (*iter); +        // By this point everything is supposed to be filtered, +        // but there was a delay to create folder so something could have changed +        LLInventoryType::EType inv_type = item->getInventoryType(); +        if ((inv_type == LLInventoryType::IT_WEARABLE) || +            (inv_type == LLInventoryType::IT_GESTURE) || +            (inv_type == LLInventoryType::IT_ATTACHMENT) || +            (inv_type == LLInventoryType::IT_OBJECT) || +            (inv_type == LLInventoryType::IT_SNAPSHOT) || +            (inv_type == LLInventoryType::IT_TEXTURE)) +        { +            link_array.push_back(LLConstPointer<LLInventoryObject>(item)); +        } +        iter++; +    } + +    if (!link_array.empty()) +    { +        LLPointer<LLInventoryCallback> cb = NULL; +        link_inventory_array(cat_dest_id, link_array, cb); +    } +} + +void dropToMyOutfits(LLInventoryCategory* inv_cat) +{ +    // make a folder in the My Outfits directory. +    const LLUUID dest_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + +    // Note: creation will take time, so passing folder id to callback is slightly unreliable, +    // but so is collecting and passing descendants' ids +    inventory_func_type func = boost::bind(&outfitFolderCreatedCallback, inv_cat->getUUID(), _1); +    gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), func, inv_cat->getThumbnailUUID()); +} + diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h new file mode 100644 index 0000000000..9b3f12701f --- /dev/null +++ b/indra/newview/llinventorygallery.h @@ -0,0 +1,419 @@ +/** + * @file llinventorygallery.h + * @brief LLInventoryGallery class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLINVENTORYGALLERY_H +#define LL_LLINVENTORYGALLERY_H + +#include "llgesturemgr.h" +#include "lllistcontextmenu.h" +#include "llpanel.h" +#include "llinventoryfilter.h" +#include "llinventoryobserver.h" +#include "llinventorymodel.h" + +class LLInventoryCategoriesObserver; +class LLInventoryGalleryItem; +class LLScrollContainer; +class LLTextBox; +class LLThumbnailsObserver; +class LLGalleryGestureObserver; + +class LLInventoryGalleryContextMenu; + +typedef boost::function<void()> callback_t; + +class LLInventoryGallery : public LLPanel, public LLEditMenuHandler +{ +public: + +    typedef boost::signals2::signal<void(const LLUUID&)> selection_change_signal_t; +    typedef boost::function<void(const LLUUID&)> selection_change_callback_t; +    typedef std::deque<LLUUID> selection_deque; + +    struct Params +        : public LLInitParam::Block<Params, LLPanel::Params> +    { +        Optional<S32>   row_panel_height; +        Optional<S32>   row_panel_width_factor; +        Optional<S32>   gallery_width_factor; +        Optional<S32>   vertical_gap; +        Optional<S32>   horizontal_gap; +        Optional<S32>   item_width; +        Optional<S32>   item_height; +        Optional<S32>   item_horizontal_gap; +        Optional<S32>   items_in_row; + +        Params(); +    }; + +    static const LLInventoryGallery::Params& getDefaultParams(); + +    LLInventoryGallery(const LLInventoryGallery::Params& params = getDefaultParams()); +    ~LLInventoryGallery(); + +    BOOL postBuild() override; +    void initGallery(); +    void draw() override; +    void onVisibilityChange(BOOL new_visibility) override; +    BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, +                           void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) override; +    void startDrag(); +    BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; +    BOOL handleKeyHere(KEY key, MASK mask) override; +    void moveUp(MASK mask); +    void moveDown(MASK mask); +    void moveLeft(MASK mask); +    void moveRight(MASK mask); +    void toggleSelectionRange(S32 start_idx, S32 end_idx); +    void toggleSelectionRangeFromLast(const LLUUID target); + +    void onFocusLost() override; +    void onFocusReceived() override; + +    void setFilterSubString(const std::string& string); +    std::string getFilterSubString() { return mFilterSubString; } +    LLInventoryFilter& getFilter() const { return *mFilter; } +    bool checkAgainstFilterType(const LLUUID& object_id); + +    void getCurrentCategories(uuid_vec_t& vcur); +    bool updateAddedItem(LLUUID item_id); // returns true if added item is visible +    void updateRemovedItem(LLUUID item_id); +    void updateChangedItemName(LLUUID item_id, std::string name); +    void updateItemThumbnail(LLUUID item_id); +    void updateWornItem(LLUUID item_id, bool is_worn); + +    void updateMessageVisibility(); + +    void setRootFolder(const LLUUID cat_id); +    void updateRootFolder(); +    LLUUID getRootFolder() { return mFolderID; } +    bool isRootDirty() { return mRootDirty; } +    boost::signals2::connection setRootChangedCallback(callback_t cb); +    void onForwardFolder(); +    void onBackwardFolder(); +    void clearNavigationHistory(); +    bool isBackwardAvailable(); +    bool isForwardAvailable(); + +    void setNavBackwardList(std::list<LLUUID> backward_list) { mBackwardFolders = backward_list; } +    void setNavForwardList(std::list<LLUUID> forward_list) { mForwardFolders = forward_list; } +    std::list<LLUUID> getNavBackwardList() { return mBackwardFolders; } +    std::list<LLUUID> getNavForwardList() { return mForwardFolders; } + +    LLUUID getOutfitImageID(LLUUID outfit_id); + +    void refreshList(const LLUUID& category_id); +    void onCOFChanged(); +    void onGesturesChanged(); +    void computeDifference(const LLInventoryModel::cat_array_t vcats, const LLInventoryModel::item_array_t vitems, uuid_vec_t& vadded, uuid_vec_t& vremoved); + +    void deselectItem(const LLUUID& category_id); +    void clearSelection(); +    void changeItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); +    void addItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); +    bool toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); +    void scrollToShowItem(const LLUUID& item_id); +    void signalSelectionItemID(const LLUUID& category_id); +    boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb); +    LLUUID getFirstSelectedItemID(); + +    void setSearchType(LLInventoryFilter::ESearchType type); +    LLInventoryFilter::ESearchType getSearchType() { return mSearchType; } + +    bool areViewsInitialized(); +    bool hasDescendents(const LLUUID& cat_id); +    bool hasVisibleItems(); +    void handleModifiedFilter(); +    LLScrollContainer* getScrollableContainer() { return mScrollPanel; } +    LLInventoryGalleryItem* getFirstSelectedItem(); + +    // Copy & paste (LLEditMenuHandler) +    void	copy() override; +    BOOL	canCopy() const override; + +    void	cut() override; +    BOOL	canCut() const override; + +    void paste() override; +    BOOL canPaste() const override; + +    // Copy & paste & delete +    static void onDelete(const LLSD& notification, const LLSD& response, const selection_deque selected_ids); +    void deleteSelection(); +    bool canDeleteSelection(); +    void pasteAsLink(); + +    void setSortOrder(U32 order, bool update = false); +    U32 getSortOrder() { return mSortOrder; }; + +    void claimEditHandler(); +    void resetEditHandler(); +    static bool isItemCopyable(const LLUUID & item_id); + +    BOOL baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, EDragAndDropType cargo_type, +                               void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); + +    void showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id); + +protected: +    void paste(const LLUUID& dest, +               std::vector<LLUUID>& objects, +               bool is_cut_mode, +               const LLUUID& marketplacelistings_id); +    void pasteAsLink(const LLUUID& dest, +                     std::vector<LLUUID>& objects, +                     const LLUUID& current_outfit_id, +                     const LLUUID& marketplacelistings_id, +                     const LLUUID& my_outifts_id); + +    bool applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring); +    bool checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring); +    static void onIdle(void* userdata); +    void dirtyRootFolder(); + +    LLInventoryCategoriesObserver*     mCategoriesObserver; +    LLThumbnailsObserver*              mThumbnailsObserver; +    LLGalleryGestureObserver*          mGestureObserver; +    LLInventoryObserver*               mInventoryObserver; +    selection_deque                    mSelectedItemIDs; +    selection_deque                    mItemsToSelect; +    LLUUID                             mLastInteractedUUID; +    bool                               mIsInitialized; +    bool                               mRootDirty; + +    selection_change_signal_t        mSelectionChangeSignal; +    boost::signals2::signal<void()>  mRootChangedSignal; +    LLUUID mFolderID; +    std::list<LLUUID> mBackwardFolders; +    std::list<LLUUID> mForwardFolders; + +private: +    void addToGallery(LLInventoryGalleryItem* item); +    void removeFromGalleryLast(LLInventoryGalleryItem* item, bool needs_reshape = true); +    void removeFromGalleryMiddle(LLInventoryGalleryItem* item); +    LLPanel* addLastRow(); +    void removeLastRow(); +    void moveRowUp(int row); +    void moveRowDown(int row); +    void moveRow(int row, int pos); +    LLPanel* addToRow(LLPanel* row_stack, LLInventoryGalleryItem* item, int pos, int hgap); +    void removeFromLastRow(LLInventoryGalleryItem* item); +    void reArrangeRows(S32 row_diff = 0); +    bool updateRowsIfNeeded(); +    void updateGalleryWidth(); + +    LLInventoryGalleryItem* buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn); + +    void buildGalleryPanel(int row_count); +    void reshapeGalleryPanel(int row_count); +    LLPanel* buildItemPanel(int left); +    LLPanel* buildRowPanel(int left, int bottom); +    void moveRowPanel(LLPanel* stack, int left, int bottom); + +    std::vector<LLPanel*> mRowPanels; +    std::vector<LLPanel*> mItemPanels; +    std::vector<LLPanel*> mUnusedRowPanels; +    std::vector<LLPanel*> mUnusedItemPanels; +    std::vector<LLInventoryGalleryItem*> mItems; +    std::vector<LLInventoryGalleryItem*> mHiddenItems; +    LLScrollContainer* mScrollPanel; +    LLPanel* mGalleryPanel; +    LLPanel* mLastRowPanel; +    LLTextBox* mMessageTextBox; +    int mRowCount; +    int mItemsAddedCount; +    bool mGalleryCreated; +    bool mNeedsArrange; + +    /* Params */ +    int mRowPanelHeight; +    int mVerticalGap; +    int mHorizontalGap; +    int mItemWidth; +    int mItemHeight; +    int mItemHorizontalGap; +    int mItemsInRow; +    int mRowPanelWidth; +    int mGalleryWidth; +    int mRowPanWidthFactor; +    int mGalleryWidthFactor; + +    LLInventoryGalleryContextMenu* mInventoryGalleryMenu; +    LLInventoryGalleryContextMenu* mRootGalleryMenu; +    std::string mFilterSubString; +    LLInventoryFilter* mFilter; +    U32 mSortOrder; + +    typedef std::map<LLUUID, LLInventoryGalleryItem*> gallery_item_map_t; +    gallery_item_map_t mItemMap; +    uuid_vec_t mCOFLinkedItems; +    uuid_vec_t mActiveGestures; +    uuid_set_t mItemBuildQuery; +    std::map<LLInventoryGalleryItem*, S32> mItemIndexMap; +    std::map<S32, LLInventoryGalleryItem*> mIndexToItemMap; + +    LLInventoryFilter::ESearchType mSearchType; +    std::string mUsername; +}; + +class LLInventoryGalleryItem : public LLPanel +{ +public: +    struct Params : public LLInitParam::Block<Params, LLPanel::Params> +    {}; + +    enum EInventorySortGroup +    { +        SG_SYSTEM_FOLDER, +        SG_TRASH_FOLDER, +        SG_NORMAL_FOLDER, +        SG_ITEM +    }; + +    LLInventoryGalleryItem(const Params& p); +    virtual ~LLInventoryGalleryItem(); + +    BOOL postBuild(); +    void draw(); +    BOOL handleMouseDown(S32 x, S32 y, MASK mask); +    BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); +    BOOL handleDoubleClick(S32 x, S32 y, MASK mask); +    BOOL handleMouseUp(S32 x, S32 y, MASK mask); +    BOOL handleHover(S32 x, S32 y, MASK mask); +    BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +                                   EDragAndDropType cargo_type, +                                   void* cargo_data, +                                   EAcceptance* accept, +                                   std::string& tooltip_msg); +    BOOL handleKeyHere(KEY key, MASK mask); + +    void onFocusLost(); +    void onFocusReceived(); + +    LLFontGL* getTextFont(); + +    void setItemName(std::string name); +    bool isSelected() { return mSelected; } +    void setSelected(bool value); +    void setWorn(bool value); +    void setUUID(LLUUID id) {mUUID = id;} +    LLUUID getUUID() { return mUUID;} + +    void setAssetIDStr(std::string asset_id) {mAssetIDStr = asset_id;} +    std::string getAssetIDStr() { return mAssetIDStr;} +    void setDescription(std::string desc) {mDesc = desc;} +    std::string getDescription() { return mDesc;} +    void setCreatorName(std::string name) {mCreatorName = name;} +    std::string getCreatorName() { return mCreatorName;} +    void setCreationDate(time_t date) {mCreationDate = date;} +    time_t getCreationDate() { return mCreationDate;} + +    std::string getItemName() {return mItemName;} +    std::string getItemNameSuffix() {return mPermSuffix + mWornSuffix;} +    bool isDefaultImage() {return mDefaultImage;} +     +    bool isHidden() {return mHidden;} +    void setHidden(bool hidden) {mHidden = hidden;} + +    void setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link); +    LLAssetType::EType getAssetType() { return mType; } +    void setThumbnail(LLUUID id); +    void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; } +    bool isFolder() { return mIsFolder; } +    bool isLink() { return mIsLink; } +    EInventorySortGroup getSortGroup() { return mSortGroup; } + +    void updateNameText(); +     +private: +    bool isFadeItem(); + +    LLUUID mUUID; +    LLTextBox* mNameText; +    LLPanel* mTextBgPanel; +    bool     mSelected; +    bool     mWorn; +    bool     mDefaultImage; +    bool     mHidden; +    bool     mIsFolder; +    bool     mIsLink; +    S32      mCutGeneration; +    bool     mSelectedForCut; + +    std::string mAssetIDStr; +    std::string mDesc; +    std::string mCreatorName; +    time_t mCreationDate; + +    EInventorySortGroup mSortGroup; +    LLAssetType::EType mType; +    std::string mItemName; +    std::string mWornSuffix; +    std::string mPermSuffix; +    LLInventoryGallery* mGallery; +}; + +class LLThumbnailsObserver : public LLInventoryObserver +{ +public: +    LLThumbnailsObserver(){}; + +    virtual void changed(U32 mask); +    bool addItem(const LLUUID& obj_id, callback_t cb); +    void removeItem(const LLUUID& obj_id); + +protected: + +    struct LLItemData +    { +        LLItemData(const LLUUID& obj_id, const LLUUID& thumbnail_id, callback_t cb) +            : mItemID(obj_id) +            , mCallback(cb) +            , mThumbnailID(thumbnail_id) +        {} + +        callback_t mCallback; +        LLUUID mItemID; +        LLUUID mThumbnailID; +    }; + +    typedef std::map<LLUUID, LLItemData> item_map_t; +    typedef item_map_t::value_type item_map_value_t; +    item_map_t mItemMap; +}; + +class LLGalleryGestureObserver : public LLGestureManagerObserver +{ +public: +    LLGalleryGestureObserver(LLInventoryGallery* gallery) : mGallery(gallery) {} +    virtual ~LLGalleryGestureObserver() {} +    virtual void changed() { mGallery->onGesturesChanged(); } + +private: +    LLInventoryGallery* mGallery; +}; + +#endif diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp new file mode 100644 index 0000000000..5f4b816b99 --- /dev/null +++ b/indra/newview/llinventorygallerymenu.cpp @@ -0,0 +1,714 @@ +/** + * @file llinventorygallerymenu.cpp + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventorygallery.h" +#include "llinventorygallerymenu.h" + +#include "llagent.h" +#include "llappearancemgr.h" +#include "llavataractions.h" +#include "llclipboard.h" +#include "llfloaterreg.h" +#include "llfloatersidepanelcontainer.h" +#include "llfloaterworldmap.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "lllandmarkactions.h" +#include "llmarketplacefunctions.h" +#include "llmenugl.h" +#include "llnotificationsutil.h" +#include "llpreviewtexture.h" +#include "lltrans.h" +#include "llviewerfoldertype.h" +#include "llviewerwindow.h" +#include "llvoavatarself.h" + +LLContextMenu* LLInventoryGalleryContextMenu::createMenu() +{ +    LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; +    LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + +    registrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryGalleryContextMenu::doToSelected, this, _2)); +    registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2)); +    registrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); +    registrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); + +    std::set<LLUUID> uuids(mUUIDs.begin(), mUUIDs.end()); +    registrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, uuids, gFloaterView->getParentFloater(mGallery))); + +    enable_registrar.add("Inventory.CanSetUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::canSetUploadLocation, this, _2)); +     +    LLContextMenu* menu = createFromFile("menu_gallery_inventory.xml"); + +    updateMenuItemsVisibility(menu); + +    return menu; +} + +void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata) +{ +    std::string action = userdata.asString(); +    LLInventoryObject* obj = gInventory.getObject(mUUIDs.front()); +    if(!obj) return; + +    if ("open_selected_folder" == action) +    { +        mGallery->setRootFolder(mUUIDs.front()); +    } +    else if ("open_in_new_window" == action) +    { +        new_folder_window(mUUIDs.front()); +    } +    else if ("properties" == action) +    { +        show_item_profile(mUUIDs.front()); +    } +    else if ("restore" == action) +    { +        for (LLUUID& selected_id : mUUIDs) +        { +            LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); +            if (cat) +            { +                const LLUUID new_parent = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType())); +                // do not restamp children on restore +                gInventory.changeCategoryParent(cat, new_parent, false); +            } +            else +            { +                LLViewerInventoryItem* item = gInventory.getItem(selected_id); +                if (item) +                { +                    bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT); + +                    const LLUUID new_parent = gInventory.findCategoryUUIDForType(is_snapshot ? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType())); +                    // do not restamp children on restore +                    gInventory.changeItemParent(item, new_parent, false); +                } +            } +        } +    } +    else if ("copy_uuid" == action) +    { +        LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front()); +        if(item) +        { +            LLUUID asset_id = item->getProtectedAssetUUID(); +            std::string buffer; +            asset_id.toString(buffer); + +            gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer)); +        } +    } +    else if ("purge" == action) +    { +        for (LLUUID& selected_id : mUUIDs) +        { +            remove_inventory_object(selected_id, NULL); +        } +    } +    else if ("goto" == action) +    { +        show_item_original(mUUIDs.front()); +    } +    else if ("thumbnail" == action) +    { +        LLSD data(mUUIDs.front()); +        LLFloaterReg::showInstance("change_item_thumbnail", data); +    } +    else if ("cut" == action) +    { +        if (mGallery->canCut()) +        { +            mGallery->cut(); +        } +    } +    else if ("paste" == action) +    { +        if (mGallery->canPaste()) +        { +            mGallery->paste(); +        } +    } +    else if ("delete" == action) +    { +        mGallery->deleteSelection(); +    } +    else if ("copy" == action) +    { +        if (mGallery->canCopy()) +        { +            mGallery->copy(); +        } +    } +    else if ("paste_link" == action) +    { +        mGallery->pasteAsLink(); +    } +    else if ("rename" == action) +    { +        rename(mUUIDs.front()); +    } +    else if ("open" == action || "open_original" == action) +    { +        LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front()); +        if (item) +        { +            LLInvFVBridgeAction::doAction(item->getType(), mUUIDs.front(), &gInventory); +        } +    } +    else if ("ungroup_folder_items" == action) +    { +        ungroup_folder_items(mUUIDs.front()); +    } +    else if ("take_off" == action || "detach" == action) +    { +        for (LLUUID& selected_id : mUUIDs) +        { +            LLAppearanceMgr::instance().removeItemFromAvatar(selected_id); +        } +    } +    else if ("wear_add" == action) +    { +        for (LLUUID& selected_id : mUUIDs) +        { +            LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, false); // Don't replace if adding. +        } +    } +    else if ("wear" == action) +    { +        for (LLUUID& selected_id : mUUIDs) +        { +            LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, true); +        } +    } +    else if ("activate" == action) +    { +        for (LLUUID& selected_id : mUUIDs) +        { +            LLGestureMgr::instance().activateGesture(selected_id); + +            LLViewerInventoryItem* item = gInventory.getItem(selected_id); +            if (!item) return; + +            gInventory.updateItem(item); +        } +        gInventory.notifyObservers(); +    } +    else if ("deactivate" == action) +    { +        for (LLUUID& selected_id : mUUIDs) +        { +            LLGestureMgr::instance().deactivateGesture(selected_id); + +            LLViewerInventoryItem* item = gInventory.getItem(selected_id); +            if (!item) return; + +            gInventory.updateItem(item); +        } +        gInventory.notifyObservers(); +    } +    else if ("replace_links" == action) +    { +        LLFloaterReg::showInstance("linkreplace", LLSD(mUUIDs.front())); +    } +    else if ("copy_slurl" == action) +    { +        boost::function<void(LLLandmark*)> copy_slurl_cb = [](LLLandmark* landmark) +        { +            LLVector3d global_pos; +            landmark->getGlobalPos(global_pos); +            boost::function<void(std::string& slurl)> copy_slurl_to_clipboard_cb = [](const std::string& slurl) +            { +               gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); +               LLSD args; +               args["SLURL"] = slurl; +               LLNotificationsUtil::add("CopySLURL", args); +            }; +            LLLandmarkActions::getSLURLfromPosGlobal(global_pos, copy_slurl_to_clipboard_cb, true); +        }; +        LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), copy_slurl_cb); +        if (landmark) +        { +            copy_slurl_cb(landmark); +        } +    } +    else if ("about" == action) +    { +        LLSD key; +        key["type"] = "landmark"; +        key["id"] = mUUIDs.front(); +        LLFloaterSidePanelContainer::showPanel("places", key); +    } +    else if ("show_on_map" == action) +    { +        boost::function<void(LLLandmark*)> show_on_map_cb = [](LLLandmark* landmark) +        { +            LLVector3d landmark_global_pos; +            if (landmark->getGlobalPos(landmark_global_pos)) +            { +                LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); +                if (!landmark_global_pos.isExactlyZero() && worldmap_instance) +                { +                    worldmap_instance->trackLocation(landmark_global_pos); +                    LLFloaterReg::showInstance("world_map", "center"); +                } +            } +        }; +        LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), show_on_map_cb); +        if(landmark) +        { +            show_on_map_cb(landmark); +        } +    } +    else if ("save_as" == action) +    { +        LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance<LLPreviewTexture>("preview_texture", mUUIDs.front()); +        if (preview_texture) +        { +            preview_texture->openToSave(); +            preview_texture->saveAs(); +        } +    } +} + +void LLInventoryGalleryContextMenu::rename(const LLUUID& item_id) +{ +    LLInventoryObject* obj = gInventory.getObject(item_id); +    if (!obj) return; + +    LLSD args; +    args["NAME"] = obj->getName(); + +    LLSD payload; +    payload["id"] = mUUIDs.front(); + +    LLNotificationsUtil::add("RenameItem", args, payload, boost::bind(onRename, _1, _2)); +} + +void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLSD& response) +{ +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +    if (option != 0) return; // canceled + +    std::string new_name = response["new_name"].asString(); +    LLStringUtil::trim(new_name); +    if (!new_name.empty()) +    { +        LLUUID id = notification["payload"]["id"].asUUID(); +         +        LLViewerInventoryCategory* cat = gInventory.getCategory(id); +        if(cat && (cat->getName() != new_name)) +        { +            LLSD updates; +            updates["name"] = new_name; +            update_inventory_category(cat->getUUID(),updates, NULL); +            return; +        } +         +        LLViewerInventoryItem* item = gInventory.getItem(id); +        if(item && (item->getName() != new_name)) +        { +            LLSD updates; +            updates["name"] = new_name; +            update_inventory_item(item->getUUID(),updates, NULL); +        } +    } +} + +void LLInventoryGalleryContextMenu::fileUploadLocation(const LLSD& userdata) +{ +    const std::string param = userdata.asString(); +    if (param == "model") +    { +        gSavedPerAccountSettings.setString("ModelUploadFolder", mUUIDs.front().asString()); +    } +    else if (param == "texture") +    { +        gSavedPerAccountSettings.setString("TextureUploadFolder", mUUIDs.front().asString()); +    } +    else if (param == "sound") +    { +        gSavedPerAccountSettings.setString("SoundUploadFolder", mUUIDs.front().asString()); +    } +    else if (param == "animation") +    { +        gSavedPerAccountSettings.setString("AnimationUploadFolder", mUUIDs.front().asString()); +    } +} + +bool LLInventoryGalleryContextMenu::canSetUploadLocation(const LLSD& userdata) +{ +    if (mUUIDs.size() != 1) +    { +        return false; +    } +    LLInventoryCategory* cat = gInventory.getCategory(mUUIDs.front()); +    if (!cat) +    { +        return false; +    } +    return true; +} + +bool is_inbox_folder(LLUUID item_id) +{ +    const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); +     +    if (inbox_id.isNull()) +    { +        return false; +    } +     +    return gInventory.isObjectDescendentOf(item_id, inbox_id); +} + +void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu) +{ +    LLUUID selected_id = mUUIDs.front(); +    LLInventoryObject* obj = gInventory.getObject(selected_id); +    if (!obj) +    { +        return; +    } + +    std::vector<std::string> items; +    std::vector<std::string> disabled_items; + +    bool is_agent_inventory = gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID()); +    bool is_link = obj->getIsLinkType(); +    bool is_folder = (obj->getType() == LLAssetType::AT_CATEGORY); +    bool is_cof = LLAppearanceMgr::instance().getIsInCOF(selected_id); +    bool is_inbox = is_inbox_folder(selected_id); +    bool is_trash = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)); +    bool is_in_trash = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)); +    bool is_lost_and_found = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); +    bool is_outfits= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS)); +    //bool is_favorites= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE)); + +    bool is_system_folder = false; +    LLFolderType::EType folder_type(LLFolderType::FT_NONE); +    bool has_children = false; +    bool is_full_perm_item = false; +    bool is_copyable = false; +    LLViewerInventoryItem* selected_item = gInventory.getItem(selected_id); + +    if(is_folder) +    { +        LLInventoryCategory* category = gInventory.getCategory(selected_id); +        if (category) +        { +            folder_type = category->getPreferredType(); +            is_system_folder = LLFolderType::lookupIsProtectedType(folder_type); +            has_children = (gInventory.categoryHasChildren(selected_id) != LLInventoryModel::CHILDREN_NO); +        } +    } +    else +    { +        if (selected_item) +        { +            is_full_perm_item = selected_item->getIsFullPerm(); +            is_copyable = selected_item->getPermissions().allowCopyBy(gAgent.getID()); +        } +    } + +    if(!is_link) +    { +        items.push_back(std::string("thumbnail")); +        if (!is_agent_inventory || (is_in_trash && !is_trash)) +        { +            disabled_items.push_back(std::string("thumbnail")); +        } +    } + +    if (is_folder) +    { +        if(!isRootFolder()) +        { +            items.push_back(std::string("Copy Separator")); + +            items.push_back(std::string("open_in_current_window")); +            items.push_back(std::string("open_in_new_window")); +            items.push_back(std::string("Open Folder Separator")); +        } +    } +    else +    { +        if (is_agent_inventory && (obj->getType() != LLAssetType::AT_LINK_FOLDER)) +        { +            items.push_back(std::string("Replace Links")); +        } +        if (obj->getType() == LLAssetType::AT_LANDMARK) +        { +            items.push_back(std::string("Landmark Separator")); +            items.push_back(std::string("url_copy")); +            items.push_back(std::string("About Landmark")); +            items.push_back(std::string("show_on_map")); +        } +    } + +    if(is_trash) +    { +        items.push_back(std::string("Empty Trash")); + +        LLInventoryModel::cat_array_t* cat_array; +        LLInventoryModel::item_array_t* item_array; +        gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array); +        if (0 == cat_array->size() && 0 == item_array->size()) +        { +            disabled_items.push_back(std::string("Empty Trash")); +        } +    } +    else if(is_in_trash) +    { +        if (is_link) +        { +            items.push_back(std::string("Find Original")); +            if (LLAssetType::lookupIsLinkType(obj->getType())) +            { +                disabled_items.push_back(std::string("Find Original")); +            } +        } +        items.push_back(std::string("Purge Item")); +        if (is_folder && !get_is_category_removable(&gInventory, selected_id)) +        { +            disabled_items.push_back(std::string("Purge Item")); +        } +        items.push_back(std::string("Restore Item")); +    } +    else +    { +        if(can_share_item(selected_id)) +        { +            items.push_back(std::string("Share")); +        } +        if (LLClipboard::instance().hasContents() && is_agent_inventory && !is_cof && !is_inbox_folder(selected_id)) +        { +            items.push_back(std::string("Paste")); + +            static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); +            if (inventory_linking) +            { +                items.push_back(std::string("Paste As Link")); +            } +        } +        if (is_folder && is_agent_inventory) +        { +            if (!is_cof && (folder_type != LLFolderType::FT_OUTFIT) && !is_outfits && !is_inbox_folder(selected_id)) +            { +                if (!gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD)) && !isRootFolder()) +                { +                    items.push_back(std::string("New Folder")); +                } +                items.push_back(std::string("upload_def")); +            } + +            if(is_outfits && !isRootFolder()) +            { +                items.push_back(std::string("New Outfit")); +            } + +            items.push_back(std::string("Subfolder Separator")); +            if (!is_system_folder && !isRootFolder()) +            { +                if(has_children && (folder_type != LLFolderType::FT_OUTFIT)) +                { +                    items.push_back(std::string("Ungroup folder items")); +                } +                items.push_back(std::string("Cut")); +                items.push_back(std::string("Delete")); +                if(!get_is_category_removable(&gInventory, selected_id)) +                { +                    disabled_items.push_back(std::string("Delete")); +                    disabled_items.push_back(std::string("Cut")); +                } + +                if(!is_inbox) +                { +                    items.push_back(std::string("Rename")); +                } +            } +            if(!is_system_folder) +            { +                items.push_back(std::string("Copy")); +            } +        } +        else if(!is_folder) +        { +            items.push_back(std::string("Properties")); +            items.push_back(std::string("Copy Asset UUID")); +            items.push_back(std::string("Copy Separator")); + +            bool is_asset_knowable = is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(obj->getType()); +            if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308 +                 || (! ( is_full_perm_item || gAgent.isGodlike()))) +            { +                disabled_items.push_back(std::string("Copy Asset UUID")); +            } +            if(is_agent_inventory) +            { +                items.push_back(std::string("Cut")); +                if (!is_link || !is_cof || !get_is_item_worn(selected_id)) +                { +                    items.push_back(std::string("Delete")); +                } +                if(!get_is_item_removable(&gInventory, selected_id)) +                { +                    disabled_items.push_back(std::string("Delete")); +                    disabled_items.push_back(std::string("Cut")); +                } + +                if (selected_item && (selected_item->getInventoryType() != LLInventoryType::IT_CALLINGCARD) && !is_inbox && selected_item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) +                { +                    items.push_back(std::string("Rename")); +                } +            } +            items.push_back(std::string("Copy")); +            if (!is_copyable) +            { +                disabled_items.push_back(std::string("Copy")); +            } +        } +        if((obj->getType() == LLAssetType::AT_SETTINGS) +           || ((obj->getType() <= LLAssetType::AT_GESTURE) +               && obj->getType() != LLAssetType::AT_OBJECT +               && obj->getType() != LLAssetType::AT_CLOTHING +               && obj->getType() != LLAssetType::AT_CATEGORY +               && obj->getType() != LLAssetType::AT_LANDMARK +               && obj->getType() != LLAssetType::AT_BODYPART)) +        { +            bool can_open = !LLAssetType::lookupIsLinkType(obj->getType()); + +            if (can_open) +            { +                if (is_link) +                    items.push_back(std::string("Open Original")); +                else +                    items.push_back(std::string("Open")); +            } +            else +            { +                disabled_items.push_back(std::string("Open")); +                disabled_items.push_back(std::string("Open Original")); +            } +             +            if(LLAssetType::AT_GESTURE == obj->getType()) +            { +                items.push_back(std::string("Gesture Separator")); +                if(!LLGestureMgr::instance().isGestureActive(selected_id)) +                { +                    items.push_back(std::string("Activate")); +                } +                else +                { +                    items.push_back(std::string("Deactivate")); +                } +            } +        } +        else if(LLAssetType::AT_LANDMARK == obj->getType()) +        { +            items.push_back(std::string("Landmark Open")); +        } +        else if (obj->getType() == LLAssetType::AT_OBJECT || obj->getType() == LLAssetType::AT_CLOTHING || obj->getType() == LLAssetType::AT_BODYPART) +        { +            items.push_back(std::string("Wearable And Object Separator")); +            if(obj->getType() == LLAssetType::AT_CLOTHING) +            { +                items.push_back(std::string("Take Off")); +            } +            if(get_is_item_worn(selected_id)) +            { +                if(obj->getType() == LLAssetType::AT_OBJECT) +                { +                    items.push_back(std::string("Detach From Yourself")); +                } +                disabled_items.push_back(std::string("Wearable And Object Wear")); +                disabled_items.push_back(std::string("Wearable Add")); +            } +            else +            { +                if(obj->getType() == LLAssetType::AT_OBJECT) +                { +                    items.push_back(std::string("Wearable Add")); +                } +                items.push_back(std::string("Wearable And Object Wear")); +                disabled_items.push_back(std::string("Take Off")); +            } + +            if (!gAgentAvatarp->canAttachMoreObjects() && (obj->getType() == LLAssetType::AT_OBJECT)) +            { +                disabled_items.push_back(std::string("Wearable And Object Wear")); +                disabled_items.push_back(std::string("Wearable Add")); +            } +            if (selected_item && (obj->getType() != LLAssetType::AT_OBJECT) && LLWearableType::getInstance()->getAllowMultiwear(selected_item->getWearableType())) +            { +                items.push_back(std::string("Wearable Add")); +                if (!gAgentWearables.canAddWearable(selected_item->getWearableType())) +                { +                    disabled_items.push_back(std::string("Wearable Add")); +                } +            } +        } +        if(obj->getType() == LLAssetType::AT_TEXTURE) +        { +            items.push_back(std::string("Save As")); +            bool can_copy = selected_item && selected_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); +            if (!can_copy) +            { +                disabled_items.push_back(std::string("Save As")); +            } +        } +        if (is_link) +        { +            items.push_back(std::string("Find Original")); +            if (LLAssetType::lookupIsLinkType(obj->getType())) +            { +                disabled_items.push_back(std::string("Find Original")); +            } +        } +        if (is_lost_and_found) +        { +            items.push_back(std::string("Empty Lost And Found")); + +            LLInventoryModel::cat_array_t* cat_array; +            LLInventoryModel::item_array_t* item_array; +            gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array); +            // Enable Empty menu item only when there is something to act upon. +            if (0 == cat_array->size() && 0 == item_array->size()) +            { +                disabled_items.push_back(std::string("Empty Lost And Found")); +            } + +            disabled_items.push_back(std::string("New Folder")); +            disabled_items.push_back(std::string("upload_def")); +        } +    } + +    hide_context_entries(*menu, items, disabled_items); +} + diff --git a/indra/newview/llinventorygallerymenu.h b/indra/newview/llinventorygallerymenu.h new file mode 100644 index 0000000000..7c3545432b --- /dev/null +++ b/indra/newview/llinventorygallerymenu.h @@ -0,0 +1,62 @@ +/** + * @file llinventorygallerymenu.h + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLINVENTORYGALLERYMENU_H +#define LL_LLINVENTORYGALLERYMENU_H + +#include "lllistcontextmenu.h" + +class LLInventoryGalleryContextMenu : public LLListContextMenu +{ +public: +    LLInventoryGalleryContextMenu(LLInventoryGallery* gallery) +    : LLListContextMenu(), +    mGallery(gallery), +    mRootFolder(false){} +    /*virtual*/ LLContextMenu* createMenu(); + +    bool isRootFolder() { return mRootFolder; } +    void setRootFolder(bool is_root) { mRootFolder = is_root; } +    void doToSelected(const LLSD& userdata); +    void rename(const LLUUID& item_id); + +protected: +    //virtual void buildContextMenu(class LLMenuGL& menu, U32 flags); +    void updateMenuItemsVisibility(LLContextMenu* menu); + +    void fileUploadLocation(const LLSD& userdata); +    bool canSetUploadLocation(const LLSD& userdata); + +    static void onRename(const LLSD& notification, const LLSD& response); + +private: +    bool enableContextMenuItem(const LLSD& userdata); +    bool checkContextMenuItem(const LLSD& userdata); + +    LLInventoryGallery* mGallery; +    bool mRootFolder; +}; + +#endif diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 9c4e122481..ea771661ec 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -37,9 +37,11 @@  #include "llappearancemgr.h"  #include "llavatarnamecache.h"  #include "llclipboard.h" +#include "lldispatcher.h"  #include "llinventorypanel.h"  #include "llinventorybridge.h"  #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h"  #include "llinventoryobserver.h"  #include "llinventorypanel.h"  #include "llfloaterpreviewtrash.h" @@ -49,6 +51,7 @@  #include "llviewercontrol.h"  #include "llviewernetwork.h"  #include "llpreview.h"  +#include "llviewergenericmessage.h"  #include "llviewermessage.h"  #include "llviewerfoldertype.h"  #include "llviewerwindow.h" @@ -74,9 +77,11 @@  // Increment this if the inventory contents change in a non-backwards-compatible way.  // For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect. -const S32 LLInventoryModel::sCurrentInvCacheVersion = 2; +const S32 LLInventoryModel::sCurrentInvCacheVersion = 3;  BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE; +S32 LLInventoryModel::sPendingSystemFolders = 0; +  ///----------------------------------------------------------------------------  /// Local function declarations, constants, enums, and typedefs  ///---------------------------------------------------------------------------- @@ -133,6 +138,222 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)  	return rv;  } +struct InventoryCallbackInfo +{ +    InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) : +        mCallback(callback), mInvID(inv_id) {} +    U32 mCallback; +    LLUUID mInvID; +}; + +///---------------------------------------------------------------------------- +/// Class LLDispatchClassifiedClickThrough +///---------------------------------------------------------------------------- + +class LLDispatchBulkUpdateInventory : public LLDispatchHandler +{ +public: +    virtual bool operator()( +        const LLDispatcher* dispatcher, +        const std::string& key, +        const LLUUID& invoice, +        const sparam_t& strings) +    { +        LLSD message; + +        // Expect single string parameter in the form of a notation serialized LLSD. +        sparam_t::const_iterator it = strings.begin(); +        if (it != strings.end()) { +            const std::string& llsdRaw = *it++; +            std::istringstream llsdData(llsdRaw); +            if (!LLSDSerialize::deserialize(message, llsdData, llsdRaw.length())) +            { +                LL_WARNS() << "LLDispatchBulkUpdateInventory: Attempted to read parameter data into LLSD but failed:" << llsdRaw << LL_ENDL; +            } +        } + +        LLInventoryModel::update_map_t update; +        LLInventoryModel::cat_array_t folders; +        LLInventoryModel::item_array_t items; +        std::list<InventoryCallbackInfo> cblist; +        uuid_vec_t wearable_ids; + +        LLSD item_data = message["item_data"]; +        if (item_data.isArray()) +        { +            for (LLSD::array_iterator itd = item_data.beginArray(); itd != item_data.endArray(); ++itd) +            { +                const LLSD &item(*itd); + +                // Agent id probably should be in the root of the message +                LLUUID agent_id = item["agent_id"].asUUID(); +                if (agent_id != gAgent.getID()) +                { +                    LL_WARNS() << "Got a BulkUpdateInventory for the wrong agent." << LL_ENDL; +                    return false; +                } + +                LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; +                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()) +                { +                    items.push_back(titem); +                    cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID())); +                    if (titem->getInventoryType() == LLInventoryType::IT_WEARABLE) +                    { +                        wearable_ids.push_back(titem->getUUID()); +                    } + +                    // examine update for changes. +                    LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); +                    if (itemp) +                    { +                        if (titem->getParentUUID() == itemp->getParentUUID()) +                        { +                            update[titem->getParentUUID()]; +                        } +                        else +                        { +                            ++update[titem->getParentUUID()]; +                            --update[itemp->getParentUUID()]; +                        } +                    } +                    else +                    { +                        LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID()); +                        if (folderp) +                        { +                            ++update[titem->getParentUUID()]; +                        } +                    } +                } +                else +                { +                    cblist.push_back(InventoryCallbackInfo(callback_id, LLUUID::null)); +                } +            } +        } + +        LLSD folder_data = message["folder_data"]; +        if (folder_data.isArray()) +        { +            for (LLSD::array_iterator itd = folder_data.beginArray(); itd != folder_data.endArray(); ++itd) +            { +                const LLSD &folder(*itd); + +                LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID()); +                tfolder->unpackMessage(folder); + +                LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' (" +                        << tfolder->getUUID() << ") in " << tfolder->getParentUUID() +                        << LL_ENDL; + +                // If the folder is a listing or a version folder, all we need to do is update the SLM data +                int depth_folder = depth_nesting_in_marketplace(tfolder->getUUID()); +                if ((depth_folder == 1) || (depth_folder == 2)) +                { +                    // Trigger an SLM listing update +                    LLUUID listing_uuid = (depth_folder == 1 ? tfolder->getUUID() : tfolder->getParentUUID()); +                    S32 listing_id = LLMarketplaceData::instance().getListingID(listing_uuid); +                    LLMarketplaceData::instance().getListing(listing_id); +                    // In that case, there is no item to update so no callback -> we skip the rest of the update +                } +                else if (tfolder->getUUID().notNull()) +                { +                    folders.push_back(tfolder); +                    LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); +                    if (folderp) +                    { +                        if (tfolder->getParentUUID() == folderp->getParentUUID()) +                        { +                            update[tfolder->getParentUUID()]; +                        } +                        else +                        { +                            ++update[tfolder->getParentUUID()]; +                            --update[folderp->getParentUUID()]; +                        } +                    } +                    else +                    { +                        // we could not find the folder, so it is probably +                        // new. However, we only want to attempt accounting +                        // for the parent if we can find the parent. +                        folderp = gInventory.getCategory(tfolder->getParentUUID()); +                        if (folderp) +                        { +                            ++update[tfolder->getParentUUID()]; +                        } +                    } +                } +            } +        } + +        gInventory.accountForUpdate(update); + +        for (LLInventoryModel::cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit) +        { +            gInventory.updateCategory(*cit); +        } +        for (LLInventoryModel::item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit) +        { +            gInventory.updateItem(*iit); +        } +        gInventory.notifyObservers(); + +        /* +        Transaction id not included? + +        // The incoming inventory could span more than one BulkInventoryUpdate packet, +        // so record the transaction ID for this purchase, then wear all clothing +        // that comes in as part of that transaction ID.  JC +        if (LLInventoryState::sWearNewClothing) +        { +            LLInventoryState::sWearNewClothingTransactionID = tid; +            LLInventoryState::sWearNewClothing = FALSE; +        } + +        if (tid.notNull() && tid == LLInventoryState::sWearNewClothingTransactionID) +        { +            count = wearable_ids.size(); +            for (i = 0; i < count; ++i) +            { +                LLViewerInventoryItem* wearable_item; +                wearable_item = gInventory.getItem(wearable_ids[i]); +                LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true); +            } +        } +        */ + +        if (LLInventoryState::sWearNewClothing && wearable_ids.size() > 0) +        { +            LLInventoryState::sWearNewClothing = FALSE; + +            size_t count = wearable_ids.size(); +            for (S32 i = 0; i < count; ++i) +            { +                LLViewerInventoryItem* wearable_item; +                wearable_item = gInventory.getItem(wearable_ids[i]); +                LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true); +            } +        } + +        std::list<InventoryCallbackInfo>::iterator inv_it; +        for (inv_it = cblist.begin(); inv_it != cblist.end(); ++inv_it) +        { +            InventoryCallbackInfo cbinfo = (*inv_it); +            gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID); +        } +        return true; +    } +}; +static LLDispatchBulkUpdateInventory sBulkUpdateInventory; +  ///----------------------------------------------------------------------------  /// Class LLInventoryValidationInfo  ///---------------------------------------------------------------------------- @@ -222,6 +443,7 @@ LLInventoryModel::LLInventoryModel()  	mIsNotifyObservers(FALSE),  	mModifyMask(LLInventoryObserver::ALL),  	mChangedItemIDs(), +    mBulkFecthCallbackSlot(),  	mObservers(),  	mHttpRequestFG(NULL),  	mHttpRequestBG(NULL), @@ -253,6 +475,11 @@ void LLInventoryModel::cleanupInventory()  		mObservers.erase(iter);  		delete observer;  	} + +    if (mBulkFecthCallbackSlot.connected()) +    { +        mBulkFecthCallbackSlot.disconnect(); +    }  	mObservers.clear();  	// Run down HTTP transport @@ -452,6 +679,31 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,  	items = get_ptr_in_map(mParentChildItemTree, cat_id);  } +void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const +{ +    if (cat_array_t* categoriesp = get_ptr_in_map(mParentChildCategoryTree, cat_id)) +    { +        for (LLViewerInventoryCategory* pFolder : *categoriesp) +        { +			if (f(pFolder, nullptr)) +			{ +				categories.push_back(pFolder); +			} +        } +    } + +    if (item_array_t* itemsp = get_ptr_in_map(mParentChildItemTree, cat_id)) +    { +        for (LLViewerInventoryItem* pItem : *itemsp) +        { +			if (f(nullptr, pItem)) +			{ +				items.push_back(pItem); +			} +        } +    } +} +  LLInventoryModel::digest_t LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const  {  	LLInventoryModel::cat_array_t* cat_array; @@ -561,10 +813,77 @@ 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) +            { +                    if (new_cat_id.isNull()) +                    { +                        LL_WARNS("Inventory") +                            << "Failed to create folder of type " << preferred_type +                            << LL_ENDL; +                    } +                    else +                    { +                        LL_WARNS("Inventory") << "Created category: " << new_cat_id +                            << " for type: " << preferred_type << LL_ENDL; +                        sPendingSystemFolders--; +                    } +            } +            ); +        } +        else +        { +            LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type +                << " because inventory is not usable" << LL_ENDL; +        } +    } +    else +    { +        sPendingSystemFolders--; +    } +} +  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) @@ -595,20 +914,15 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(  	if(rv.isNull()          && root_id.notNull() -       && create_folder         && preferred_type != LLFolderType::FT_MARKETPLACE_LISTINGS         && preferred_type != LLFolderType::FT_OUTBOX)  	{ - -		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(!isInventoryUsable()); +        LL_WARNS("Inventory") << "Tried to find folder, type " << preferred_type +								  << " but category does not exist" << LL_ENDL;  	}  	return rv;  } @@ -617,12 +931,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) @@ -653,40 +967,46 @@ 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());  }  // 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. -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) +										   inventory_func_type callback, +										   const LLUUID& thumbnail_id)  {  	LL_DEBUGS(LOG_INV) << "Create '" << pname << "' in '" << make_inventory_path(parent_id) << "'" << LL_ENDL; -	LLUUID id;  	if (!isInventoryUsable())  	{  		LL_WARNS(LOG_INV) << "Inventory is not usable; can't create requested category of type "  						  << preferred_type << LL_ENDL; -		// FIXME failing but still returning an id? -		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; -		// FIXME failing but still returning an id? -		return id; +        if (callback) +        { +            callback(LLUUID::null); +        } +		return;  	}  	if (preferred_type != LLFolderType::FT_NONE) @@ -697,26 +1017,72 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,  		LL_WARNS(LOG_INV) << "Creating new system folder, type " << preferred_type << LL_ENDL;  	} -	id.generate();  	std::string name = pname; -	if(!pname.empty()) +	if (pname.empty())  	{ -		name.assign(pname); +		name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type));  	} -	else + +	if (AISAPI::isAvailable())  	{ -		name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type)); +		LLSD new_inventory = LLSD::emptyMap(); +		new_inventory["categories"] = LLSD::emptyArray(); +		LLViewerInventoryCategory cat(LLUUID::null, parent_id, preferred_type, name, gAgent.getID()); +        cat.setThumbnailUUID(thumbnail_id); +		LLSD cat_sd = cat.asAISCreateCatLLSD(); +		new_inventory["categories"].append(cat_sd); +		AISAPI::CreateInventory( +            parent_id, +            new_inventory, +            [this, callback, parent_id, preferred_type, name] (const LLUUID& new_category) +        { +            if (new_category.isNull()) +            { +                if (callback && !callback.empty()) +                { +                    callback(new_category); +                } +                return; +            } + +            // todo: not needed since AIS does the accounting? +            LLViewerInventoryCategory* folderp = gInventory.getCategory(new_category); +            if (!folderp) +            { +                // Add the category to the internal representation +                LLPointer<LLViewerInventoryCategory> 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.empty()) +            { +                callback(new_category); +            } +        }); +        return;  	} -	 +  	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;  		body["parent_id"] = parent_id; @@ -729,44 +1095,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; -	} - -	if (!gMessageSystem) -	{ -		return LLUUID::null; +        return;  	} -	// 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 -	LLPointer<LLViewerInventoryCategory> 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(); - -	LL_INFOS(LOG_INV) << "Created new category '" << make_inventory_path(id) << "'" << LL_ENDL; -	// 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) @@ -790,12 +1125,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;      } @@ -1300,7 +1643,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; @@ -1442,17 +1785,25 @@ 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); -	} +    // make sure we aren't adding expensive Rebuild to anything else. +    notifyObservers(); -	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 (const broken_links_t::value_type &link_list : mPossiblyBrockenLinks) +    { +        for (const LLUUID& link_id : link_list.second) +        { +            addChangedMask(LLInventoryObserver::REBUILD , link_id); +        } +    } +    for (const LLUUID& link_id : mLinksRebuildList) +    { +        addChangedMask(LLInventoryObserver::REBUILD , link_id); +    } +    mPossiblyBrockenLinks.clear(); +    mLinksRebuildList.clear(); +    notifyObservers();  }  // Does not appear to be used currently. @@ -1758,6 +2109,20 @@ void LLInventoryModel::idleNotifyObservers()  {  	// *FIX:  Think I want this conditional or moved elsewhere...  	handleResponses(true); + +    if (mLinksRebuildList.size() > 0) +    { +        if (mModifyMask != LLInventoryObserver::NONE || (mChangedItemIDs.size() != 0)) +        { +            notifyObservers(); +        } +        for (const LLUUID& link_id : mLinksRebuildList) +        { +            addChangedMask(LLInventoryObserver::REBUILD , link_id); +        } +        mLinksRebuildList.clear(); +        notifyObservers(); +    }  	if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0))  	{ @@ -2077,10 +2442,52 @@ 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 (item->getAssetUUID().notNull() +                && LLInventoryModelBackgroundFetch::getInstance()->folderFetchActive()) +            { +                // Schedule this link for a recheck as inventory gets loaded +                // Todo: expand to cover not just an initial fetch +                mPossiblyBrockenLinks[item->getAssetUUID()].insert(item->getUUID()); + +                // Do a blank rebuild of links once fetch is done +                if (!mBulkFecthCallbackSlot.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. +                    mBulkFecthCallbackSlot = +                        LLInventoryModelBackgroundFetch::getInstance()->setFetchCompletionCallback( +                            [this]() +                    { +                        // rebuild is just in case, primary purpose is to wipe +                        // the list since we won't be getting anything 'new' +                        // see mLinksRebuildList +                        rebuildBrockenLinks(); +                        mBulkFecthCallbackSlot.disconnect(); +                    }); +                } +                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 (!mPossiblyBrockenLinks.empty()) +        { +            // check if we are waiting for this item +            broken_links_t::iterator iter = mPossiblyBrockenLinks.find(item->getUUID()); +            if (iter != mPossiblyBrockenLinks.end()) +            { +                mLinksRebuildList.insert(iter->second.begin() , iter->second.end()); +                mPossiblyBrockenLinks.erase(iter); +            } +        }  		if (item->getIsLinkType())  		{  			// Add back-link from linked-to UUID. @@ -2348,6 +2755,10 @@ bool LLInventoryModel::loadSkeleton(  				else  				{  					cached_ids.insert(tcat->getUUID()); + +                    // At the moment download does not provide a thumbnail +                    // uuid, use the one from cache +                    tcat->setThumbnailUUID(cat->getThumbnailUUID());  				}  			} @@ -2635,7 +3046,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(); @@ -2798,6 +3209,11 @@ void LLInventoryModel::initHttpRequest()  		mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML);  		mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_INVENTORY);  	} + +    if (!gGenericDispatcher.isHandlerPresent("BulkUpdateInventory")) +    { +        gGenericDispatcher.addHandler("BulkUpdateInventory", &sBulkUpdateInventory); +    }  }  void LLInventoryModel::handleResponses(bool foreground) @@ -2850,14 +3266,17 @@ 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); +    //amount of System Folder we should wait for +    sPendingSystemFolders = 8; + +	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 @@ -3082,9 +3501,6 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)  	msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem,  						processRemoveInventoryItem,  						NULL); -	msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder, -						processUpdateInventoryFolder, -						NULL);  	msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,  						processRemoveInventoryFolder,  						NULL); @@ -3113,6 +3529,10 @@ void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, vo  		msg->getU32Fast(_PREHASH_InventoryData, _PREHASH_CallbackID, callback_id);  		gInventoryCallbacks.fire(callback_id, item_id); + +        // todo: instead of unpacking message fully, +        // grab only an item_id, then fetch +        LLInventoryModelBackgroundFetch::instance().scheduleItemFetch(item_id, true);  	}  } @@ -3228,66 +3648,6 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)  }  // 	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<LLViewerInventoryCategory> lastfolder; // hack -	cat_array_t folders; -	update_map_t update; -	S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); -	for(S32 i = 0; i < count; ++i) -	{ -		LLPointer<LLViewerInventoryCategory> 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)  { @@ -3389,14 +3749,6 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,  	}  } -struct InventoryCallbackInfo -{ -	InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) : -		mCallback(callback), mInvID(inv_id) {} -	U32 mCallback; -	LLUUID mInvID; -}; -  // static  void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)  { @@ -3442,15 +3794,22 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)  			LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());  			if(folderp)  			{ -				if(tfolder->getParentUUID() == folderp->getParentUUID()) -				{ -					update[tfolder->getParentUUID()]; -				} -				else -				{ -					++update[tfolder->getParentUUID()]; -					--update[folderp->getParentUUID()]; -				} +                if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) +                { +                    if (tfolder->getParentUUID() == folderp->getParentUUID()) +                    { +                        update[tfolder->getParentUUID()]; +                    } +                    else +                    { +                        ++update[tfolder->getParentUUID()]; +                        --update[folderp->getParentUUID()]; +                    } +                } +                else +                { +                    folderp->fetch(); +                }  			}  			else  			{ @@ -3460,7 +3819,14 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)  				folderp = gInventory.getCategory(tfolder->getParentUUID());  				if(folderp)  				{ -					++update[tfolder->getParentUUID()]; +                    if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) +                    { +                        ++update[tfolder->getParentUUID()]; +                    } +                    else +                    { +                        folderp->fetch(); +                    }  				}  			}  		} @@ -3506,7 +3872,14 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)  				LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID());  				if(folderp)  				{ -					++update[titem->getParentUUID()]; +                    if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) +                    { +                        ++update[titem->getParentUUID()]; +                    } +                    else +                    { +                        folderp->fetch(); +                    }  				}  			}  		} @@ -3520,10 +3893,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 +        LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch((*cit)->getUUID(), true /*force, since it has changes*/);  	}  	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 +        LLInventoryModelBackgroundFetch::instance().scheduleItemFetch((*iit)->getUUID(), true);  	}  	gInventory.notifyObservers(); @@ -4348,7 +4731,6 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const  			}  			else if (count_under_root > 1)  			{ -				LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;  				validation_info->mDuplicateRequiredSystemFolders.insert(folder_type);                  if (!is_automatic && folder_type != LLFolderType::FT_SETTINGS)                  { @@ -4356,6 +4738,7 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const                      // outfits, trash and other non-automatic folders.  					validation_info->mFatalSystemDuplicate++;                      fatal_errs++; +                    LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;                  }                  else                  { @@ -4364,6 +4747,7 @@ LLPointer<LLInventoryValidationInfo> LLInventoryModel::validate() const                      // Exception: FT_SETTINGS is not automatic, but only deserves a warning.  					validation_info->mWarnings["non_fatal_system_duplicate_under_root"]++;                      warning_count++; +                    LL_WARNS("Inventory") << "System folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL;                  }  			}  			if (count_elsewhere > 0) diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 6c5706f871..4309c03f8e 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -226,10 +226,14 @@ private:  	//--------------------------------------------------------------------  public:  	static BOOL getIsFirstTimeInViewer2(); +    static bool  isSysFoldersReady() { return (sPendingSystemFolders == 0); } +  private:  	static BOOL sFirstTimeInViewer2;  	const static S32 sCurrentInvCacheVersion; // expected inventory cache version +    static S32 sPendingSystemFolders; +  /**                    Initialization/Setup   **                                                                            **   *******************************************************************************/ @@ -255,6 +259,7 @@ public:  	void getDirectDescendentsOf(const LLUUID& cat_id,  								cat_array_t*& categories,  								item_array_t*& items) const; +    void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const;  	typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"  	// Compute a hash of direct descendant names (for detecting child name changes) @@ -302,24 +307,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; @@ -405,13 +411,15 @@ public:  							  const LLUUID& new_parent_id,  							  BOOL restamp); +    // Marks links from a "possibly" broken list for a rebuild +    // clears the list +    void rebuildBrockenLinks(); +    bool hasPosiblyBrockenLinks() const { return mPossiblyBrockenLinks.size() > 0; } +  	//--------------------------------------------------------------------  	// 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. @@ -475,10 +483,11 @@ public:  public:  	// 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); +							 inventory_func_type callback = NULL, +							 const LLUUID& thumbnail_id = LLUUID::null);  protected:  	// Internal methods that add inventory and make sure that all of  	// the internal data structures are consistent. These methods @@ -575,6 +584,10 @@ private:      U32 mModifyMaskBacklog;      changed_items_t mChangedItemIDsBacklog;      changed_items_t mAddedItemIDsBacklog; +    typedef std::map<LLUUID , changed_items_t> broken_links_t; +    broken_links_t mPossiblyBrockenLinks; // there can be multiple links per item +    changed_items_t mLinksRebuildList; +    boost::signals2::connection mBulkFecthCallbackSlot;  	//-------------------------------------------------------------------- @@ -663,7 +676,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 f544b318d6..91adef8047 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" @@ -186,12 +187,14 @@ const char * const LOG_INV("Inventory");  ///----------------------------------------------------------------------------  LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch(): -	mBackgroundFetchActive(FALSE), +	mBackgroundFetchActive(false),  	mFolderFetchActive(false),  	mFetchCount(0), -	mAllFoldersFetched(FALSE), -	mRecursiveInventoryFetchStarted(FALSE), -	mRecursiveLibraryFetchStarted(FALSE), +    mLastFetchCount(0), +    mFetchFolderCount(0), +    mAllRecursiveFoldersFetched(false), +	mRecursiveInventoryFetchStarted(false), +	mRecursiveLibraryFetchStarted(false),  	mMinTimeBetweenFetches(0.3f)  {} @@ -200,7 +203,12 @@ LLInventoryModelBackgroundFetch::~LLInventoryModelBackgroundFetch()  bool LLInventoryModelBackgroundFetch::isBulkFetchProcessingComplete() const  { -	return mFetchQueue.empty() && mFetchCount <= 0; +	return mFetchFolderQueue.empty() && mFetchItemQueue.empty() && mFetchCount <= 0; +} + +bool LLInventoryModelBackgroundFetch::isFolderFetchProcessingComplete() const +{ +    return mFetchFolderQueue.empty() && mFetchFolderCount <= 0;  }  bool LLInventoryModelBackgroundFetch::libraryFetchStarted() const @@ -235,7 +243,7 @@ bool LLInventoryModelBackgroundFetch::inventoryFetchInProgress() const  bool LLInventoryModelBackgroundFetch::isEverythingFetched() const  { -	return mAllFoldersFetched; +	return mAllRecursiveFoldersFetched;  }  BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const @@ -243,17 +251,33 @@ 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)); +    EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT; +    if (is_category) +    { +        mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type, is_category)); +    } +    else +    { +        mFetchItemQueue.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)); +    EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT; +    if (is_category) +    { +        mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type, is_category)); +    } +    else +    { +        mFetchItemQueue.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)); @@ -262,31 +286,53 @@ 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; +        EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT;  		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 +                    mFetchFolderQueue.push_front(FetchQueueInfo(gInventory.getRootFolderID(), FT_FOLDER_AND_CONTENT)); +                } +                else +                { +                    mFetchFolderQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursion_type)); +                }  				gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);  			}  			if (! mRecursiveLibraryFetchStarted)  			{  				mRecursiveLibraryFetchStarted |= recursive; -				mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursive)); +                mFetchFolderQueue.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 (AISAPI::isAvailable()) +            { +                if (mFetchFolderQueue.empty() || mFetchFolderQueue.back().mUUID != id) +                { +                    // On AIS make sure root goes to the top and follow up recursive +                    // fetches, not individual requests +                    mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type)); +                    gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); +                } +            } +            else if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != id) +            { +                    // Specific folder requests go to front of queue. +                    mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type)); +                    gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); +            } +  			if (id == gInventory.getLibraryRootFolderID())  			{  				mRecursiveLibraryFetchStarted |= recursive; @@ -299,21 +345,41 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive)  	}  	else if (LLViewerInventoryItem * itemp = gInventory.getItem(id))  	{ -		if (! itemp->mIsComplete && (mFetchQueue.empty() || mFetchQueue.front().mUUID != id)) +		if (! itemp->mIsComplete)  		{ -			mBackgroundFetchActive = TRUE; - -			mFetchQueue.push_front(FetchQueueInfo(id, false, false)); -			gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); +            scheduleItemFetch(id);  		}  	}  } +void LLInventoryModelBackgroundFetch::scheduleFolderFetch(const LLUUID& cat_id, bool forced) +{ +    if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id) +    { +        mBackgroundFetchActive = true; + +        // Specific folder requests go to front of queue. +        mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT)); +        gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); +    } +} + +void LLInventoryModelBackgroundFetch::scheduleItemFetch(const LLUUID& item_id, bool forced) +{ +    if (mFetchItemQueue.empty() || mFetchItemQueue.front().mUUID != item_id) +    { +        mBackgroundFetchActive = true; + +        mFetchItemQueue.push_front(FetchQueueInfo(item_id, forced ? FT_FORCED : FT_DEFAULT, false)); +        gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); +    } +} +  void LLInventoryModelBackgroundFetch::findLostItems()  { -	mBackgroundFetchActive = TRUE; -	mFolderFetchActive = true; -    mFetchQueue.push_back(FetchQueueInfo(LLUUID::null, TRUE)); +    mBackgroundFetchActive = true; +    mFolderFetchActive = true; +    mFetchFolderQueue.push_back(FetchQueueInfo(LLUUID::null, FT_RECURSIVE));      gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL);  } @@ -322,15 +388,28 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched()  	if (mRecursiveInventoryFetchStarted &&  		mRecursiveLibraryFetchStarted)  	{ -		mAllFoldersFetched = TRUE; +        mAllRecursiveFoldersFetched = true;  		//LL_INFOS(LOG_INV) << "All folders fetched, validating" << LL_ENDL;  		//gInventory.validate();  	} +  	mFolderFetchActive = false; -	mBackgroundFetchActive = false; +    if (isBulkFetchProcessingComplete()) +    { +        mBackgroundFetchActive = false; +    } + +    // For now only informs about initial fetch being done +    mFoldersFetchedSignal(); +  	LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL;  } +boost::signals2::connection LLInventoryModelBackgroundFetch::setFetchCompletionCallback(folders_fetched_callback_t cb) +{ +    return mFoldersFetchedSignal.connect(cb); +} +  void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *)  {  	LLInventoryModelBackgroundFetch::instance().backgroundFetch(); @@ -338,10 +417,17 @@ void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *)  void LLInventoryModelBackgroundFetch::backgroundFetch()  { -	if (mBackgroundFetchActive && gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived()) +	if (mBackgroundFetchActive)  	{ -		// If we'll be using the capability, we'll be sending batches and the background thing isn't as important. -		bulkFetch(); +        if (AISAPI::isAvailable()) +        { +            bulkFetchViaAis(); +        } +        else if (gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived()) +        { +            // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. +            bulkFetch(); +        }  	}  } @@ -354,9 +440,476 @@ void LLInventoryModelBackgroundFetch::incrFetchCount(S32 fetching)  		mFetchCount = 0;   	}  } +void LLInventoryModelBackgroundFetch::incrFetchFolderCount(S32 fetching) +{ +    incrFetchCount(fetching); +    mFetchFolderCount += fetching; +    if (mFetchCount < 0) +    { +        LL_WARNS_ONCE(LOG_INV) << "Inventory fetch count fell below zero (0)." << LL_ENDL; +        mFetchFolderCount = 0; +    } +} + +void ais_simple_item_callback(const LLUUID& inv_id) +{ +    LL_DEBUGS(LOG_INV , "AIS3") << "Response for " << inv_id << LL_ENDL; +    LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); +} + +void LLInventoryModelBackgroundFetch::onAISContentCalback( +    const LLUUID& request_id, +    const uuid_vec_t& content_ids, +    const LLUUID& response_id, +    EFetchType fetch_type) +{ +    // Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis +    incrFetchFolderCount(-1); + +    uuid_vec_t::const_iterator folder_iter = content_ids.begin(); +    uuid_vec_t::const_iterator folder_end = content_ids.end(); +    while (folder_iter != folder_end) +    { +        std::list<LLUUID>::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), *folder_iter); +        if (found != mExpectedFolderIds.end()) +        { +            mExpectedFolderIds.erase(found); +        } + +        LLViewerInventoryCategory* cat(gInventory.getCategory(*folder_iter)); +        if (cat) +        { +            cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); +        } +        if (response_id.isNull()) +        { +            // Failed to fetch, get it individually +            mFetchFolderQueue.push_back(FetchQueueInfo(*folder_iter, FT_RECURSIVE)); +        } +        else +        { +            // push descendant back to verify they are fetched fully (ex: didn't encounter depth limit) +            LLInventoryModel::cat_array_t* categories(NULL); +            LLInventoryModel::item_array_t* items(NULL); +            gInventory.getDirectDescendentsOf(*folder_iter, categories, items); +            if (categories) +            { +                for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); +                     it != categories->end(); +                     ++it) +                { +                    mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE)); +                } +            } +        } + +        folder_iter++; +    } + +    if (!mFetchFolderQueue.empty()) +    { +        mBackgroundFetchActive = true; +        mFolderFetchActive = true; +        gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); +    } +} +void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type) +{ +    // Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis +    incrFetchFolderCount(-1); +    std::list<LLUUID>::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), request_id); +    if (found != mExpectedFolderIds.end()) +    { +        mExpectedFolderIds.erase(found); +    } +    else +    { +        // ais shouldn't respond twice +        llassert(false); +        LL_WARNS() << "Unexpected folder response for " << request_id << LL_ENDL; +    } + +    if (request_id.isNull()) +    { +        // orhans, no other actions needed +        return; +    } + +    bool request_descendants = false; +    if (response_id.isNull()) // Failure +    { +        LL_DEBUGS(LOG_INV , "AIS3") << "Failure response for folder " << request_id << LL_ENDL; +        if (fetch_type == FT_RECURSIVE) +        { +            // A full recursive request failed. +            // Try requesting folder and nested content separately +            mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_FOLDER_AND_CONTENT)); +        } +        else if (fetch_type == FT_FOLDER_AND_CONTENT) +        { +            LL_WARNS() << "Failed to download folder: " << request_id << " Requesting known content separately" << LL_ENDL; +            mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE)); + +            // set folder's version to prevent viewer from trying to request folder indefinetely +            LLViewerInventoryCategory* cat(gInventory.getCategory(request_id)); +            if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) +            { +                cat->setVersion(0); +            } +        } +    } +    else +    { +        if (fetch_type == FT_RECURSIVE) +        { +            // Got the folder and content, now verify content +            // Request content even for FT_RECURSIVE in case of changes, failures +            // or if depth limit gets imlemented. +            // This shouldn't redownload folders if they already have version +            request_descendants = true; +            LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << ". Requesting content" << LL_ENDL; +        } +        else if (fetch_type == FT_FOLDER_AND_CONTENT) +        { +            // readd folder for content request +            mFetchFolderQueue.push_front(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE)); +        } +        else +        { +            LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << "." << LL_ENDL; +        } + +    } + +    if (request_descendants) +    { +        LLInventoryModel::cat_array_t* categories(NULL); +        LLInventoryModel::item_array_t* items(NULL); +        gInventory.getDirectDescendentsOf(request_id, categories, items); +        if (categories) +        { +            for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); +                 it != categories->end(); +                 ++it) +            { +                mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE)); +            } +        } +    } + +    if (!mFetchFolderQueue.empty()) +    { +        mBackgroundFetchActive = true; +        mFolderFetchActive = true; +        gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); +    } + +    // done +    LLViewerInventoryCategory * cat(gInventory.getCategory(request_id)); +    if (cat) +    { +        cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); +    } +}  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 LLCachedControl<U32> ais_pool(gSavedSettings, "PoolSizeAIS", 20); +    // Don't have too many requests at once, AIS throttles +    // Reserve one request for actions outside of fetch (like renames) +    const U32 max_concurrent_fetches = llclamp(ais_pool - 1, 1, 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 = LLStartUp::getStartupState() > STATE_WEARABLES_WAIT +        ? 0.006f // 6 ms +        : 1.f;  +    const F64 end_time = curent_time + max_time; +    S32 last_fetch_count = mFetchCount; + +    while (!mFetchFolderQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time) +    { +        const FetchQueueInfo & fetch_info(mFetchFolderQueue.front()); +        bulkFetchViaAis(fetch_info); +        mFetchFolderQueue.pop_front(); +        curent_time = LLTimer::getTotalSeconds(); +    } + +    // Ideally we shouldn't fetch items if recursive fetch isn't done, +    // but there is a chance some request will start timeouting and recursive +    // fetch will get stuck on a signle folder, don't block item fetch in such case +    while (!mFetchItemQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time) +    { +        const FetchQueueInfo& fetch_info(mFetchItemQueue.front()); +        bulkFetchViaAis(fetch_info); +        mFetchItemQueue.pop_front(); +        curent_time = LLTimer::getTotalSeconds(); +    } + +    if (last_fetch_count != mFetchCount // if anything was added +        || mLastFetchCount != mFetchCount) // if anything was substracted +    { +        LL_DEBUGS(LOG_INV , "AIS3") << "Total active fetches: " << mLastFetchCount << "->" << last_fetch_count << "->" << mFetchCount +            << ", scheduled folder fetches: " << (S32)mFetchFolderQueue.size() +            << ", scheduled item fetches: " << (S32)mFetchItemQueue.size() +            << LL_ENDL; +        mLastFetchCount = mFetchCount; + +        if (!mExpectedFolderIds.empty()) +        { +            // A folder seem to be stack fetching on QA account, print oldest folder out +            LL_DEBUGS(LOG_INV , "AIS3") << "Oldest expected folder: "; +            std::list<LLUUID>::const_iterator iter = mExpectedFolderIds.begin(); +            LL_CONT << *iter; +            if ((*iter).notNull()) +            { +                LLViewerInventoryCategory* cat(gInventory.getCategory(*iter)); +                if (cat) +                { +                    LL_CONT << " Folder name: " << cat->getName() << " Parent: " << cat->getParentUUID(); +                } +                else +                { +                    LL_CONT << " This folder doesn't exist"; +                } +            } +            else +            { +                LL_CONT << " Orphans request"; +            } +            LL_CONT << LL_ENDL; +        } +    } +     +    if (isFolderFetchProcessingComplete() && mFolderFetchActive) +    { +        setAllFoldersFetched(); +    } + +    if (isBulkFetchProcessingComplete()) +    { +        mBackgroundFetchActive = false; +    } +} + +void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetch_info) +{ +    if (fetch_info.mIsCategory) +    { +        const LLUUID & cat_id(fetch_info.mUUID); +        if (cat_id.isNull()) +        { +            incrFetchFolderCount(1); +            mExpectedFolderIds.push_back(cat_id); +            // Lost and found +            // Should it actually be recursive? +            AISAPI::FetchOrphans([](const LLUUID& response_id) +                                 { +                                     LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(LLUUID::null, +                                         response_id, +                                         FT_DEFAULT); +                                 }); +        } +        else +        { +            LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); +            if (cat) +            { +                if (fetch_info.mFetchType == FT_CONTENT_RECURSIVE) +                { +                    // fetch content only, ignore cat itself +                    uuid_vec_t children; +                    LLInventoryModel::cat_array_t* categories(NULL); +                    LLInventoryModel::item_array_t* items(NULL); +                    gInventory.getDirectDescendentsOf(cat_id, categories, items); + +                    LLViewerInventoryCategory::EFetchType target_state = LLViewerInventoryCategory::FETCH_RECURSIVE; +                    bool content_done = true; + +                    // Top limit is 'as many as you can put into url' +                    static LLCachedControl<S32> ais_batch(gSavedSettings, "BatchSizeAIS3", 20); +                    S32 batch_limit = llclamp(ais_batch(), 1, 40); + +                    for (LLInventoryModel::cat_array_t::iterator it = categories->begin(); +                         it != categories->end(); +                         ++it) +                    { +                        LLViewerInventoryCategory* child_cat = (*it); +                        if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion() +                            || child_cat->getFetching() >= target_state) +                        { +                            continue; +                        } + +                        if (child_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_LISTINGS) +                        { +                            // special case +                            content_done = false; +                            if (children.empty()) +                            { +                                // fetch marketplace alone +                                // Should it actually be fetched as FT_FOLDER_AND_CONTENT? +                                children.push_back(child_cat->getUUID()); +                                mExpectedFolderIds.push_back(child_cat->getUUID()); +                                child_cat->setFetching(target_state); +                                break; +                            } +                            else +                            { +                                // fetch marketplace alone next run +                                continue; +                            } +                        } + +                        children.push_back(child_cat->getUUID()); +                        mExpectedFolderIds.push_back(child_cat->getUUID()); +                        child_cat->setFetching(target_state); + +                        if (children.size() >= batch_limit) +                        { +                            content_done = false; +                            break; +                        } +                    } + +                    if (!children.empty()) +                    { +                        // increment before call in case of immediate callback +                        incrFetchFolderCount(1); + +                        EFetchType type = fetch_info.mFetchType; +                        LLUUID cat_id = cat->getUUID(); // need a copy for lambda +                        AISAPI::completion_t cb = [cat_id, children, type](const LLUUID& response_id) +                        { +                            LLInventoryModelBackgroundFetch::instance().onAISContentCalback(cat_id, children, response_id, type); +                        }; + +                        AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY; +                        if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) +                        { +                            item_type = AISAPI::LIBRARY; +                        } + +                        AISAPI::FetchCategorySubset(cat_id, children, item_type, true, cb, 0); +                    } + +                    if (content_done) +                    { +                        // This will have a bit of overlap with onAISContentCalback, +                        // but something else might have dowloaded folders, so verify +                        // every child that is complete has it's children done as well +                        for (LLInventoryModel::cat_array_t::iterator it = categories->begin(); +                             it != categories->end(); +                             ++it) +                        { +                            LLViewerInventoryCategory* child_cat = (*it); +                            if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion()) +                            { +                                mFetchFolderQueue.push_back(FetchQueueInfo(child_cat->getUUID(), FT_RECURSIVE)); +                            } +                        } +                    } +                    else +                    { +                        // send it back to get the rest +                        mFetchFolderQueue.push_back(FetchQueueInfo(cat_id, FT_CONTENT_RECURSIVE)); +                    } +                } +                else if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion() +                         || fetch_info.mFetchType == FT_FORCED) +                { +                    LLViewerInventoryCategory::EFetchType target_state = +                        fetch_info.mFetchType > FT_CONTENT_RECURSIVE +                        ? LLViewerInventoryCategory::FETCH_RECURSIVE +                        : LLViewerInventoryCategory::FETCH_NORMAL; +                    // start again if we did a non-recursive fetch before +                    // to get all children in a single request +                    if (cat->getFetching() < target_state) +                    { +                        // increment before call in case of immediate callback +                        incrFetchFolderCount(1); +                        cat->setFetching(target_state); +                        mExpectedFolderIds.push_back(cat_id); + +                        EFetchType type = fetch_info.mFetchType; +                        LLUUID cat_id = cat->getUUID(); +                        AISAPI::completion_t cb = [cat_id , type](const LLUUID& response_id) +                        { +                            LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(cat_id , response_id , type); +                        }; + +                        AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY; +                        if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) +                        { +                            item_type = AISAPI::LIBRARY; +                        } + +                        AISAPI::FetchCategoryChildren(cat_id , item_type , type == FT_RECURSIVE , cb, 0); +                    } +                } +                else +                { +                    // Already fetched, check if anything inside needs fetching +                    if (fetch_info.mFetchType == FT_RECURSIVE +                        || fetch_info.mFetchType == FT_FOLDER_AND_CONTENT) +                    { +                        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 +                            mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE)); +                        } +                    } +                } +            } // else try to fetch folder either way? +        } +    } +    else +    { +        LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); + +        if (itemp) +        { +            if (!itemp->isFinished() || fetch_info.mFetchType == FT_FORCED) +            { +                mFetchCount++; +                if (itemp->getPermissions().getOwner() == gAgent.getID()) +                { +                    AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback); +                } +                else +                { +                    AISAPI::FetchItem(fetch_info.mUUID, AISAPI::LIBRARY, ais_simple_item_callback); +                } +            } +        } +        else // We don't know it, assume incomplete +        { +            // Assume agent's inventory, library wouldn't have gotten here +            mFetchCount++; +            AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback); +        } +    } +} +  // Bundle up a bunch of requests to send all at once.  void LLInventoryModelBackgroundFetch::bulkFetch()  { @@ -376,13 +929,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)  	{ @@ -396,8 +942,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch()  		gInventory.notifyObservers();  	} -	if ((mFetchCount > max_concurrent_fetches) || -		(mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches)) +	if (mFetchCount > max_concurrent_fetches)  	{  		return;  	} @@ -417,95 +962,105 @@ void LLInventoryModelBackgroundFetch::bulkFetch()  	LLSD item_request_body;  	LLSD item_request_body_lib; -	while (! mFetchQueue.empty()  +	while (! mFetchFolderQueue.empty()   			&& (item_count + folder_count) < max_batch_size)  	{ -		const FetchQueueInfo & fetch_info(mFetchQueue.front()); +		const FetchQueueInfo & fetch_info(mFetchFolderQueue.front());  		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() -                        && std::find(all_cats.begin(), all_cats.end(), cat_id) == all_cats.end()) -					{ -						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); -				     -						if (ALEXANDRIA_LINDEN_ID == 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)); -						} -					} -				} +                const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); +                if (cat) +                { +                    if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) +                    { +                        if (std::find(all_cats.begin(), all_cats.end(), cat_id) == all_cats.end()) +                        { +                            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); + +                            if (ALEXANDRIA_LINDEN_ID == 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.mFetchType >= FT_CONTENT_RECURSIVE) +                        { +                            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) +                            { +                                mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mFetchType)); +                            } +                        } +                    } +                }  			} -			if (fetch_info.mRecursive) +			if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE)  			{  				recursive_cats.push_back(cat_id);  			}              all_cats.push_back(cat_id);  		} -		else -		{ -			LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); -			if (itemp) -			{ -				LLSD item_sd; -				item_sd["owner_id"] = itemp->getPermissions().getOwner(); -				item_sd["item_id"] = itemp->getUUID(); -				if (itemp->getPermissions().getOwner() == gAgent.getID()) -				{ -					item_request_body.append(item_sd); -				} -				else -				{ -					item_request_body_lib.append(item_sd); -				} -				//itemp->fetchFromServer(); -				item_count++; -			} -		} +        mFetchFolderQueue.pop_front(); +    } + + +    while (!mFetchItemQueue.empty() +        && (item_count + folder_count) < max_batch_size) +    { +        const FetchQueueInfo & fetch_info(mFetchItemQueue.front()); + +        LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); + +        if (itemp) +        { +            LLSD item_sd; +            item_sd["owner_id"] = itemp->getPermissions().getOwner(); +            item_sd["item_id"] = itemp->getUUID(); +            if (itemp->getPermissions().getOwner() == gAgent.getID()) +            { +                item_request_body.append(item_sd); +            } +            else +            { +                item_request_body_lib.append(item_sd); +            } +            //itemp->fetchFromServer(); +            item_count++; +        } -		mFetchQueue.pop_front(); +        mFetchItemQueue.pop_front();  	}  	// Issue HTTP POST requests to fetch folders and items @@ -576,14 +1131,22 @@ void LLInventoryModelBackgroundFetch::bulkFetch()  bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID & cat_id) const  { -	for (fetch_queue_t::const_iterator it = mFetchQueue.begin(); -		 it != mFetchQueue.end(); +	for (fetch_queue_t::const_iterator it = mFetchFolderQueue.begin(); +		 it != mFetchFolderQueue.end();  		 ++it)  	{  		const LLUUID & fetch_id = (*it).mUUID;  		if (gInventory.isObjectDescendentOf(fetch_id, cat_id))  			return false;  	} +    for (fetch_queue_t::const_iterator it = mFetchItemQueue.begin(); +        it != mFetchItemQueue.end(); +        ++it) +    { +        const LLUUID & fetch_id = (*it).mUUID; +        if (gInventory.isObjectDescendentOf(fetch_id, cat_id)) +            return false; +    }  	return true;  } @@ -829,10 +1392,10 @@ void BGFolderHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::Http              while (iter != end)              {                  folders.append(*iter); -                LLUUID fodler_id = iter->get("folder_id").asUUID(); -                if (std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), fodler_id) != mRecursiveCatUUIDs.end()) +                LLUUID folder_id = iter->get("folder_id").asUUID(); +                if (std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), folder_id) != mRecursiveCatUUIDs.end())                  { -                    recursive_cats.push_back(fodler_id); +                    recursive_cats.push_back(folder_id);                  }                  if (folders.size() == (S32)(size / 2))                  { diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h index 00d2908c1b..e7be265a3d 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.h +++ b/indra/newview/llinventorymodelbackgroundfetch.h @@ -47,9 +47,11 @@ class LLInventoryModelBackgroundFetch : public LLSingleton<LLInventoryModelBackg  	~LLInventoryModelBackgroundFetch();  public: -	// Start and stop background breadth-first fetching of inventory contents. +    // Start 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); +    void scheduleFolderFetch(const LLUUID& cat_id, bool forced = false); +    void scheduleItemFetch(const LLUUID& item_id, bool forced = false);  	BOOL folderFetchActive() const;  	bool isEverythingFetched() const; // completing the fetch once per session should be sufficient @@ -62,16 +64,47 @@ public:  	bool inventoryFetchCompleted() const;  	bool inventoryFetchInProgress() const; -    void findLostItems();	 -	void incrFetchCount(S32 fetching); +    void findLostItems(); +    void incrFetchCount(S32 fetching); +    void incrFetchFolderCount(S32 fetching);  	bool isBulkFetchProcessingComplete() const; +    bool isFolderFetchProcessingComplete() 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<void()> folders_fetched_callback_t; +    boost::signals2::connection setFetchCompletionCallback(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 { +        FT_DEFAULT = 0, +        FT_FORCED, // request non-recursively even if already loaded +        FT_CONTENT_RECURSIVE, // request content recursively +        FT_FOLDER_AND_CONTENT, // request folder, then content recursively +        FT_RECURSIVE, // request everything recursively +    } EFetchType; +    struct FetchQueueInfo +    { +        FetchQueueInfo(const LLUUID& id, EFetchType recursive, bool is_category = true) +            : mUUID(id), +            mIsCategory(is_category), +            mFetchType(recursive) +        {} + +        LLUUID mUUID; +        bool mIsCategory; +        EFetchType mFetchType; +    }; +    typedef std::deque<FetchQueueInfo> fetch_queue_t; + +    void onAISContentCalback(const LLUUID& request_id, const uuid_vec_t &content_ids, const LLUUID& response_id, EFetchType fetch_type); +    void onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type); +    void bulkFetchViaAis(); +    void bulkFetchViaAis(const FetchQueueInfo& fetch_info);  	void bulkFetch();  	void backgroundFetch(); @@ -80,31 +113,23 @@ protected:  	bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const;  private: - 	BOOL mRecursiveInventoryFetchStarted; -	BOOL mRecursiveLibraryFetchStarted; -	BOOL mAllFoldersFetched; + 	bool mRecursiveInventoryFetchStarted; +	bool mRecursiveLibraryFetchStarted; +	bool mAllRecursiveFoldersFetched; +    typedef boost::signals2::signal<void()> folders_fetched_signal_t; +    folders_fetched_signal_t mFoldersFetchedSignal; -	BOOL mBackgroundFetchActive; +    bool mBackgroundFetchActive;  	bool mFolderFetchActive;  	S32 mFetchCount; +    S32 mLastFetchCount; // for debug +    S32 mFetchFolderCount;  	LLFrameTimer mFetchTimer;  	F32 mMinTimeBetweenFetches; - -	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<FetchQueueInfo> fetch_queue_t; -	fetch_queue_t mFetchQueue; +	fetch_queue_t mFetchFolderQueue; +    fetch_queue_t mFetchItemQueue; +    std::list<LLUUID> mExpectedFolderIds; // for debug, should this track time?  };  #endif // LL_LLINVENTORYMODELBACKGROUNDFETCH_H diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index db0751cb89..281a8bc789 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -37,8 +37,10 @@  #include "llagent.h"  #include "llagentwearables.h" +#include "llaisapi.h"  #include "llfloater.h"  #include "llfocusmgr.h" +#include "llinventorymodelbackgroundfetch.h"  #include "llinventorybridge.h"  #include "llinventoryfunctions.h"  #include "llinventorymodel.h" @@ -56,6 +58,7 @@  #include "llsdutil.h"  #include <deque> +const S32 LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS = 7;  const F32 LLInventoryFetchItemsObserver::FETCH_TIMER_EXPIRY = 60.0f; @@ -149,7 +152,7 @@ LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const uuid_vec_t& i  void LLInventoryFetchItemsObserver::changed(U32 mask)  { -	LL_DEBUGS() << this << " remaining incomplete " << mIncomplete.size() +    LL_DEBUGS("InventoryFetch") << this << " remaining incomplete " << mIncomplete.size()  			 << " complete " << mComplete.size()  			 << " wait period " << mFetchingPeriod.getRemainingTimeF32()  			 << LL_ENDL; @@ -158,6 +161,15 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)  	// appropriate.  	if (!mIncomplete.empty())  	{ +        if (!LLInventoryModelBackgroundFetch::getInstance()->isEverythingFetched()) +        { +            // Folders have a priority over items and they download items as well +            // Wait untill initial folder fetch is done +            LL_DEBUGS("InventoryFetch") << "Folder fetch in progress, resetting fetch timer" << LL_ENDL; + +            mFetchingPeriod.reset(); +            mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY); +        }  		// Have we exceeded max wait time?  		bool timeout_expired = mFetchingPeriod.hasExpired(); @@ -176,7 +188,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)  				if (timeout_expired)  				{  					// Just concede that this item hasn't arrived in reasonable time and continue on. -					LL_WARNS() << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL; +                    LL_WARNS("InventoryFetch") << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL;  					it = mIncomplete.erase(it);  				}  				else @@ -191,7 +203,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)  	if (mIncomplete.empty())  	{ -		LL_DEBUGS() << this << " done at remaining incomplete " +        LL_DEBUGS("InventoryFetch") << this << " done at remaining incomplete "  				 << mIncomplete.size() << " complete " << mComplete.size() << LL_ENDL;  		done();  	} @@ -251,29 +263,21 @@ void fetch_items_from_llsd(const LLSD& items_llsd)  void LLInventoryFetchItemsObserver::startFetch()  { -	LLUUID owner_id; +    bool aisv3 = AISAPI::isAvailable(); +  	LLSD items_llsd; + +    typedef std::map<LLUUID, uuid_vec_t> requests_by_folders_t; +    requests_by_folders_t requests;  	for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it)  	{ -		LLViewerInventoryItem* item = gInventory.getItem(*it); -		if (item) -		{ -			if (item->isFinished()) -			{ -				// It's complete, so put it on the complete container. -				mComplete.push_back(*it); -				continue; -			} -			else -			{ -				owner_id = item->getPermissions().getOwner(); -			} -		} -		else -		{ -			// assume it's agent inventory. -			owner_id = gAgent.getID(); -		} +        LLViewerInventoryItem* item = gInventory.getItem(*it); +        if (item && item->isFinished()) +        { +            // It's complete, so put it on the complete container. +            mComplete.push_back(*it); +            continue; +        }  		// Ignore categories since they're not items.  We  		// could also just add this to mComplete but not sure what the @@ -294,17 +298,98 @@ void LLInventoryFetchItemsObserver::startFetch()  		// pack this on the message.  		mIncomplete.push_back(*it); -		// Prepare the data to fetch -		LLSD item_entry; -		item_entry["owner_id"] = owner_id; -		item_entry["item_id"] = (*it); -		items_llsd.append(item_entry); +        if (aisv3) +        { +            if (item) +            { +                LLUUID parent_id = item->getParentUUID(); +                requests[parent_id].push_back(*it); +            } +            else +            { +                // Can happen for gestures and calling cards if server notified us before they fetched +                // Request by id without checking for an item. +                LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(*it); +            } +        } +        else +        { +            // Prepare the data to fetch +            LLSD item_entry; +            if (item) +            { +                item_entry["owner_id"] = item->getPermissions().getOwner(); +            } +            else +            { +                // assume it's agent inventory. +                item_entry["owner_id"] = gAgent.getID(); +            } +            item_entry["item_id"] = (*it); +            items_llsd.append(item_entry); +        }  	}  	mFetchingPeriod.reset();  	mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY); -	fetch_items_from_llsd(items_llsd); +    if (aisv3) +    { +        for (requests_by_folders_t::value_type &folder : requests) +        { +            if (folder.second.size() > MAX_INDIVIDUAL_ITEM_REQUESTS) +            { +                // requesting one by one will take a while +                // do whole folder +                LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true); +            } +            else +            { +                LLViewerInventoryCategory* cat = gInventory.getCategory(folder.first); +                if (cat) +                { +                    if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) +                    { +                        // start fetching whole folder since it's not ready either way +                        cat->fetch(); +                    } +                    else if (cat->getViewerDescendentCount() <= folder.second.size() +                             || cat->getDescendentCount() <= folder.second.size()) +                    { +                        // Start fetching whole folder since we need all items +                        LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true); + +                    } +                    else +                    { +                        // get items one by one +                        for (LLUUID &item_id : folder.second) +                        { +                            LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id); +                        } +                    } +                } +                else +                { +                    // Isn't supposed to happen? We should have all folders +                    // and if item exists, folder is supposed to exist as well. +                    llassert(false); +                    LL_WARNS("Inventory") << "Missing folder: " << folder.first << " fetching items individually" << LL_ENDL; + +                    // get items one by one +                    for (LLUUID &item_id : folder.second) +                    { +                        LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id); +                    } +                } +            } +        } +    } +    else +    { +        fetch_items_from_llsd(items_llsd); +    } +  }  LLInventoryFetchDescendentsObserver::LLInventoryFetchDescendentsObserver(const LLUUID& cat_id) : @@ -649,6 +734,13 @@ void LLInventoryCategoriesObserver::changed(U32 mask)  			}  		} +        const LLUUID thumbnail_id = category->getThumbnailUUID(); +        if (cat_data.mThumbnailId != thumbnail_id) +        { +            cat_data.mThumbnailId = thumbnail_id; +            cat_changed = true; +        } +  		// If anything has changed above, fire the callback.  		if (cat_changed)  			cat_data.mCallback(); @@ -666,6 +758,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t  	S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN;  	S32 current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN;  	bool can_be_added = true; +    LLUUID thumbnail_id;  	LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);  	// If category could not be retrieved it might mean that @@ -677,6 +770,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t  		// Inventory category version is used to find out if some changes  		// to a category have been made.  		version = category->getVersion(); +        thumbnail_id = category->getThumbnailUUID();  		LLInventoryModel::cat_array_t* cats;  		LLInventoryModel::item_array_t* items; @@ -701,12 +795,12 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t  	{  		if(init_name_hash)  		{ -			digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id); -			mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents,item_name_hash))); +			digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id);			 +			mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents,item_name_hash)));  		}  		else  		{ -			mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents))); +			mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents)));  		}  	} @@ -719,23 +813,25 @@ void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id)  }  LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData( -	const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents) +	const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents)  	: mCatID(cat_id)  	, mCallback(cb)  	, mVersion(version)  	, mDescendentsCount(num_descendents) +    , mThumbnailId(thumbnail_id)  	, mIsNameHashInitialized(false)  {  }  LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData( -	const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash) +	const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash)  	: mCatID(cat_id)  	, mCallback(cb)  	, mVersion(version)  	, mDescendentsCount(num_descendents) +    , mThumbnailId(thumbnail_id)  	, mIsNameHashInitialized(true)  	, mItemNameHash(name_hash)  { diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index 4af8102055..bec08d2cdf 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -104,6 +104,9 @@ public:  	/*virtual*/ void startFetch();  	/*virtual*/ void changed(U32 mask); + +    // For attempts to group requests if too many items are requested +    static const S32 MAX_INDIVIDUAL_ITEM_REQUESTS;  private:  	LLTimer mFetchingPeriod; @@ -125,7 +128,7 @@ public:  	LLInventoryFetchDescendentsObserver(const LLUUID& cat_id = LLUUID::null);  	LLInventoryFetchDescendentsObserver(const uuid_vec_t& cat_ids); -	/*virtual*/ void startFetch(); +	virtual void startFetch();  	/*virtual*/ void changed(U32 mask);  protected:  	BOOL isCategoryComplete(const LLViewerInventoryCategory* cat) const; @@ -273,14 +276,15 @@ protected:  	typedef LLUUID digest_t; // To clarify the actual usage of this "UUID"  	struct LLCategoryData  	{ -		LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents); -		LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash); +		LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents); +		LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash);  		callback_t	mCallback;  		S32			mVersion;  		S32			mDescendentsCount;  		digest_t	mItemNameHash;  		bool		mIsNameHashInitialized;  		LLUUID		mCatID; +        LLUUID		mThumbnailId;  	};  	typedef	std::map<LLUUID, LLCategoryData>	category_map_t; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 2799cb4cdf..54f91451ac 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -40,6 +40,7 @@  #include "llfolderviewitem.h"  #include "llfloaterimcontainer.h"  #include "llimview.h" +#include "llinspecttexture.h"  #include "llinventorybridge.h"  #include "llinventoryfunctions.h"  #include "llinventorymodelbackgroundfetch.h" @@ -160,13 +161,15 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :  	mInvFVBridgeBuilder(NULL),  	mInventoryViewModel(p.name),  	mGroupedItemBridge(new LLFolderViewGroupedItemBridge), -	mFocusSelection(false) +	mFocusSelection(false), +    mBuildChildrenViews(true), +    mRootInited(false)  {  	mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER;  	if (!sColorSetInitialized)  	{ -		sDefaultColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); +		sDefaultColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE);  		sDefaultHighlightColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);  		sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE);  		sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); @@ -182,6 +185,7 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :  	mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this));  	mCommitCallbackRegistrar.add("Inventory.Share",  boost::bind(&LLAvatarActions::shareWithAvatars, this));  	mCommitCallbackRegistrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryPanel::fileUploadLocation, this, _2)); +    mCommitCallbackRegistrar.add("Inventory.OpenNewFolderWindow", boost::bind(&LLInventoryPanel::openSingleViewInventory, this, LLUUID()));  }  LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id ) @@ -248,52 +252,116 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)  {  	// save off copy of params  	mParams = params; -	// Clear up the root view -	// Note: This needs to be done *before* we build the new folder view  -	LLUUID root_id = getRootFolderID(); -	if (mFolderRoot.get()) -	{ -		removeItemID(root_id); -		mFolderRoot.get()->destroyView(); -	} -	mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves -	{ -		// Determine the root folder in case specified, and -		// build the views starting with that folder. +    initFolderRoot(); + +	// Initialize base class params. +	LLPanel::initFromParams(mParams); +} + +LLInventoryPanel::~LLInventoryPanel() +{ +	U32 sort_order = getFolderViewModel()->getSorter().getSortOrder(); +    if (mSortOrderSetting != INHERIT_SORT_ORDER) +    { +        gSavedSettings.setU32(mSortOrderSetting, sort_order); +    } +     +    clearFolderRoot(); +} + +void LLInventoryPanel::initFolderRoot() +{ +    // Clear up the root view +    // Note: This needs to be done *before* we build the new folder view +    LLUUID root_id = getRootFolderID(); +    if (mFolderRoot.get()) +    { +        removeItemID(root_id); +        mFolderRoot.get()->destroyView(); +    } + +    mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves +    { +        // Determine the root folder in case specified, and +        // build the views starting with that folder.          LLFolderView* folder_view = createFolderRoot(root_id); -		mFolderRoot = folder_view->getHandle(); -	 -		addItemID(root_id, mFolderRoot.get()); -	} -	mCommitCallbackRegistrar.popScope(); -	mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar); -	mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar); -	 -	// Scroller -		LLRect scroller_view_rect = getRect(); -		scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); -	LLScrollContainer::Params scroller_params(mParams.scroll()); -		scroller_params.rect(scroller_view_rect); -		mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); -		addChild(mScroller); -		mScroller->addChild(mFolderRoot.get()); -		mFolderRoot.get()->setScrollContainer(mScroller); -		mFolderRoot.get()->setFollowsAll(); -		mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox); - -	// Set up the callbacks from the inventory we're viewing, and then build everything. -	mInventoryObserver = new LLInventoryPanelObserver(this); -	mInventory->addObserver(mInventoryObserver); - -	mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); -	mInventory->addObserver(mCompletionObserver); - -    if (mBuildViewsOnInit && mViewsInitialized == VIEWS_UNINITIALIZED) +        mFolderRoot = folder_view->getHandle(); +        mRootInited = true; +     +        addItemID(root_id, mFolderRoot.get()); +    } +    mCommitCallbackRegistrar.popScope(); +    mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar); +    mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar); +     +    // Scroller +    LLRect scroller_view_rect = getRect(); +    scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); +    LLScrollContainer::Params scroller_params(mParams.scroll()); +    scroller_params.rect(scroller_view_rect); +    mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); +    addChild(mScroller); +    mScroller->addChild(mFolderRoot.get()); +    mFolderRoot.get()->setScrollContainer(mScroller); +    mFolderRoot.get()->setFollowsAll(); +    mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox); + +    if (mSelectionCallback)      { +        mFolderRoot.get()->setSelectCallback(mSelectionCallback); +    } + +    // Set up the callbacks from the inventory we're viewing, and then build everything. +    mInventoryObserver = new LLInventoryPanelObserver(this); +    mInventory->addObserver(mInventoryObserver); + +    mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); +    mInventory->addObserver(mCompletionObserver); + +    if (mBuildViewsOnInit) +    { +        initializeViewBuilding(); +    } + +    if (mSortOrderSetting != INHERIT_SORT_ORDER) +    { +        setSortOrder(gSavedSettings.getU32(mSortOrderSetting)); +    } +    else +    { +        setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); +    } + +    // hide inbox +    if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible")) +    { +        getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); +    } +    // hide marketplace listing box, unless we are a marketplace panel +    if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders) +    { +        getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS)); +    } +     +    // set the filter for the empty folder if the debug setting is on +    if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) +    { +        getFilter().setFilterEmptySystemFolders(); +    } +     +    // keep track of the clipboard state so that we avoid filtering too much +    mClipboardState = LLClipboard::instance().getGeneration(); +} + +void LLInventoryPanel::initializeViewBuilding() +{ +    if (mViewsInitialized == VIEWS_UNINITIALIZED) +    { +        LL_DEBUGS("Inventory") << "Setting views for " << getName() << " to initialize" << LL_ENDL;          // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle.          // Initializing views takes a while so always do it onIdle if viewer already loaded. -        if (mInventory->isInventoryUsable()             +        if (mInventory->isInventoryUsable()              && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)          {              // Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect @@ -306,49 +374,6 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)              gIdleCallbacks.addFunction(onIdle, (void*)this);          }      } - -	if (mSortOrderSetting != INHERIT_SORT_ORDER) -	{ -		setSortOrder(gSavedSettings.getU32(mSortOrderSetting)); -	} -	else -	{ -		setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); -	} - -	// hide inbox -	if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible")) -	{ -		getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); -	} -    // hide marketplace listing box, unless we are a marketplace panel -	if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders) -	{ -		getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS)); -    } -     -	// set the filter for the empty folder if the debug setting is on -	if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) -	{ -		getFilter().setFilterEmptySystemFolders(); -	} -	 -	// keep track of the clipboard state so that we avoid filtering too much -	mClipboardState = LLClipboard::instance().getGeneration(); -	 -	// Initialize base class params. -	LLPanel::initFromParams(mParams); -} - -LLInventoryPanel::~LLInventoryPanel() -{ -	U32 sort_order = getFolderViewModel()->getSorter().getSortOrder(); -    if (mSortOrderSetting != INHERIT_SORT_ORDER) -    { -        gSavedSettings.setU32(mSortOrderSetting, sort_order); -    } -     -    clearFolderRoot();  }  /*virtual*/ @@ -356,8 +381,11 @@ void LLInventoryPanel::onVisibilityChange(BOOL new_visibility)  {      if (new_visibility && mViewsInitialized == VIEWS_UNINITIALIZED)      { -        mViewsInitialized = VIEWS_INITIALIZING; -        gIdleCallbacks.addFunction(onIdle, (void*)this); +        // first call can be from tab initialization +        if (gFloaterView->getParentFloater(this) != NULL) +        { +            initializeViewBuilding(); +        }      }      LLPanel::onVisibilityChange(new_visibility);  } @@ -743,7 +771,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; @@ -878,6 +906,7 @@ void LLInventoryPanel::idle(void* user_data)  void LLInventoryPanel::initializeViews(F64 max_time)  {  	if (!gInventory.isInventoryUsable()) return; +    if (!mRootInited) return;      mViewsInitialized = VIEWS_BUILDING; @@ -905,7 +934,10 @@ void LLInventoryPanel::initializeViews(F64 max_time)  	gIdleCallbacks.addFunction(idle, this); -	openStartFolderOrMyInventory(); +    if(mParams.open_first_folder) +    { +        openStartFolderOrMyInventory(); +    }  	// Special case for new user login  	if (gAgent.isFirstLogin()) @@ -937,8 +969,8 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br  	params.tool_tip = params.name;      params.allow_drop = allow_drop; -	params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor)); -	params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor)); +	params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor); +	params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);  	return LLUICtrlFactory::create<LLFolderViewFolder>(params);  } @@ -954,8 +986,8 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge  	params.rect = LLRect (0, 0, 0, 0);  	params.tool_tip = params.name; -	params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor)); -	params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor)); +	params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor); +	params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor);  	return LLUICtrlFactory::create<LLFolderViewItem>(params);  } @@ -1011,8 +1043,11 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,                                                    LLInventoryObject const* objectp,                                                    LLFolderViewItem *folder_view_item,                                                    LLFolderViewFolder *parent_folder, -                                                  const EBuildModes &mode) +                                                  const EBuildModes &mode, +                                                  S32 depth)  { +    depth++; +      // Force the creation of an extra root level folder item if required by the inventory panel (default is "false")      bool allow_drop = true;      bool create_root = false; @@ -1042,7 +1077,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,  			if (objectp->getType() >= LLAssetType::AT_COUNT)    			{  				// Example: Happens when we add assets of new, not yet supported type to library -				LL_DEBUGS() << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : " +				LL_DEBUGS("Inventory") << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : "  				<< ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID()  				<< LL_ENDL; @@ -1112,7 +1147,8 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,          }  	} -    bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY; +    bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY +                            && (mBuildChildrenViews || depth == 0);      if (create_children)      { @@ -1132,12 +1168,15 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,                  {                      create_children = false;                      // run it again for the sake of creating children -                    mBuildViewsQueue.push_back(id); +                    if (mBuildChildrenViews || depth == 0) +                    { +                        mBuildViewsQueue.push_back(id); +                    }                  }                  else                  {                      create_children = true; -                    folder_view_item->setChildrenInited(true); +                    folder_view_item->setChildrenInited(mBuildChildrenViews);                  }                  break;              } @@ -1145,7 +1184,10 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,              {                  create_children = false;                  // run it to create children, current caller is only interested in current view -                mBuildViewsQueue.push_back(id); +                if (mBuildChildrenViews || depth == 0) +                { +                    mBuildViewsQueue.push_back(id); +                }                  break;              }              case BUILD_ONE_FOLDER: @@ -1175,7 +1217,13 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,  		LLViewerInventoryItem::item_array_t* items;  		mInventory->lockDirectDescendentArrays(id, categories, items); +        // Make sure panel won't lock in a loop over existing items if +        // folder is enormous and at least some work gets done +        const S32 MIN_ITEMS_PER_CALL = 500; +        const S32 starting_item_count = mItemMap.size(); +          LLFolderViewFolder *parentp = dynamic_cast<LLFolderViewFolder*>(folder_view_item); +        bool done = true;  		if(categories)          { @@ -1193,11 +1241,28 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,                          // each time, especially since content is growing, we can just                          // iter over copy of mItemMap in some way                          LLFolderViewItem* view_itemp = getItemByID(cat->getUUID()); -                        buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode)); +                        buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth);                      }                      else                      { -                        buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode)); +                        buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth); +                    } +                } + +                if (!mBuildChildrenViews +                    && mode == BUILD_TIMELIMIT +                    && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size()) +                { +                    // Single folder view, check if we still have time +                    //  +                    // Todo: make sure this causes no dupplciates, breaks nothing, +                    // especially filters and arrange +                    F64 curent_time = LLTimer::getTotalSeconds(); +                    if (mBuildViewsEndTime < curent_time) +                    { +                        mBuildViewsQueue.push_back(id); +                        done = false; +                        break;                      }                  }  			} @@ -1217,10 +1282,33 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,                      // each time, especially since content is growing, we can just                      // iter over copy of mItemMap in some way                      LLFolderViewItem* view_itemp = getItemByID(item->getUUID()); -                    buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode); +                    buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode, depth); +                } + +                if (!mBuildChildrenViews +                    && mode == BUILD_TIMELIMIT +                    && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size()) +                { +                    // Single folder view, check if we still have time +                    //  +                    // Todo: make sure this causes no dupplciates, breaks nothing, +                    // especially filters and arrange +                    F64 curent_time = LLTimer::getTotalSeconds(); +                    if (mBuildViewsEndTime < curent_time) +                    { +                        mBuildViewsQueue.push_back(id); +                        done = false; +                        break; +                    }                  }  			}  		} + +        if (!mBuildChildrenViews && done) +        { +            // flat list is done initializing folder +            folder_view_item->setChildrenInited(true); +        }  		mInventory->unlockDirectDescendentArrays(id);  	} @@ -1285,6 +1373,37 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)  	return TRUE;  } +BOOL LLInventoryPanel::handleToolTip(S32 x, S32 y, MASK mask) +{ +	if (const LLFolderViewItem* hover_item_p = (!mFolderRoot.isDead()) ? mFolderRoot.get()->getHoveredItem() : nullptr) +	{ +		if (const LLFolderViewModelItemInventory* vm_item_p = static_cast<const LLFolderViewModelItemInventory*>(hover_item_p->getViewModelItem())) +		{ +            LLSD params; +            params["inv_type"] = vm_item_p->getInventoryType(); +            params["thumbnail_id"] = vm_item_p->getThumbnailUUID(); +            params["item_id"] = vm_item_p->getUUID(); + +            // tooltip should only show over folder, but screen +            // rect includes items under folder as well +            LLRect actionable_rect = hover_item_p->calcScreenRect(); +            if (hover_item_p->isOpen() && hover_item_p->hasVisibleChildren()) +            { +                actionable_rect.mBottom = actionable_rect.mTop - hover_item_p->getItemHeight(); +            } + +			LLToolTipMgr::instance().show(LLToolTip::Params() +					.message(hover_item_p->getToolTip()) +					.sticky_rect(actionable_rect) +					.delay_time(LLView::getTooltipTimeout()) +					.create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) +					.create_params(params)); +			return TRUE; +		} +	} +	return LLPanel::handleToolTip(x, y, mask); +} +  BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,  								   EDragAndDropType cargo_type,  								   void* cargo_data, @@ -1330,6 +1449,45 @@ void LLInventoryPanel::onFocusReceived()  	// inventory now handles cut/copy/paste/delete  	LLEditMenuHandler::gEditMenuHandler = mFolderRoot.get(); +    // Tab support, when tabbing into this view, select first item +    // (ideally needs to account for scroll) +    bool select_first = mSelectThisID.isNull() && mFolderRoot.get() && mFolderRoot.get()->getSelectedCount() == 0; + +    if (select_first) +    { +        LLFolderViewFolder::folders_t::const_iterator folders_it = mFolderRoot.get()->getFoldersBegin(); +        LLFolderViewFolder::folders_t::const_iterator folders_end = mFolderRoot.get()->getFoldersEnd(); + +        for (; folders_it != folders_end; ++folders_it) +        { +            const LLFolderViewFolder* folder_view = *folders_it; +            if (folder_view->getVisible()) +            { +                const LLFolderViewModelItemInventory* modelp = static_cast<const LLFolderViewModelItemInventory*>(folder_view->getViewModelItem()); +                setSelectionByID(modelp->getUUID(), TRUE); +                select_first = false; +                break; +            } +        } +    } + +    if (select_first) +    { +        LLFolderViewFolder::items_t::const_iterator items_it = mFolderRoot.get()->getItemsBegin(); +        LLFolderViewFolder::items_t::const_iterator items_end = mFolderRoot.get()->getItemsEnd(); + +        for (; items_it != items_end; ++items_it) +        { +            const LLFolderViewItem* item_view = *items_it; +            if (item_view->getVisible()) +            { +                const LLFolderViewModelItemInventory* modelp = static_cast<const LLFolderViewModelItemInventory*>(item_view->getViewModelItem()); +                setSelectionByID(modelp->getUUID(), TRUE); +                break; +            } +        } +    } +  	LLPanel::onFocusReceived();  } @@ -1380,6 +1538,7 @@ void LLInventoryPanel::setSelectCallback(const boost::function<void (const std::  	{  		mFolderRoot.get()->setSelectCallback(cb);  	} +    mSelectionCallback = cb;  }  void LLInventoryPanel::clearSelection() @@ -1426,6 +1585,10 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it  		{  			fv->startRenamingSelectedItem();  		} +        else +        { +            LL_DEBUGS("Inventory") << "Failed to start renemr, no items selected" << LL_ENDL; +        }  	}  	std::set<LLFolderViewItem*> selected_items = mFolderRoot.get()->getSelectionList(); @@ -1621,6 +1784,11 @@ void LLInventoryPanel::fileUploadLocation(const LLSD& userdata)      }  } +void LLInventoryPanel::openSingleViewInventory(LLUUID folder_id) +{ +    LLPanelMainInventory::newFolderWindow(folder_id.isNull() ? LLFolderBridge::sSelf.get()->getUUID() : folder_id); +} +  void LLInventoryPanel::purgeSelectedItems()  {      if (!mFolderRoot.get()) return; @@ -1754,22 +1922,49 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open)  }  //static -void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL main_panel, BOOL take_keyboard_focus, BOOL reset_filter) +void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL use_main_panel, BOOL take_keyboard_focus, BOOL reset_filter)  {  	LLSidepanelInventory* sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");  	sidepanel_inventory->showInventoryPanel();  	bool in_inbox = (gInventory.isObjectDescendentOf(obj_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX))); -	if (!in_inbox && (main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected())) +	if (!in_inbox && (use_main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected()))  	{  		sidepanel_inventory->selectAllItemsPanel();  	} + +    LLFloater* inventory_floater = LLFloaterSidePanelContainer::getTopmostInventoryFloater(); +    if(!auto_open && inventory_floater && inventory_floater->getVisible()) +    { +        LLSidepanelInventory *inventory_panel = inventory_floater->findChild<LLSidepanelInventory>("main_panel"); +        LLPanelMainInventory* main_panel = inventory_panel->getMainInventoryPanel(); +        if(main_panel->isSingleFolderMode() && main_panel->isGalleryViewMode()) +        { +            LL_DEBUGS("Inventory") << "Opening gallery panel for item" << obj_id << LL_ENDL; +            main_panel->setGallerySelection(obj_id); +            return; +        } +    } + +    LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); +    if (main_inventory && main_inventory->isSingleFolderMode() +        && use_main_panel) +    { +        const LLInventoryObject *obj = gInventory.getObject(obj_id); +        if (obj) +        { +            LL_DEBUGS("Inventory") << "Opening main inventory panel for item" << obj_id << LL_ENDL; +            main_inventory->setSingleFolderViewRoot(obj->getParentUUID(), false); +            main_inventory->setGallerySelection(obj_id); +            return; +        } +    }  	LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open);  	if (active_panel)  	{ -		LL_DEBUGS("Messaging") << "Highlighting" << obj_id  << LL_ENDL; +		LL_DEBUGS("Messaging", "Inventory") << "Highlighting" << obj_id  << LL_ENDL;  		if (reset_filter)  		{ @@ -1788,7 +1983,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L  				inventory_panel->setSelection(obj_id, take_keyboard_focus);  			}  		} -		else +		else if (auto_open)  		{  			LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory");  			if (floater_inventory) @@ -1797,9 +1992,33 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L  			}  			active_panel->setSelection(obj_id, take_keyboard_focus);  		} +        else +        { +            // Created items are going to receive proper focus from callbacks +            active_panel->setSelection(obj_id, take_keyboard_focus); +        }  	}  } +void LLInventoryPanel::setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id) +{ + +    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(); ++iter) +    { +        LLFloaterSidePanelContainer* inventory_floater = dynamic_cast<LLFloaterSidePanelContainer*>(*iter); +        LLSidepanelInventory* sidepanel_inventory = inventory_floater->findChild<LLSidepanelInventory>("main_panel"); + +        LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); +        if (main_inventory && panel->hasAncestor(main_inventory) && !main_inventory->isSingleFolderMode()) +        { +            main_inventory->initSingleFolderRoot(folder_id); +            main_inventory->toggleViewMode(); +            main_inventory->setSingleFolderViewRoot(folder_id, false); +        } +    } +} +  void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type)  {  	getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type)); @@ -2008,10 +2227,205 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params)  	mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER;  } +static LLDefaultChildRegistry::Register<LLInventorySingleFolderPanel> t_single_folder_inventory_panel("single_folder_inventory_panel"); + +LLInventorySingleFolderPanel::LLInventorySingleFolderPanel(const Params& params) +    : LLInventoryPanel(params) +{ +    mBuildChildrenViews = false; +    getFilter().setSingleFolderMode(true); +    getFilter().setEmptyLookupMessage("InventorySingleFolderNoMatches"); +    getFilter().setDefaultEmptyLookupMessage("InventorySingleFolderEmpty"); + +    mCommitCallbackRegistrar.replace("Inventory.DoToSelected", boost::bind(&LLInventorySingleFolderPanel::doToSelected, this, _2)); +    mCommitCallbackRegistrar.replace("Inventory.DoCreate", boost::bind(&LLInventorySingleFolderPanel::doCreate, this, _2)); +    mCommitCallbackRegistrar.replace("Inventory.Share", boost::bind(&LLInventorySingleFolderPanel::doShare, this)); +} + +LLInventorySingleFolderPanel::~LLInventorySingleFolderPanel() +{ +} + +void LLInventorySingleFolderPanel::initFromParams(const Params& p) +{ +    mFolderID = gInventory.getRootFolderID(); + +    mParams = p; +    LLPanel::initFromParams(mParams); +} + +void LLInventorySingleFolderPanel::initFolderRoot(const LLUUID& start_folder_id) +{ +    if(mRootInited) return; + +    mRootInited = true; +    if(start_folder_id.notNull()) +    { +        mFolderID = start_folder_id; +    } + +    mParams.open_first_folder = false; +    mParams.start_folder.id = mFolderID; + +    LLInventoryPanel::initFolderRoot(); +    mFolderRoot.get()->setSingleFolderMode(true); +} + +void LLInventorySingleFolderPanel::changeFolderRoot(const LLUUID& new_id) +{ +    if (mFolderID != new_id) +    { +        if(mFolderID.notNull()) +        { +            mBackwardFolders.push_back(mFolderID); +        } +        mFolderID = new_id; +        updateSingleFolderRoot(); +    } +} + +void LLInventorySingleFolderPanel::onForwardFolder() +{ +    if(isForwardAvailable()) +    { +        mBackwardFolders.push_back(mFolderID); +        mFolderID = mForwardFolders.back(); +        mForwardFolders.pop_back(); +        updateSingleFolderRoot(); +    } +} + +void LLInventorySingleFolderPanel::onBackwardFolder() +{ +    if(isBackwardAvailable()) +    { +        mForwardFolders.push_back(mFolderID); +        mFolderID = mBackwardFolders.back(); +        mBackwardFolders.pop_back(); +        updateSingleFolderRoot(); +    } +} + +void LLInventorySingleFolderPanel::clearNavigationHistory() +{ +    mForwardFolders.clear(); +    mBackwardFolders.clear(); +} + +bool LLInventorySingleFolderPanel::isBackwardAvailable() +{ +    return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back())); +} + +bool LLInventorySingleFolderPanel::isForwardAvailable() +{ +    return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back())); +} + +boost::signals2::connection LLInventorySingleFolderPanel::setRootChangedCallback(root_changed_callback_t cb) +{ +    return mRootChangedSignal.connect(cb); +} + +void LLInventorySingleFolderPanel::updateSingleFolderRoot() +{ +    if (mFolderID != getRootFolderID()) +    { +        mRootChangedSignal(); + +        LLUUID root_id = mFolderID; +        if (mFolderRoot.get()) +        { +            mItemMap.clear(); +            mFolderRoot.get()->destroyRoot(); +        } + +        mCommitCallbackRegistrar.pushScope(); +        { +            LLFolderView* folder_view = createFolderRoot(root_id); +            folder_view->setChildrenInited(false); +            mFolderRoot = folder_view->getHandle(); +            mFolderRoot.get()->setSingleFolderMode(true); +            addItemID(root_id, mFolderRoot.get()); + +            LLRect scroller_view_rect = getRect(); +            scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); +            LLScrollContainer::Params scroller_params(mParams.scroll()); +            scroller_params.rect(scroller_view_rect); + +            if (mScroller) +            { +                removeChild(mScroller); +                delete mScroller; +                mScroller = NULL; +            } +            mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); +            addChild(mScroller); +            mScroller->addChild(mFolderRoot.get()); +            mFolderRoot.get()->setScrollContainer(mScroller); +            mFolderRoot.get()->setFollowsAll(); +            mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox); + +            if (!mSelectionCallback.empty()) +            { +                mFolderRoot.get()->setSelectCallback(mSelectionCallback); +            } +        } +        mCommitCallbackRegistrar.popScope(); +        mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar); + +        buildNewViews(mFolderID); +         +        LLFloater* root_floater = gFloaterView->getParentFloater(this); +        if(root_floater) +        { +            root_floater->setFocus(true); +        } +    } +} + +bool LLInventorySingleFolderPanel::hasVisibleItems() +{ +    return mFolderRoot.get()->hasVisibleChildren(); +} + +void LLInventorySingleFolderPanel::doCreate(const LLSD& userdata) +{ +    std::string type_name = userdata.asString(); +    LLUUID dest_id = LLFolderBridge::sSelf.get()->getUUID(); +    if (("category" == type_name) || ("outfit" == type_name)) +    { +        changeFolderRoot(dest_id); +    } +    reset_inventory_filter(); +    menu_create_inventory_item(this, dest_id, userdata); +} + +void LLInventorySingleFolderPanel::doToSelected(const LLSD& userdata) +{ +    if (("open_in_current_window" == userdata.asString())) +    { +        changeFolderRoot(LLFolderBridge::sSelf.get()->getUUID()); +        return; +    } +    LLInventoryPanel::doToSelected(userdata); +} + +void LLInventorySingleFolderPanel::doShare() +{ +    LLAvatarActions::shareWithAvatars(this); +}  /************************************************************************/  /* Asset Pre-Filtered Inventory Panel related class                     */  /************************************************************************/ +LLAssetFilteredInventoryPanel::LLAssetFilteredInventoryPanel(const Params& p) +    : LLInventoryPanel(p) +    , mAssetType(LLAssetType::AT_NONE) +{ +} + +  void LLAssetFilteredInventoryPanel::initFromParams(const Params& p)  {      mAssetType = LLAssetType::lookup(p.filter_asset_type.getValue()); diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 2c782a5ea7..341be0cf86 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -107,6 +107,7 @@ public:  		Optional<LLFolderView::Params>		folder_view;  		Optional<LLFolderViewFolder::Params> folder;  		Optional<LLFolderViewItem::Params>	 item; +        Optional<bool>                       open_first_folder;          // All item and folder views will be initialized on init if true (default)          // Will initialize on visibility change otherwise. @@ -126,6 +127,7 @@ public:              show_root_folder("show_root_folder", false),              allow_drop_on_root("allow_drop_on_root", true),              use_marketplace_folders("use_marketplace_folders", false), +            open_first_folder("open_first_folder", true),  			scroll("scroll"),  			accepts_drag_and_drop("accepts_drag_and_drop"),  			folder_view("folder_view"), @@ -159,22 +161,23 @@ public:  	LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; }  	// LLView methods -	/*virtual*/ void onVisibilityChange(BOOL new_visibility); -	void draw(); -	/*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); -	BOOL handleHover(S32 x, S32 y, MASK mask); +	/*virtual*/ void onVisibilityChange(BOOL new_visibility) override; +	void draw() override; +	/*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ) override; +	BOOL handleHover(S32 x, S32 y, MASK mask) override;  	/*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,  								   EDragAndDropType cargo_type,  								   void* cargo_data,  								   EAcceptance* accept, -								   std::string& tooltip_msg); +								   std::string& tooltip_msg) override; +	            BOOL handleToolTip(S32 x, S32 y, MASK mask) override;  	// LLUICtrl methods -	 /*virtual*/ void onFocusLost(); -	 /*virtual*/ void onFocusReceived(); +	 /*virtual*/ void onFocusLost() override; +	 /*virtual*/ void onFocusReceived() override;       void onFolderOpening(const LLUUID &id);  	// LLBadgeHolder methods -	bool addBadge(LLBadge * badge); +	bool addBadge(LLBadge * badge) override;  	// Call this method to set the selection.  	void openAllFolders(); @@ -211,6 +214,7 @@ public:  	LLUUID getRootFolderID();  	LLScrollContainer* getScrollableContainer() { return mScroller; }      bool getAllowDropOnRoot() { return mParams.allow_drop_on_root; } +    bool areViewsInitialized() { return mViewsInitialized == VIEWS_INITIALIZED && mFolderRoot.get() && !mFolderRoot.get()->needsArrange(); }  	void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action); @@ -221,6 +225,7 @@ public:  	void doCreate(const LLSD& userdata);  	bool beginIMSession();  	void fileUploadLocation(const LLSD& userdata); +    void openSingleViewInventory(LLUUID folder_id = LLUUID());  	void purgeSelectedItems();  	bool attachObject(const LLSD& userdata);  	static void idle(void* user_data); @@ -241,10 +246,10 @@ public:  	static void openInventoryPanelAndSetSelection(BOOL auto_open,  													const LLUUID& obj_id, -													BOOL main_panel = FALSE, +													BOOL use_main_panel = FALSE,  													BOOL take_keyboard_focus = TAKE_FOCUS_YES,  													BOOL reset_filter = FALSE); - +    static void setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id);  	void addItemID(const LLUUID& id, LLFolderViewItem* itemp);  	void removeItemID(const LLUUID& id);  	LLFolderViewItem* getItemByID(const LLUUID& id); @@ -262,6 +267,10 @@ public:      static void callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response, const std::vector<LLUUID> inventory_selected); +    void changeFolderRoot(const LLUUID& new_id) {}; +    void initFolderRoot(); +    void initializeViewBuilding(); +  protected:  	void openStartFolderOrMyInventory(); // open the first level of inventory  	void onItemsCompletion();			// called when selected items are complete @@ -298,6 +307,9 @@ protected:  	 */  	const LLInventoryFolderViewModelBuilder* mInvFVBridgeBuilder; +    bool mBuildChildrenViews; // build root and children +    bool mRootInited; +  	//--------------------------------------------------------------------  	// Sorting @@ -357,6 +369,8 @@ protected:      virtual LLFolderView * createFolderRoot(LLUUID root_id );  	virtual LLFolderViewFolder*	createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop);  	virtual LLFolderViewItem*	createFolderViewItem(LLInvFVBridge * bridge); + +    boost::function<void(const std::deque<LLFolderViewItem*>& items, BOOL user_action)> mSelectionCallback;  private:      // buildViewsTree does not include some checks and is meant      // for recursive use, use buildNewViews() for first call @@ -365,7 +379,8 @@ private:                                                LLInventoryObject const* objectp,                                                LLFolderViewItem *target_view,                                                LLFolderViewFolder *parent_folder_view, -                                              const EBuildModes &mode); +                                              const EBuildModes &mode, +                                              S32 depth = -1);      typedef enum e_views_initialization_state      { @@ -381,6 +396,55 @@ private:      std::deque<LLUUID>			mBuildViewsQueue;  }; + +class LLInventorySingleFolderPanel : public LLInventoryPanel +{ +public: +    struct Params : public LLInitParam::Block<Params, LLInventoryPanel::Params> +    {}; + +    void initFromParams(const Params& p); +    bool isSelectionRemovable() { return false; } + +    void initFolderRoot(const LLUUID& start_folder_id = LLUUID::null); + +    void changeFolderRoot(const LLUUID& new_id); +    void onForwardFolder(); +    void onBackwardFolder(); +    void clearNavigationHistory(); +    LLUUID getSingleFolderRoot() { return mFolderID; } + +    void doCreate(const LLSD& userdata); +    void doToSelected(const LLSD& userdata); +    void doShare(); + +    bool isBackwardAvailable(); +    bool isForwardAvailable(); + +    bool hasVisibleItems(); + +    void setNavBackwardList(std::list<LLUUID> backward_list) { mBackwardFolders = backward_list; } +    void setNavForwardList(std::list<LLUUID> forward_list) { mForwardFolders = forward_list; } +    std::list<LLUUID> getNavBackwardList() { return mBackwardFolders; } +    std::list<LLUUID> getNavForwardList() { return mForwardFolders; } + +    typedef boost::function<void()> root_changed_callback_t; +    boost::signals2::connection setRootChangedCallback(root_changed_callback_t cb); + +protected: +    LLInventorySingleFolderPanel(const Params& params); +    ~LLInventorySingleFolderPanel(); +    void updateSingleFolderRoot(); + +    friend class LLUICtrlFactory; +     +    LLUUID mFolderID; +    std::list<LLUUID> mBackwardFolders; +    std::list<LLUUID> mForwardFolders; + +    boost::signals2::signal<void()> mRootChangedSignal; +}; +  /************************************************************************/  /* Asset Pre-Filtered Inventory Panel related class                     */  /* Exchanges filter's flexibility for speed of generation and           */ @@ -400,7 +464,7 @@ public:      void initFromParams(const Params& p);  protected: -    LLAssetFilteredInventoryPanel(const Params& p) : LLInventoryPanel(p) {} +    LLAssetFilteredInventoryPanel(const Params& p);      friend class LLUICtrlFactory;  public:      ~LLAssetFilteredInventoryPanel() {} diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index dd8c9b2dde..01496fa7ce 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -87,6 +87,7 @@ LLLoginInstance::LLLoginInstance() :  	mLoginModule(new LLLogin()),  	mNotifications(NULL),  	mLoginState("offline"), +    mSaveMFA(true),  	mAttemptComplete(false),  	mTransferRate(0.0f),  	mDispatcher("LLLoginInstance", "change") @@ -449,10 +450,7 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)              gViewerWindow->setShowProgress(FALSE);          } -        LLSD args(llsd::map( "MESSAGE", LLTrans::getString(response["message_id"]) )); -        LLSD payload; -        LLNotificationsUtil::add("PromptMFAToken", args, payload, -            boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); +        showMFAChallange(LLTrans::getString(response["message_id"]));      }      else if(   reason_response == "key"              || reason_response == "presence" @@ -540,10 +538,7 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)          {              // SL-18511 this TOS failure happened while we are in the middle of an MFA challenge/response.              // the previously entered token is very likely expired, so prompt again -            LLSD args(llsd::map( "MESSAGE", LLTrans::getString("LoginFailedAuthenticationMFARequired") )); -            LLSD payload; -            LLNotificationsUtil::add("PromptMFAToken", args, payload, -                boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); +            showMFAChallange(LLTrans::getString("LoginFailedAuthenticationMFARequired"));          }          else          { @@ -561,6 +556,22 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)      return true;  } +void LLLoginInstance::showMFAChallange(const std::string& message) +{ +    LLSD args(llsd::map("MESSAGE", message)); +    LLSD payload; +    if (gSavedSettings.getBOOL("RememberUser")) +    { +        LLNotificationsUtil::add("PromptMFATokenWithSave", args, payload, +                                 boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); +    } +    else +    { +        LLNotificationsUtil::add("PromptMFAToken", args, payload, +                                 boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); +    } +} +  bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & response)  {      bool continue_clicked = response["continue"].asBoolean(); @@ -576,6 +587,7 @@ bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & respon          // Set the request data to true and retry login.          mRequestData["params"]["token"] = token; +        mSaveMFA = response.has("ignore") ? response["ignore"].asBoolean() : false;          reconnect();      } else {          LL_INFOS("LLLogin") << "PromptMFAToken: no token, attemptComplete" << LL_ENDL; diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index ee3ef0e4b1..2e9aab7c00 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -56,6 +56,7 @@ public:  	bool authSuccess() { return mAttemptComplete && mLoginState == "online"; }  	const std::string& getLoginState() { return mLoginState; } +    bool saveMFA() const { return mSaveMFA; }  	LLSD getResponse(const std::string& key) { return getResponse()[key]; }  	LLSD getResponse(); @@ -84,6 +85,7 @@ private:  	void syncWithUpdater(ResponsePtr resp, const LLSD& notification, const LLSD& response);  	bool handleTOSResponse(bool v, const std::string& key); +    void showMFAChallange(const std::string& message);      bool handleMFAChallenge(LLSD const & notif, LLSD const & response);  	void attemptComplete() { mAttemptComplete = true; } // In the future an event? @@ -95,6 +97,7 @@ private:  	LLSD mRequestData;  	LLSD mResponseData;  	bool mAttemptComplete; +    bool mSaveMFA;  	F64 mTransferRate;  	std::string mSerialNumber;  	int mLastExecEvent; diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 6f3d40bb3a..8784f403cb 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -700,10 +700,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 @@ -897,7 +896,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", @@ -1804,7 +1803,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<LLUUID>::iterator it = mPendingUpdateSet.find(marketplace_listings_uuid);          if (it != mPendingUpdateSet.end())          { @@ -1848,8 +1847,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/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 602b7412a4..de988555c5 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -36,12 +36,11 @@  #include "llaccordionctrltab.h"  #include "llappearancemgr.h" -#include "llagentbenefits.h"  #include "llerror.h"  #include "llfilepicker.h"  #include "llfloaterperms.h"  #include "llfloaterreg.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h"  #include "llimagedimensionsinfo.h"  #include "llinventoryfunctions.h"  #include "llinventorymodel.h" @@ -65,7 +64,6 @@ const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;  LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p)      : LLOutfitListBase(), -      mTexturesObserver(NULL),        mOutfitsObserver(NULL),        mScrollPanel(NULL),        mGalleryPanel(NULL), @@ -125,7 +123,6 @@ void LLOutfitGallery::onOpen(const LLSD& info)      LLOutfitListBase::onOpen(info);      if (!mGalleryCreated)      { -        loadPhotos();          uuid_vec_t cats;          getCurrentCategories(cats);          int n = cats.size(); @@ -149,6 +146,264 @@ void LLOutfitGallery::draw()      }  } +BOOL LLOutfitGallery::handleKeyHere(KEY key, MASK mask) +{ +    BOOL handled = FALSE; +    switch (key) +    { +        case KEY_RETURN: +            // Open selected items if enter key hit on the inventory panel +            if (mask == MASK_NONE && mSelectedOutfitUUID.notNull()) +            { +                // Or should it wearSelectedOutfit? +                getSelectedItem()->openOutfitsContent(); +            } +            handled = TRUE; +            break; +        case KEY_DELETE: +#if LL_DARWIN +        case KEY_BACKSPACE: +#endif +            // Delete selected items if delete or backspace key hit on the inventory panel +            // Note: on Mac laptop keyboards, backspace and delete are one and the same +            if (mSelectedOutfitUUID.notNull()) +            { +                onRemoveOutfit(mSelectedOutfitUUID); +            } +            handled = TRUE; +            break; + +        case KEY_F2: +            LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID); +            handled = TRUE; +            break; + +        case KEY_PAGE_UP: +            if (mScrollPanel) +            { +                mScrollPanel->pageUp(30); +            } +            handled = TRUE; +            break; + +        case KEY_PAGE_DOWN: +            if (mScrollPanel) +            { +                mScrollPanel->pageDown(30); +            } +            handled = TRUE; +            break; + +        case KEY_HOME: +            if (mScrollPanel) +            { +                mScrollPanel->goToTop(); +            } +            handled = TRUE; +            break; + +        case KEY_END: +            if (mScrollPanel) +            { +                mScrollPanel->goToBottom(); +            } +            handled = TRUE; +            break; + +        case KEY_LEFT: +            moveLeft(); +            handled = TRUE; +            break; + +        case KEY_RIGHT: +            moveRight(); +            handled = TRUE; +            break; + +        case KEY_UP: +            moveUp(); +            handled = TRUE; +            break; + +        case KEY_DOWN: +            moveDown(); +            handled = TRUE; +            break; + +        default: +            break; +    } + +    if (handled) +    { +        mOutfitGalleryMenu->hide(); +    } + +    return handled; +} + +void LLOutfitGallery::moveUp() +{ +    if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) +    { +        LLOutfitGalleryItem* item = getSelectedItem(); +        if (item) +        { +            S32 n = mItemIndexMap[item]; +            n -= mItemsInRow; +            if (n >= 0) +            { +                item = mIndexToItemMap[n]; +                LLUUID item_id = item->getUUID(); +                ChangeOutfitSelection(nullptr, item_id); +                item->setFocus(TRUE); + +                scrollToShowItem(mSelectedOutfitUUID); +            } +        } +    } +} + +void LLOutfitGallery::moveDown() +{ +    if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) +    { +        LLOutfitGalleryItem* item = getSelectedItem(); +        if (item) +        { +            S32 n = mItemIndexMap[item]; +            n += mItemsInRow; +            if (n < mItemsAddedCount) +            { +                item = mIndexToItemMap[n]; +                LLUUID item_id = item->getUUID(); +                ChangeOutfitSelection(nullptr, item_id); +                item->setFocus(TRUE); + +                scrollToShowItem(mSelectedOutfitUUID); +            } +        } +    } +} + +void LLOutfitGallery::moveLeft() +{ +    if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) +    { +        LLOutfitGalleryItem* item = getSelectedItem(); +        if (item) +        { +            // Might be better to get item from panel +            S32 n = mItemIndexMap[item]; +            n--; +            if (n < 0) +            { +                n = mItemsAddedCount - 1; +            } +            item = mIndexToItemMap[n]; +            LLUUID item_id = item->getUUID(); +            ChangeOutfitSelection(nullptr, item_id); +            item->setFocus(TRUE); + +            scrollToShowItem(mSelectedOutfitUUID); +        } +    } +} + +void LLOutfitGallery::moveRight() +{ +    if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) +    { +        LLOutfitGalleryItem* item = getSelectedItem(); +        if (item) +        { +            S32 n = mItemIndexMap[item]; +            n++; +            if (n == mItemsAddedCount) +            { +                n = 0; +            } +            item = mIndexToItemMap[n]; +            LLUUID item_id = item->getUUID(); +            ChangeOutfitSelection(nullptr, item_id); +            item->setFocus(TRUE); + +            scrollToShowItem(mSelectedOutfitUUID); +        } +    } +} + +void LLOutfitGallery::onFocusLost() +{ +    LLOutfitListBase::onFocusLost(); + +    if (mSelectedOutfitUUID.notNull()) +    { +        LLOutfitGalleryItem* item = getSelectedItem(); +        if (item) +        { +            item->setSelected(false); +        } +    } +} + +void LLOutfitGallery::onFocusReceived() +{ +    LLOutfitListBase::onFocusReceived(); + +    if (mSelectedOutfitUUID.notNull()) +    { +        LLOutfitGalleryItem* item = getSelectedItem(); +        if (item) +        { +            item->setSelected(true); +        } +    } +} + +void LLOutfitGallery::onRemoveOutfit(const LLUUID& outfit_cat_id) +{ +    LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(onOutfitsRemovalConfirmation, _1, _2, outfit_cat_id)); +} + +void LLOutfitGallery::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id) +{ +    S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +    if (option != 0) return; // canceled + +    if (outfit_cat_id.notNull()) +    { +        gInventory.removeCategory(outfit_cat_id); +    } +} + +void LLOutfitGallery::scrollToShowItem(const LLUUID& item_id) +{ +    LLOutfitGalleryItem* item = mOutfitMap[item_id]; +    if (item) +    { +        const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect(); + +        LLRect item_rect; +        item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel); +        LLRect overlap_rect(item_rect); +        overlap_rect.intersectWith(visible_content_rect); + +        //Scroll when the selected item is outside the visible area +        if (overlap_rect.getHeight() + 5 < item->getRect().getHeight()) +        { +            LLRect content_rect = mScrollPanel->getContentWindowRect(); +            LLRect constraint_rect; +            constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + +            LLRect item_doc_rect; +            item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel); + +            mScrollPanel->scrollToShowRect(item_doc_rect, constraint_rect); +        } +    } +} +  void LLOutfitGallery::updateRowsIfNeeded()  {      if(((getRect().getWidth() - mRowPanelWidth) > mItemWidth) && mRowCount > 1) @@ -269,8 +524,9 @@ void LLOutfitGallery::addToGallery(LLOutfitGalleryItem* item)          mHiddenItems.push_back(item);          return;      } +    mItemIndexMap[item] = mItemsAddedCount; +    mIndexToItemMap[mItemsAddedCount] = item;      mItemsAddedCount++; -    mItemIndexMap[item] = mItemsAddedCount - 1;      int n = mItemsAddedCount;      int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1;      int n_prev = n - 1; @@ -306,6 +562,7 @@ void LLOutfitGallery::removeFromGalleryLast(LLOutfitGalleryItem* item)      int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1;      int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1;      mItemsAddedCount--; +    mIndexToItemMap.erase(mItemsAddedCount);      bool remove_row = row_count != row_count_prev;      removeFromLastRow(mItems[mItemsAddedCount]); @@ -331,6 +588,7 @@ void LLOutfitGallery::removeFromGalleryMiddle(LLOutfitGalleryItem* item)      }      int n = mItemIndexMap[item];      mItemIndexMap.erase(item); +    mIndexToItemMap.erase(n);      std::vector<LLOutfitGalleryItem*> saved;      for (int i = mItemsAddedCount - 1; i > n; i--)      { @@ -364,9 +622,15 @@ LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID      gitem->setFollowsTop();      gitem->setOutfitName(name);      gitem->setUUID(outfit_id); +    gitem->setGallery(this);      return gitem;  } +LLOutfitGalleryItem* LLOutfitGallery::getSelectedItem() +{ +    return mOutfitMap[mSelectedOutfitUUID]; +} +  void LLOutfitGallery::buildGalleryPanel(int row_count)  {      LLPanel::Params params; @@ -440,12 +704,6 @@ void LLOutfitGallery::moveRowPanel(LLPanel* stack, int left, int bottom)  LLOutfitGallery::~LLOutfitGallery()  {      delete mOutfitGalleryMenu; -     -    if (gInventory.containsObserver(mTexturesObserver)) -    { -        gInventory.removeObserver(mTexturesObserver); -    } -    delete mTexturesObserver;      if (gInventory.containsObserver(mOutfitsObserver))      { @@ -617,6 +875,7 @@ void LLOutfitGallery::onChangeOutfitSelection(LLWearableItemsList* list, const L      {          mOutfitMap[category_id]->setSelected(TRUE);      } +    // mSelectedOutfitUUID will be set in LLOutfitListBase::ChangeOutfitSelection  }  void LLOutfitGallery::wearSelectedOutfit() @@ -668,7 +927,8 @@ static LLDefaultChildRegistry::Register<LLOutfitGalleryItem> r("outfit_gallery_i  LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p)      : LLPanel(p), -    mTexturep(NULL), +    mGallery(nullptr), +    mTexturep(nullptr),      mSelected(false),      mWorn(false),      mDefaultImage(true), @@ -743,10 +1003,13 @@ void LLOutfitGalleryItem::setOutfitWorn(bool value)      mWorn = value;      LLStringUtil::format_map_t worn_string_args;      std::string worn_string = getString("worn_string", worn_string_args); -    LLUIColor text_color = LLUIColorTable::instance().getColor(mSelected ? "White" : (mWorn ? "OutfitGalleryItemWorn" : "White"), LLColor4::white); +    LLUIColor text_color = LLUIColorTable::instance().getColor("White", LLColor4::white);      mOutfitWornText->setReadOnlyColor(text_color.get());      mOutfitNameText->setReadOnlyColor(text_color.get()); +    mOutfitWornText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall()); +    mOutfitNameText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall());      mOutfitWornText->setValue(value ? worn_string : ""); +    mOutfitNameText->setText(mOutfitName); // refresh LLTextViewModel to pick up font changes  }  void LLOutfitGalleryItem::setSelected(bool value) @@ -770,8 +1033,63 @@ BOOL LLOutfitGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask)  BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)  { +    return openOutfitsContent() || LLPanel::handleDoubleClick(x, y, mask); +} + +BOOL LLOutfitGalleryItem::handleKeyHere(KEY key, MASK mask) +{ +    if (!mGallery) +    { +        return FALSE; +    } + +    BOOL handled = FALSE; +    switch (key) +    { +        case KEY_LEFT: +            mGallery->moveLeft(); +            handled = true; +            break; + +        case KEY_RIGHT: +            mGallery->moveRight(); +            handled = true; +            break; + +        case KEY_UP: +            mGallery->moveUp(); +            handled = true; +            break; + +        case KEY_DOWN: +            mGallery->moveDown(); +            handled = true; +            break; + +        default: +            break; +    } +    return handled; +} + +void LLOutfitGalleryItem::onFocusLost() +{ +    setSelected(false); + +    LLPanel::onFocusLost(); +} + +void LLOutfitGalleryItem::onFocusReceived() +{ +    setSelected(true); + +    LLPanel::onFocusReceived(); +} + +bool LLOutfitGalleryItem::openOutfitsContent() +{      LLTabContainer* appearence_tabs = LLPanelOutfitsInventory::findInstance()->getChild<LLTabContainer>("appearance_tabs"); -    if (appearence_tabs && (mUUID != LLUUID())) +    if (appearence_tabs && mUUID.notNull())      {          appearence_tabs->selectTabByName("outfitslist_tab");          LLPanel* panel = appearence_tabs->getCurrentPanel(); @@ -784,12 +1102,11 @@ BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask)                  outfit_list->setSelectedOutfitByUUID(mUUID);                  LLAccordionCtrlTab* tab = accordion->getSelectedTab();                  tab->showAndFocusHeader(); -                return TRUE; +                return true;              }          }      } - -    return LLPanel::handleDoubleClick(x, y, mask); +    return false;  }  bool LLOutfitGalleryItem::setImageAssetId(LLUUID image_asset_id) @@ -835,68 +1152,22 @@ LLContextMenu* LLOutfitGalleryContextMenu::createMenu()                    boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id));      registrar.add("Outfit.Edit", boost::bind(editOutfit));      registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); -    registrar.add("Outfit.Delete", boost::bind(&LLOutfitGalleryContextMenu::onRemoveOutfit, this, selected_id)); +    registrar.add("Outfit.Delete", boost::bind(LLOutfitGallery::onRemoveOutfit, selected_id));      registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2)); -    registrar.add("Outfit.UploadPhoto", boost::bind(&LLOutfitGalleryContextMenu::onUploadPhoto, this, selected_id)); -    registrar.add("Outfit.SelectPhoto", boost::bind(&LLOutfitGalleryContextMenu::onSelectPhoto, this, selected_id)); -    registrar.add("Outfit.TakeSnapshot", boost::bind(&LLOutfitGalleryContextMenu::onTakeSnapshot, this, selected_id)); -    registrar.add("Outfit.RemovePhoto", boost::bind(&LLOutfitGalleryContextMenu::onRemovePhoto, this, selected_id)); +    registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id));      enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2));      enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitGalleryContextMenu::onVisible, this, _2));      return createFromFile("menu_gallery_outfit_tab.xml");  } -void LLOutfitGalleryContextMenu::onUploadPhoto(const LLUUID& outfit_cat_id) -{ -    LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -    if (gallery && outfit_cat_id.notNull()) -    { -        gallery->uploadPhoto(outfit_cat_id); -    } -} - -void LLOutfitGalleryContextMenu::onSelectPhoto(const LLUUID& outfit_cat_id) -{ -    LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -    if (gallery && outfit_cat_id.notNull()) -    { -        gallery->onSelectPhoto(outfit_cat_id); -    } -} - -void LLOutfitGalleryContextMenu::onRemovePhoto(const LLUUID& outfit_cat_id) -{ -    LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -    if (gallery && outfit_cat_id.notNull()) -    { -        gallery->checkRemovePhoto(outfit_cat_id); -        gallery->refreshOutfit(outfit_cat_id); -    } -} - -void LLOutfitGalleryContextMenu::onTakeSnapshot(const LLUUID& outfit_cat_id) +void LLOutfitGalleryContextMenu::onThumbnail(const LLUUID& outfit_cat_id)  {      LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList);      if (gallery && outfit_cat_id.notNull())      { -        gallery->onTakeSnapshot(outfit_cat_id); -    } -} - -void LLOutfitGalleryContextMenu::onRemoveOutfit(const LLUUID& outfit_cat_id) -{ -    LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation, this, _1, _2, outfit_cat_id)); -} - -void LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id) -{ -    S32 option = LLNotificationsUtil::getSelectedOption(notification, response); -    if (option != 0) return; // canceled -     -    if (outfit_cat_id.notNull()) -    { -        gInventory.removeCategory(outfit_cat_id); +        LLSD data(outfit_cat_id); +        LLFloaterReg::showInstance("change_item_thumbnail", data);      }  } @@ -919,16 +1190,6 @@ bool LLOutfitGalleryContextMenu::onEnable(LLSD::String param)  bool LLOutfitGalleryContextMenu::onVisible(LLSD::String param)  { -	mMenuHandle.get()->getChild<LLUICtrl>("upload_photo")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost())); -    if ("remove_photo" == param) -    { -        LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -        LLUUID selected_id = mUUIDs.front(); -        if (gallery && selected_id.notNull()) -        { -            return !gallery->hasDefaultImage(selected_id); -        } -    }      return LLOutfitContextMenu::onVisible(param);  } @@ -943,56 +1204,12 @@ void LLOutfitGalleryGearMenu::onUpdateItemsVisibility()      bool have_selection = getSelectedOutfitID().notNull();      mMenu->setItemVisible("expand", FALSE);      mMenu->setItemVisible("collapse", FALSE); -    mMenu->setItemVisible("upload_photo", have_selection); -    mMenu->setItemVisible("select_photo", have_selection); -    mMenu->setItemVisible("take_snapshot", have_selection); -    mMenu->setItemVisible("remove_photo", !hasDefaultImage()); +    mMenu->setItemVisible("thumbnail", have_selection);      mMenu->setItemVisible("sepatator3", TRUE);      mMenu->setItemVisible("sort_folders_by_name", TRUE);      LLOutfitListGearMenuBase::onUpdateItemsVisibility();  } -void LLOutfitGalleryGearMenu::onUploadFoto() -{ -    LLUUID selected_outfit_id = getSelectedOutfitID(); -    LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -    if (gallery && selected_outfit_id.notNull()) -    { -        gallery->uploadPhoto(selected_outfit_id); -    } -} - -void LLOutfitGalleryGearMenu::onSelectPhoto() -{ -    LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -    LLUUID selected_outfit_id = getSelectedOutfitID(); -    if (gallery && !selected_outfit_id.isNull()) -    { -        gallery->onSelectPhoto(selected_outfit_id); -    } -} - -void LLOutfitGalleryGearMenu::onRemovePhoto() -{ -    LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -    LLUUID selected_outfit_id = getSelectedOutfitID(); -    if (gallery && !selected_outfit_id.isNull()) -    { -        gallery->checkRemovePhoto(selected_outfit_id); -        gallery->refreshOutfit(selected_outfit_id); -    } -} - -void LLOutfitGalleryGearMenu::onTakeSnapshot() -{ -    LLOutfitGallery* gallery = dynamic_cast<LLOutfitGallery*>(mOutfitList); -    LLUUID selected_outfit_id = getSelectedOutfitID(); -    if (gallery && !selected_outfit_id.isNull()) -    { -        gallery->onTakeSnapshot(selected_outfit_id); -    } -} -  void LLOutfitGalleryGearMenu::onChangeSortOrder()  {      bool sort_by_name = !gSavedSettings.getBOOL("OutfitGallerySortByName"); @@ -1019,240 +1236,89 @@ void LLOutfitGallery::onTextureSelectionChanged(LLInventoryItem* itemp)  {  } -void LLOutfitGallery::loadPhotos() -{ -    //Iterate over inventory -    mSnapshotFolderID = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE); -    LLViewerInventoryCategory* textures_category = gInventory.getCategory(mSnapshotFolderID); -    if (!textures_category) -        return; -    if (mTexturesObserver == NULL) -    { -        mTexturesObserver = new LLInventoryCategoriesObserver(); -        gInventory.addObserver(mTexturesObserver); -    } - -    // Start observing changes in "Textures" category. -    mTexturesObserver->addCategory(mSnapshotFolderID, -        boost::bind(&LLOutfitGallery::refreshTextures, this, mSnapshotFolderID)); - -    textures_category->fetch(); -} - -void LLOutfitGallery::updateSnapshotFolderObserver() -{ -    if(mSnapshotFolderID != gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE)) -    { -        if (gInventory.containsObserver(mTexturesObserver)) -        { -            gInventory.removeObserver(mTexturesObserver); -        } -        delete mTexturesObserver; -        mTexturesObserver = NULL; -        loadPhotos(); -    } -} -  void LLOutfitGallery::refreshOutfit(const LLUUID& category_id)  {      LLViewerInventoryCategory* category = gInventory.getCategory(category_id);      if (category)      {          bool photo_loaded = false; -        LLInventoryModel::cat_array_t sub_cat_array; -        LLInventoryModel::item_array_t outfit_item_array; -        // Collect all sub-categories of a given category. -        gInventory.collectDescendents( -            category->getUUID(), -            sub_cat_array, -            outfit_item_array, -            LLInventoryModel::EXCLUDE_TRASH); -        BOOST_FOREACH(LLViewerInventoryItem* outfit_item, outfit_item_array) +        LLUUID asset_id = category->getThumbnailUUID(); +        if (asset_id.isNull())          { -            LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem(); -            LLUUID asset_id, inv_id; -            std::string item_name; -            if (linked_item != NULL) +            LLInventoryModel::cat_array_t sub_cat_array; +            LLInventoryModel::item_array_t outfit_item_array; +            // Collect all sub-categories of a given category. +            gInventory.collectDescendents( +                category->getUUID(), +                sub_cat_array, +                outfit_item_array, +                LLInventoryModel::EXCLUDE_TRASH); +            BOOST_FOREACH(LLViewerInventoryItem* outfit_item, outfit_item_array)              { -                if (linked_item->getActualType() == LLAssetType::AT_TEXTURE) +                LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem(); +                LLUUID asset_id, inv_id; +                std::string item_name; +                if (linked_item != NULL)                  { -                    asset_id = linked_item->getAssetUUID(); -                    inv_id = linked_item->getUUID(); -                    item_name = linked_item->getName(); +                    if (linked_item->getActualType() == LLAssetType::AT_TEXTURE) +                    { +                        asset_id = linked_item->getAssetUUID(); +                        inv_id = linked_item->getUUID(); +                        item_name = linked_item->getName(); +                    }                  } -            } -            else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE) -            { -                asset_id = outfit_item->getAssetUUID(); -                inv_id = outfit_item->getUUID(); -                item_name = outfit_item->getName(); -            } -            if (asset_id.notNull()) -            { -                photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id); -                // Rename links -                if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name) +                else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE) +                { +                    asset_id = outfit_item->getAssetUUID(); +                    inv_id = outfit_item->getUUID(); +                    item_name = outfit_item->getName(); +                } +                if (category->getThumbnailUUID().notNull()) +                { +                    asset_id = category->getThumbnailUUID(); +                } +                if (asset_id.notNull())                  { -                    LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending); -                    LLStringUtil::format_map_t photo_string_args; -                    photo_string_args["OUTFIT_NAME"] = outfit_cat->getName(); -                    std::string new_name = getString("outfit_photo_string", photo_string_args); -                    LLSD updates; -                    updates["name"] = new_name; -                    update_inventory_item(inv_id, updates, NULL); -                    mOutfitRenamePending.setNull(); -                    LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance"); -                    if (appearance_floater) +                    photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id); +                    // Rename links +                    if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name) +                    { +                        LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending); +                        LLStringUtil::format_map_t photo_string_args; +                        photo_string_args["OUTFIT_NAME"] = outfit_cat->getName(); +                        std::string new_name = getString("outfit_photo_string", photo_string_args); +                        LLSD updates; +                        updates["name"] = new_name; +                        update_inventory_item(inv_id, updates, NULL); +                        mOutfitRenamePending.setNull(); +                        LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance"); +                        if (appearance_floater) +                        { +                            appearance_floater->setFocus(TRUE); +                        } +                    } +                    if (item_name == LLAppearanceMgr::sExpectedTextureName)                      { -                        appearance_floater->setFocus(TRUE); +                        // Images with "appropriate" name take priority +                        break;                      }                  } -                if (item_name == LLAppearanceMgr::sExpectedTextureName) +                if (!photo_loaded)                  { -                    // Images with "appropriate" name take priority -                    break; +                    mOutfitMap[category_id]->setDefaultImage();                  }              } -            if (!photo_loaded) -            { -                mOutfitMap[category_id]->setDefaultImage(); -            } -        } -    } -     -    if (mGalleryCreated && !LLApp::isExiting()) -    { -        reArrangeRows(); -    } -} - -// Refresh linked textures from "textures" uploads folder -void LLOutfitGallery::refreshTextures(const LLUUID& category_id) -{ -    LLInventoryModel::cat_array_t cat_array; -    LLInventoryModel::item_array_t item_array; - -    // Collect all sub-categories of a given category. -    LLIsType is_texture(LLAssetType::AT_TEXTURE); -    gInventory.collectDescendentsIf( -        category_id, -        cat_array, -        item_array, -        LLInventoryModel::EXCLUDE_TRASH, -        is_texture); - -    //Find texture which contain pending outfit ID string in name -    LLViewerInventoryItem* photo_upload_item = NULL; -    BOOST_FOREACH(LLViewerInventoryItem* item, item_array) -    { -        std::string name = item->getName(); -        if (!mOutfitLinkPending.isNull() && name == mOutfitLinkPending.asString()) -        { -            photo_upload_item = item; -            break; -        } -    } - -    if (photo_upload_item != NULL) -    { -        LLUUID photo_item_id = photo_upload_item->getUUID(); -        LLInventoryObject* upload_object = gInventory.getObject(photo_item_id); -        if (!upload_object) -        { -            LL_WARNS() << "LLOutfitGallery::refreshTextures added_object is null!" << LL_ENDL;          }          else          { -            linkPhotoToOutfit(photo_item_id, mOutfitLinkPending); -            mOutfitRenamePending = mOutfitLinkPending; -            mOutfitLinkPending.setNull(); +            mOutfitMap[category_id]->setImageAssetId(asset_id);          }      } -} - -void LLOutfitGallery::uploadPhoto(LLUUID outfit_id) -{ -	outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id); -	if (outfit_it == mOutfitMap.end() || outfit_it->first.isNull()) -	{ -		return; -	} -    (new LLFilePickerReplyThread(boost::bind(&LLOutfitGallery::uploadOutfitImage, this, _1, outfit_id), LLFilePicker::FFLOAD_IMAGE, false))->getFile(); -} - -void LLOutfitGallery::uploadOutfitImage(const std::vector<std::string>& filenames, LLUUID outfit_id) -{ -    std::string filename = filenames[0]; -    LLLocalBitmap* unit = new LLLocalBitmap(filename); -    if (unit->getValid()) +     +    if (mGalleryCreated && !LLApp::isExiting())      { -        std::string exten = gDirUtilp->getExtension(filename); -        U32 codec = LLImageBase::getCodecFromExtension(exten); - -        LLImageDimensionsInfo image_info; -        std::string image_load_error; -        if (!image_info.load(filename, codec)) -        { -            image_load_error = image_info.getLastError(); -        } - -        S32 max_width = MAX_OUTFIT_PHOTO_WIDTH; -        S32 max_height = MAX_OUTFIT_PHOTO_HEIGHT; - -        if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height)) -        { -            LLStringUtil::format_map_t args; -            args["WIDTH"] = llformat("%d", max_width); -            args["HEIGHT"] = llformat("%d", max_height); - -            image_load_error = LLTrans::getString("outfit_photo_load_dimensions_error", args); -        } - -        if (!image_load_error.empty()) -        { -            LLSD subst; -            subst["REASON"] = image_load_error; -            LLNotificationsUtil::add("OutfitPhotoLoadError", subst); -            return; -        } - -        S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); -        void *nruserdata = NULL; -        nruserdata = (void *)&outfit_id; - -        LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(outfit_id); -        if (!outfit_cat) return; -        updateSnapshotFolderObserver(); -        checkRemovePhoto(outfit_id); -        std::string upload_pending_name = outfit_id.asString(); -        std::string upload_pending_desc = ""; -        upload_new_resource(filename, // file -            upload_pending_name, -            upload_pending_desc, -            0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, -            LLFloaterPerms::getNextOwnerPerms("Uploads"), -            LLFloaterPerms::getGroupPerms("Uploads"), -            LLFloaterPerms::getEveryonePerms("Uploads"), -            upload_pending_name, LLAssetStorage::LLStoreAssetCallback(), expected_upload_cost, nruserdata, false); -        mOutfitLinkPending = outfit_id; +        reArrangeRows();      } -    delete unit; -} - -void LLOutfitGallery::linkPhotoToOutfit(LLUUID photo_id, LLUUID outfit_id) -{ -    LLPointer<LLInventoryCallback> cb = new LLUpdateGalleryOnPhotoLinked(); -    link_inventory_object(outfit_id, photo_id, cb); -} - -bool LLOutfitGallery::checkRemovePhoto(LLUUID outfit_id) -{ -    LLAppearanceMgr::instance().removeOutfitPhoto(outfit_id); -    return true; -} - -void LLUpdateGalleryOnPhotoLinked::fire(const LLUUID& inv_item_id) -{  }  LLUUID LLOutfitGallery::getPhotoAssetId(const LLUUID& outfit_id) @@ -1270,151 +1336,3 @@ LLUUID LLOutfitGallery::getDefaultPhoto()      return LLUUID();  } -void LLOutfitGallery::onTexturePickerCommit(LLTextureCtrl::ETexturePickOp op, LLUUID id) -{ -    LLUUID selected_outfit_id = getSelectedOutfitUUID(); - -    if (selected_outfit_id.isNull()) -    { -        return; -    } - -    LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get(); - -    if (floaterp && op == LLTextureCtrl::TEXTURE_SELECT) -    { -        LLUUID image_item_id; -        if (id.notNull()) -        { -            image_item_id = id; -        } -        else -        { -            image_item_id = floaterp->findItemID(floaterp->getAssetID(), FALSE, TRUE); -            if (image_item_id.isNull()) -            { -                LL_WARNS() << "id or image_item_id is NULL!" << LL_ENDL; -                return; -            } -        } - -        std::string image_load_error; -        S32 max_width = MAX_OUTFIT_PHOTO_WIDTH; -        S32 max_height = MAX_OUTFIT_PHOTO_HEIGHT; -        if (mTextureSelected.isNull() || -            mTextureSelected->getFullWidth() == 0 || -            mTextureSelected->getFullHeight() == 0) -        { -            image_load_error = LLTrans::getString("outfit_photo_verify_dimensions_error"); -            LL_WARNS() << "Cannot verify selected texture dimensions" << LL_ENDL; -            return; -        } -        S32 width = mTextureSelected->getFullWidth(); -        S32 height = mTextureSelected->getFullHeight(); -        if ((width > max_width) || (height > max_height)) -        { -            LLStringUtil::format_map_t args; -            args["WIDTH"] = llformat("%d", max_width); -            args["HEIGHT"] = llformat("%d", max_height); - -            image_load_error = LLTrans::getString("outfit_photo_select_dimensions_error", args); -        } - -        if (!image_load_error.empty()) -        { -            LLSD subst; -            subst["REASON"] = image_load_error; -            LLNotificationsUtil::add("OutfitPhotoLoadError", subst); -            return; -        } - -        checkRemovePhoto(selected_outfit_id); -        linkPhotoToOutfit(image_item_id, selected_outfit_id); -    } -} - -void LLOutfitGallery::onSelectPhoto(LLUUID selected_outfit_id) -{ -    if (selected_outfit_id.notNull()) -    { - -        // show hourglass cursor when loading inventory window -        // because inventory construction is slooow -        getWindow()->setCursor(UI_CURSOR_WAIT); -        LLFloater* floaterp = mFloaterHandle.get(); - -        // Show the dialog -        if (floaterp) -        { -            floaterp->openFloater(); -        } -        else -        { -            floaterp = new LLFloaterTexturePicker( -                this, -                getPhotoAssetId(selected_outfit_id), -                getPhotoAssetId(selected_outfit_id), -                getPhotoAssetId(selected_outfit_id), -                FALSE, -                TRUE, -                "SELECT PHOTO", -                PERM_NONE, -                PERM_NONE, -                PERM_NONE, -                FALSE, -                NULL); - -            mFloaterHandle = floaterp->getHandle(); -            mTextureSelected = NULL; - -            LLFloaterTexturePicker* texture_floaterp = dynamic_cast<LLFloaterTexturePicker*>(floaterp); -            if (texture_floaterp) -            { -                texture_floaterp->setTextureSelectedCallback(boost::bind(&LLOutfitGallery::onTextureSelectionChanged, this, _1)); -                texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLOutfitGallery::onTexturePickerCommit, this, _1, _2)); -                texture_floaterp->setOnUpdateImageStatsCallback(boost::bind(&LLOutfitGallery::onTexturePickerUpdateImageStats, this, _1)); -                texture_floaterp->setLocalTextureEnabled(FALSE); -                texture_floaterp->setBakeTextureEnabled(FALSE); -                texture_floaterp->setCanApply(false, true); -            } - -            floaterp->openFloater(); -        } -        floaterp->setFocus(TRUE); -    } -} - -void LLOutfitGallery::onTakeSnapshot(LLUUID selected_outfit_id) -{ -    LLFloaterReg::toggleInstanceOrBringToFront("simple_outfit_snapshot"); -    LLFloaterSimpleOutfitSnapshot* snapshot_floater = LLFloaterSimpleOutfitSnapshot::getInstance(); -    if (snapshot_floater) -    { -        snapshot_floater->setOutfitID(selected_outfit_id); -        snapshot_floater->getInstance()->setGallery(this); -    } -} - -void LLOutfitGallery::onBeforeOutfitSnapshotSave() -{ -    LLUUID selected_outfit_id = getSelectedOutfitUUID(); -    if (!selected_outfit_id.isNull()) -    { -        checkRemovePhoto(selected_outfit_id); -        updateSnapshotFolderObserver(); -    } -} - -void LLOutfitGallery::onAfterOutfitSnapshotSave() -{ -    LLUUID selected_outfit_id = getSelectedOutfitUUID(); -    if (!selected_outfit_id.isNull()) -    { -        mOutfitLinkPending = selected_outfit_id; -    } -} - -void LLOutfitGallery::onTexturePickerUpdateImageStats(LLPointer<LLViewerTexture> texture) -{ -    mTextureSelected = texture; -} diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h index ce5c090134..9915752962 100644 --- a/indra/newview/lloutfitgallery.h +++ b/indra/newview/lloutfitgallery.h @@ -33,7 +33,6 @@  #include "lllayoutstack.h"  #include "lloutfitslist.h"  #include "llpanelappearancetab.h" -#include "lltexturectrl.h"  #include "llviewertexture.h"  #include <vector> @@ -44,15 +43,6 @@ class LLOutfitListGearMenuBase;  class LLOutfitGalleryGearMenu;  class LLOutfitGalleryContextMenu; -class LLUpdateGalleryOnPhotoLinked : public LLInventoryCallback -{ -public: -    LLUpdateGalleryOnPhotoLinked(){} -    virtual ~LLUpdateGalleryOnPhotoLinked(){} -    /* virtual */ void fire(const LLUUID& inv_item_id); -private: -}; -  class LLOutfitGallery : public LLOutfitListBase  {  public: @@ -83,10 +73,19 @@ public:      /*virtual*/ BOOL postBuild();      /*virtual*/ void onOpen(const LLSD& info); -    /*virtual*/ void draw();	 -     -    void onSelectPhoto(LLUUID selected_outfit_id); -    void onTakeSnapshot(LLUUID selected_outfit_id); +    /*virtual*/ void draw(); +    /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); +    void moveUp(); +    void moveDown(); +    void moveLeft(); +    void moveRight(); + +    /*virtual*/ void onFocusLost(); +    /*virtual*/ void onFocusReceived(); + +    static void onRemoveOutfit(const LLUUID& outfit_cat_id); +    static void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id); +    void scrollToShowItem(const LLUUID& item_id);      void wearSelectedOutfit(); @@ -106,14 +105,8 @@ public:      void updateMessageVisibility();      bool hasDefaultImage(const LLUUID& outfit_cat_id); -    void refreshTextures(const LLUUID& category_id);      void refreshOutfit(const LLUUID& category_id); -    void onTexturePickerCommit(LLTextureCtrl::ETexturePickOp op, LLUUID id); -    void onTexturePickerUpdateImageStats(LLPointer<LLViewerTexture> texture); -    void onBeforeOutfitSnapshotSave(); -    void onAfterOutfitSnapshotSave(); -  protected:      /*virtual*/ void onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id);      /*virtual*/ void onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid); @@ -127,14 +120,8 @@ protected:      void applyFilter(LLOutfitGalleryItem* item, const std::string& filter_substring);  private: -    void loadPhotos(); -    void uploadPhoto(LLUUID outfit_id); -    void uploadOutfitImage(const std::vector<std::string>& filenames, LLUUID outfit_id); -    void updateSnapshotFolderObserver();      LLUUID getPhotoAssetId(const LLUUID& outfit_id);      LLUUID getDefaultPhoto(); -    void linkPhotoToOutfit(LLUUID outfit_id, LLUUID photo_id); -    bool checkRemovePhoto(LLUUID outfit_id);      void addToGallery(LLOutfitGalleryItem* item);      void removeFromGalleryLast(LLOutfitGalleryItem* item);      void removeFromGalleryMiddle(LLOutfitGalleryItem* item); @@ -150,6 +137,7 @@ private:      void updateGalleryWidth();      LLOutfitGalleryItem* buildGalleryItem(std::string name, LLUUID outfit_id); +    LLOutfitGalleryItem* getSelectedItem();      void onTextureSelectionChanged(LLInventoryItem* itemp); @@ -190,17 +178,15 @@ private:      LLListContextMenu* mOutfitGalleryMenu; -    LLHandle<LLFloater> mFloaterHandle; -      typedef std::map<LLUUID, LLOutfitGalleryItem*>      outfit_map_t;      typedef outfit_map_t::value_type                    outfit_map_value_t;      outfit_map_t                                        mOutfitMap; -    typedef std::map<LLOutfitGalleryItem*, int>         item_num_map_t; +    typedef std::map<LLOutfitGalleryItem*, S32>         item_num_map_t;      typedef item_num_map_t::value_type                  item_numb_map_value_t;      item_num_map_t                                      mItemIndexMap; +    std::map<S32, LLOutfitGalleryItem*>                 mIndexToItemMap; -    LLInventoryCategoriesObserver* 	mTexturesObserver;      LLInventoryCategoriesObserver* 	mOutfitsObserver;  };  class LLOutfitGalleryContextMenu : public LLOutfitContextMenu @@ -211,17 +197,13 @@ public:      LLOutfitGalleryContextMenu(LLOutfitListBase* outfit_list)      : LLOutfitContextMenu(outfit_list),      mOutfitList(outfit_list){} +  protected:      /* virtual */ LLContextMenu* createMenu();      bool onEnable(LLSD::String param);      bool onVisible(LLSD::String param); -    void onUploadPhoto(const LLUUID& outfit_cat_id); -    void onSelectPhoto(const LLUUID& outfit_cat_id); -    void onRemovePhoto(const LLUUID& outfit_cat_id); -    void onTakeSnapshot(const LLUUID& outfit_cat_id); +    void onThumbnail(const LLUUID& outfit_cat_id);      void onCreate(const LLSD& data); -    void onRemoveOutfit(const LLUUID& outfit_cat_id); -    void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id);  private:      LLOutfitListBase*	mOutfitList;  }; @@ -236,10 +218,6 @@ public:  protected:      /*virtual*/ void onUpdateItemsVisibility();  private: -    /*virtual*/ void onUploadFoto(); -    /*virtual*/ void onSelectPhoto(); -    /*virtual*/ void onTakeSnapshot(); -    /*virtual*/ void onRemovePhoto();      /*virtual*/ void onChangeSortOrder();      bool hasDefaultImage(); @@ -259,14 +237,21 @@ public:      /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);      /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);      /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); +    /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); +    /*virtual*/ void onFocusLost(); +    /*virtual*/ void onFocusReceived(); + +    bool openOutfitsContent(); +    void setGallery(LLOutfitGallery* gallery) { mGallery = gallery; }      void setDefaultImage();      bool setImageAssetId(LLUUID asset_id);      LLUUID getImageAssetId();      void setOutfitName(std::string name);      void setOutfitWorn(bool value);      void setSelected(bool value); -    void setUUID(LLUUID outfit_id) {mUUID = outfit_id;} +    void setUUID(const LLUUID &outfit_id) {mUUID = outfit_id;} +    LLUUID getUUID() const { return mUUID; }      std::string getItemName() {return mOutfitName;}      bool isDefaultImage() {return mDefaultImage;} @@ -275,6 +260,7 @@ public:      void setHidden(bool hidden) {mHidden = hidden;}  private: +    LLOutfitGallery* mGallery;      LLPointer<LLViewerFetchedTexture> mTexturep;      LLUUID mUUID;      LLUUID mImageAssetId; diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 4171fd8822..5c7792b0df 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -35,7 +35,7 @@  #include "llaccordionctrltab.h"  #include "llagentwearables.h"  #include "llappearancemgr.h" -#include "llagentbenefits.h" +#include "llfloaterreg.h"  #include "llfloatersidepanelcontainer.h"  #include "llinventoryfunctions.h"  #include "llinventorymodel.h" @@ -122,9 +122,8 @@ void LLOutfitsList::onOpen(const LLSD& info)  {      if (!mIsInitialized)      { -        const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);          // Start observing changes in Current Outfit category. -        mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this)); +        LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLOutfitsList::onCOFChanged, this));      }      LLOutfitListBase::onOpen(info); @@ -1112,10 +1111,7 @@ LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist)      registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this)); -    registrar.add("Gear.UploadPhoto", boost::bind(&LLOutfitListGearMenuBase::onUploadFoto, this)); -    registrar.add("Gear.SelectPhoto", boost::bind(&LLOutfitListGearMenuBase::onSelectPhoto, this)); -    registrar.add("Gear.TakeSnapshot", boost::bind(&LLOutfitListGearMenuBase::onTakeSnapshot, this)); -    registrar.add("Gear.RemovePhoto", boost::bind(&LLOutfitListGearMenuBase::onRemovePhoto, this)); +    registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this));      registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this));      enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2)); @@ -1232,7 +1228,6 @@ bool LLOutfitListGearMenuBase::onEnable(LLSD::String param)  bool LLOutfitListGearMenuBase::onVisible(LLSD::String param)  { -	getMenu()->getChild<LLUICtrl>("upload_photo")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost()));      const LLUUID& selected_outfit_id = getSelectedOutfitID();      if (selected_outfit_id.isNull()) // no selection or invalid outfit selected      { @@ -1251,24 +1246,11 @@ bool LLOutfitListGearMenuBase::onVisible(LLSD::String param)      return true;  } -void LLOutfitListGearMenuBase::onUploadFoto() +void LLOutfitListGearMenuBase::onThumbnail()  { - -} - -void LLOutfitListGearMenuBase::onSelectPhoto() -{ - -} - -void LLOutfitListGearMenuBase::onTakeSnapshot() -{ - -} - -void LLOutfitListGearMenuBase::onRemovePhoto() -{ - +    const LLUUID& selected_outfit_id = getSelectedOutfitID(); +    LLSD data(selected_outfit_id); +    LLFloaterReg::showInstance("change_item_thumbnail", data);  }  void LLOutfitListGearMenuBase::onChangeSortOrder() @@ -1288,10 +1270,7 @@ void LLOutfitListGearMenu::onUpdateItemsVisibility()      if (!mMenu) return;      mMenu->setItemVisible("expand", TRUE);      mMenu->setItemVisible("collapse", TRUE); -    mMenu->setItemVisible("upload_photo", FALSE); -    mMenu->setItemVisible("select_photo", FALSE); -    mMenu->setItemVisible("take_snapshot", FALSE); -    mMenu->setItemVisible("remove_photo", FALSE); +    mMenu->setItemVisible("thumbnail", FALSE); // Never visible?      mMenu->setItemVisible("sepatator3", FALSE);      mMenu->setItemVisible("sort_folders_by_name", FALSE);      LLOutfitListGearMenuBase::onUpdateItemsVisibility(); diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index 81be8de94f..66b3165169 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -163,10 +163,7 @@ public:  protected:      virtual void onUpdateItemsVisibility(); -    virtual void onUploadFoto(); -    virtual void onSelectPhoto(); -    virtual void onTakeSnapshot(); -    virtual void onRemovePhoto(); +    virtual void onThumbnail();      virtual void onChangeSortOrder();      const LLUUID& getSelectedOutfitID(); diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 834e664723..cc3c51dd83 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -111,9 +111,9 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type)  }  // Sets CREATE_LANDMARK infotype and creates landmark at desired folder -void LLPanelLandmarkInfo::setInfoAndCreateLandmark(const LLUUID& fodler_id) +void LLPanelLandmarkInfo::setInfoAndCreateLandmark(const LLUUID& folder_id)  { -    setInfoType(CREATE_LANDMARK, fodler_id); +    setInfoType(CREATE_LANDMARK, folder_id);  }  void LLPanelLandmarkInfo::setInfoType(EInfoType type, const LLUUID &folder_id) diff --git a/indra/newview/llpanellandmarkinfo.h b/indra/newview/llpanellandmarkinfo.h index 46e2a1935b..8802ce066e 100644 --- a/indra/newview/llpanellandmarkinfo.h +++ b/indra/newview/llpanellandmarkinfo.h @@ -48,7 +48,7 @@ public:  	/*virtual*/ void setInfoType(EInfoType type);      // Sets CREATE_LANDMARK infotype and creates landmark at desired folder -    void setInfoAndCreateLandmark(const LLUUID& fodler_id); +    void setInfoAndCreateLandmark(const LLUUID& folder_id);  	/*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 8f1e57e44c..49756a4e09 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -1110,11 +1110,6 @@ void LLPanelLogin::onRememberPasswordCheck(void*)          std::string grid(LLGridManager::getInstance()->getGridId());          std::string user_id(cred->userID()); -        if (!remember_password) -        { -            gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id); -            gSecAPIHandler->syncProtectedMap(); -        }      }  } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 744d49ff57..30f301027c 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -37,8 +37,8 @@  #include "llfilepicker.h"  #include "llinventorybridge.h"  #include "llinventoryfunctions.h" +#include "llinventorygallery.h"  #include "llinventorymodelbackgroundfetch.h" -#include "llinventorypanel.h"  #include "llfiltereditor.h"  #include "llfloatersidepanelcontainer.h"  #include "llfloaterreg.h" @@ -53,12 +53,14 @@  #include "llspinctrl.h"  #include "lltoggleablemenu.h"  #include "lltooldraganddrop.h" +#include "lltrans.h"  #include "llviewermenu.h"  #include "llviewertexturelist.h"  #include "llsidepanelinventory.h"  #include "llfolderview.h"  #include "llradiogroup.h"  #include "llenvironment.h" +#include "llweb.h"  const std::string FILTERS_FILENAME("filters.xml"); @@ -113,7 +115,13 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p)  	  mMenuGearDefault(NULL),  	  mMenuVisibility(NULL),  	  mMenuAddHandle(), -	  mNeedUploadCost(true) +	  mNeedUploadCost(true), +      mMenuViewDefault(NULL), +      mSingleFolderMode(false), +      mForceShowInvLayout(false), +      mViewMode(MODE_COMBINATION), +      mListViewRootUpdatedConnection(), +      mGalleryRootUpdatedConnection()  {  	// Menu Callbacks (non contex menus)  	mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelMainInventory::doToSelected, this, _2)); @@ -124,7 +132,6 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p)  	mCommitCallbackRegistrar.add("Inventory.ShowFilters", boost::bind(&LLPanelMainInventory::toggleFindOptions, this));  	mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this));  	mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2)); -	mCommitCallbackRegistrar.add("Inventory.Share",  boost::bind(&LLAvatarActions::shareWithAvatars, this));      mEnableCallbackRegistrar.add("Inventory.EnvironmentEnabled", [](LLUICtrl *, const LLSD &) { return LLPanelMainInventory::hasSettingsInventory(); }); @@ -192,7 +199,7 @@ BOOL LLPanelMainInventory::postBuild()  	}  	// Now load the stored settings from disk, if available.  	std::string filterSaveName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, FILTERS_FILENAME)); -	LL_INFOS() << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL; +	LL_INFOS("Inventory") << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL;  	llifstream file(filterSaveName.c_str());  	LLSD savedFilterState;  	if (file.is_open()) @@ -237,6 +244,34 @@ BOOL LLPanelMainInventory::postBuild()  	mGearMenuButton = getChild<LLMenuButton>("options_gear_btn");  	mVisibilityMenuButton = getChild<LLMenuButton>("options_visibility_btn"); +    mViewMenuButton = getChild<LLMenuButton>("view_btn"); + +    mBackBtn = getChild<LLButton>("back_btn"); +    mForwardBtn = getChild<LLButton>("forward_btn"); +    mUpBtn = getChild<LLButton>("up_btn"); +    mViewModeBtn = getChild<LLButton>("view_mode_btn"); +    mNavigationBtnsPanel = getChild<LLLayoutPanel>("nav_buttons"); + +    mDefaultViewPanel = getChild<LLPanel>("default_inventory_panel"); +    mCombinationViewPanel = getChild<LLPanel>("combination_view_inventory"); +    mCombinationGalleryLayoutPanel = getChild<LLLayoutPanel>("comb_gallery_layout"); +    mCombinationListLayoutPanel = getChild<LLLayoutPanel>("comb_inventory_layout"); +    mCombinationLayoutStack = getChild<LLLayoutStack>("combination_view_stack"); + +    mCombinationInventoryPanel = getChild<LLInventorySingleFolderPanel>("comb_single_folder_inv"); +    LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter(); +    comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_EXCLUDE_THUMBNAILS); +    comb_inv_filter.markDefault(); +    mCombinationInventoryPanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onCombinationInventorySelectionChanged, this, _1, _2)); +    mListViewRootUpdatedConnection = mCombinationInventoryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::onCombinationRootChanged, this, false)); + +    mCombinationGalleryPanel = getChild<LLInventoryGallery>("comb_gallery_view_inv"); +    mCombinationGalleryPanel->setSortOrder(mCombinationInventoryPanel->getSortOrder()); +    LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter(); +    comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_ONLY_THUMBNAILS); +    comb_gallery_filter.markDefault(); +    mGalleryRootUpdatedConnection = mCombinationGalleryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::onCombinationRootChanged, this, true)); +    mCombinationGalleryPanel->setSelectionChangeCallback(boost::bind(&LLPanelMainInventory::onCombinationGallerySelectionChanged, this, _1));  	initListCommandsHandlers(); @@ -308,12 +343,21 @@ LLPanelMainInventory::~LLPanelMainInventory( void )  	gInventory.removeObserver(this);  	delete mSavedFolderState; -	auto menu = mMenuAddHandle.get(); -	if(menu) -	{ -		menu->die(); -		mMenuAddHandle.markDead(); -	} +    auto menu = mMenuAddHandle.get(); +    if(menu) +    { +        menu->die(); +        mMenuAddHandle.markDead(); +    } + +    if (mListViewRootUpdatedConnection.connected()) +    { +        mListViewRootUpdatedConnection.disconnect(); +    } +    if (mGalleryRootUpdatedConnection.connected()) +    { +        mGalleryRootUpdatedConnection.disconnect(); +    }  }  LLInventoryPanel* LLPanelMainInventory::getAllItemsPanel() @@ -362,6 +406,10 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask)  		{  			startSearch();  		} +        if(mSingleFolderMode && key == KEY_LEFT) +        { +            onBackFolderClicked(); +        }  	}  	return LLPanel::handleKeyHere(key, mask); @@ -381,27 +429,145 @@ void LLPanelMainInventory::closeAllFolders()  	getPanel()->getRootFolder()->closeAllFolders();  } -void LLPanelMainInventory::newWindow() +S32 get_instance_num() +{ +    static S32 instance_num = 0; +    instance_num = (instance_num + 1) % S32_MAX; + +    return instance_num; +} + +LLFloaterSidePanelContainer* LLPanelMainInventory::newWindow()  { -	static S32 instance_num = 0; -	instance_num = (instance_num + 1) % S32_MAX; +    S32 instance_num = get_instance_num();  	if (!gAgentCamera.cameraMouselook())  	{ -		LLFloaterReg::showTypedInstance<LLFloaterSidePanelContainer>("inventory", LLSD(instance_num)); +        LLFloaterSidePanelContainer* floater = LLFloaterReg::showTypedInstance<LLFloaterSidePanelContainer>("inventory", LLSD(instance_num)); +        LLSidepanelInventory* sidepanel_inventory = floater->findChild<LLSidepanelInventory>("main_panel"); +        sidepanel_inventory->initInventoryViews(); +		return floater;  	} +    return NULL; +} + +//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<LLFloaterSidePanelContainer*>(*iter++); +        if (inventory_container) +        { +            LLSidepanelInventory* sidepanel_inventory = dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true)); +            if (sidepanel_inventory) +            { +                LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); +                if (main_inventory && main_inventory->isSingleFolderMode() +                    && (main_inventory->getCurrentSFVRoot() == folder_id)) +                { +                    main_inventory->setFocus(true); +                    if(item_to_select.notNull()) +                    { +                        main_inventory->setGallerySelection(item_to_select); +                    } +                    return; +                } +            } +        } +    } +     +    S32 instance_num = get_instance_num(); + +    LLFloaterSidePanelContainer* inventory_container = LLFloaterReg::showTypedInstance<LLFloaterSidePanelContainer>("inventory", LLSD(instance_num)); +    if(inventory_container) +    { +        LLSidepanelInventory* sidepanel_inventory = dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true)); +        if (sidepanel_inventory) +        { +            LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); +            if (main_inventory) +            { +                main_inventory->initSingleFolderRoot(folder_id); +                main_inventory->toggleViewMode(); +                if(folder_id.notNull()) +                { +                    if(item_to_select.notNull()) +                    { +                        main_inventory->setGallerySelection(item_to_select, true); +                    } +                } +            } +        } +    }  }  void LLPanelMainInventory::doCreate(const LLSD& userdata)  {  	reset_inventory_filter(); -	menu_create_inventory_item(getPanel(), NULL, userdata); +    if(mSingleFolderMode) +    { +        if(isListViewMode() || isCombinationViewMode()) +        { +            LLFolderViewItem* current_folder = getActivePanel()->getRootFolder(); +            if (current_folder) +            { +                if(isCombinationViewMode()) +                { +                    mForceShowInvLayout = true; +                } + +                LLHandle<LLPanel> handle = getHandle(); +                std::function<void(const LLUUID&)> callback_created = [handle](const LLUUID& new_id) +                { +                    gInventory.notifyObservers(); // not really needed, should have been already done +                    LLPanelMainInventory* panel = (LLPanelMainInventory*)handle.get(); +                    if (new_id.notNull() && panel) +                    { +                        // might need to refresh visibility, delay rename +                        panel->mCombInvUUIDNeedsRename = new_id; + +                        if (panel->isCombinationViewMode()) +                        { +                            panel->mForceShowInvLayout = true; +                        } + +                        LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL; +                    } +                }; +                menu_create_inventory_item(NULL, getCurrentSFVRoot(), userdata, LLUUID::null, callback_created); +            } +        } +        else +        { +            LLHandle<LLPanel> handle = getHandle(); +            std::function<void(const LLUUID&)> callback_created = [handle](const LLUUID &new_id) +            { +                gInventory.notifyObservers(); // not really needed, should have been already done +                if (new_id.notNull()) +                { +                    LLPanelMainInventory* panel = (LLPanelMainInventory*)handle.get(); +                    if (panel) +                    { +                        panel->setGallerySelection(new_id); +                        LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL; +                    } +                } +            }; +            menu_create_inventory_item(NULL, getCurrentSFVRoot(), userdata, LLUUID::null, callback_created); +        } +    } +    else +    { +        menu_create_inventory_item(getPanel(), NULL, userdata); +    }  }  void LLPanelMainInventory::resetFilters()  {  	LLFloaterInventoryFinder *finder = getFinder(); -	getActivePanel()->getFilter().resetDefault(); +	getCurrentFilter().resetDefault();  	if (finder)  	{  		finder->updateElementsFromFilter(); @@ -422,6 +588,17 @@ void LLPanelMainInventory::resetAllItemsFilters()      setFilterTextFromFilter();  } +void LLPanelMainInventory::findLinks(const LLUUID& item_id, const std::string& item_name) +{ +    mFilterSubString = item_name; + +    LLInventoryFilter &filter = mActivePanel->getFilter(); +    filter.setFindAllLinksMode(item_name, item_id); + +    mFilterEditor->setText(item_name); +    mFilterEditor->setFocus(TRUE); +} +  void LLPanelMainInventory::setSortBy(const LLSD& userdata)  {  	U32 sort_order_mask = getActivePanel()->getSortOrder(); @@ -456,6 +633,10 @@ void LLPanelMainInventory::setSortBy(const LLSD& userdata)  			sort_order_mask |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;  		}  	} +    if(mSingleFolderMode && !isListViewMode()) +    { +        mCombinationGalleryPanel->setSortOrder(sort_order_mask, true); +    }  	getActivePanel()->setSortOrder(sort_order_mask);      if (isRecentItemsPanelSelected()) @@ -473,25 +654,56 @@ void LLPanelMainInventory::onSelectSearchType()  	std::string new_type = mSearchTypeCombo->getValue();  	if (new_type == "search_by_name")  	{ -		getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_NAME); +		setSearchType(LLInventoryFilter::SEARCHTYPE_NAME);  	}  	if (new_type == "search_by_creator")  	{ -		getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_CREATOR); +		setSearchType(LLInventoryFilter::SEARCHTYPE_CREATOR);  	}  	if (new_type == "search_by_description")  	{ -		getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_DESCRIPTION); +		setSearchType(LLInventoryFilter::SEARCHTYPE_DESCRIPTION);  	}  	if (new_type == "search_by_UUID")  	{ -		getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_UUID); +		setSearchType(LLInventoryFilter::SEARCHTYPE_UUID);  	}  } +void LLPanelMainInventory::setSearchType(LLInventoryFilter::ESearchType type) +{ +    if(mSingleFolderMode && isGalleryViewMode()) +    { +        mCombinationGalleryPanel->setSearchType(type); +    } +    if(mSingleFolderMode && isCombinationViewMode()) +    { +        mCombinationInventoryPanel->setSearchType(type); +        mCombinationGalleryPanel->setSearchType(type); +    } +    else +    { +        getActivePanel()->setSearchType(type); +    } +} +  void LLPanelMainInventory::updateSearchTypeCombo()  { -	LLInventoryFilter::ESearchType search_type = getActivePanel()->getSearchType(); +    LLInventoryFilter::ESearchType search_type(LLInventoryFilter::SEARCHTYPE_NAME); + +    if(mSingleFolderMode && isGalleryViewMode()) +    { +        search_type = mCombinationGalleryPanel->getSearchType(); +    } +    else if(mSingleFolderMode && isCombinationViewMode()) +    { +        search_type = mCombinationGalleryPanel->getSearchType(); +    } +    else +    { +        search_type = getActivePanel()->getSearchType(); +    } +  	switch(search_type)  	{  		case LLInventoryFilter::SEARCHTYPE_CREATOR: @@ -537,7 +749,7 @@ void LLPanelMainInventory::onClearSearch()  	}  	// re-open folders that were initially open in case filter was active -	if (mActivePanel && (mFilterSubString.size() || initially_active)) +	if (mActivePanel && (mFilterSubString.size() || initially_active) && !mSingleFolderMode)  	{  		mSavedFolderState->setApply(TRUE);  		mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -547,7 +759,7 @@ void LLPanelMainInventory::onClearSearch()  	}  	mFilterSubString = ""; -	LLSidepanelInventory * sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); +	LLSidepanelInventory * sidepanel_inventory = getParentSidepanelInventory();  	if (sidepanel_inventory)  	{  		LLPanelMarketplaceInbox* inbox_panel = sidepanel_inventory->getChild<LLPanelMarketplaceInbox>("marketplace_inbox"); @@ -560,16 +772,32 @@ void LLPanelMainInventory::onClearSearch()  void LLPanelMainInventory::onFilterEdit(const std::string& search_string )  { +    if(mSingleFolderMode && isGalleryViewMode()) +    { +        mFilterSubString = search_string; +        mCombinationGalleryPanel->setFilterSubString(mFilterSubString); +        return; +    } +    if(mSingleFolderMode && isCombinationViewMode()) +    { +        mCombinationGalleryPanel->setFilterSubString(search_string); +    } +  	if (search_string == "")  	{  		onClearSearch();  	} +  	if (!mActivePanel)  	{  		return;  	} -	LLInventoryModelBackgroundFetch::instance().start(); +    if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) +    { +        llassert(false); // this should have been done on startup +        LLInventoryModelBackgroundFetch::instance().start(); +    }  	mFilterSubString = search_string;  	if (mActivePanel->getFilterSubString().empty() && mFilterSubString.empty()) @@ -588,7 +816,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string )  	// set new filter string  	setFilterSubString(mFilterSubString); -	LLSidepanelInventory * sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); +    LLSidepanelInventory * sidepanel_inventory = getParentSidepanelInventory();  	if (sidepanel_inventory)  	{  		LLPanelMarketplaceInbox* inbox_panel = sidepanel_inventory->getChild<LLPanelMarketplaceInbox>("marketplace_inbox"); @@ -643,7 +871,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string )  void LLPanelMainInventory::onFilterSelected()  {  	// Find my index -	mActivePanel = (LLInventoryPanel*)getChild<LLTabContainer>("inventory filter tabs")->getCurrentPanel(); +    setActivePanel();  	if (!mActivePanel)  	{ @@ -656,15 +884,19 @@ void LLPanelMainInventory::onFilterSelected()  	}  	updateSearchTypeCombo();  	setFilterSubString(mFilterSubString); -	LLInventoryFilter& filter = mActivePanel->getFilter(); +	LLInventoryFilter& filter = getCurrentFilter();  	LLFloaterInventoryFinder *finder = getFinder();  	if (finder)  	{  		finder->changeFilter(&filter); +        if (mSingleFolderMode) +        { +            finder->setTitle(getLocalizedRootName()); +        }  	} -	if (filter.isActive()) +	if (filter.isActive() && !LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted())  	{ -		// If our filter is active we may be the first thing requiring a fetch so we better start it here. +        llassert(false); // this should have been done on startup  		LLInventoryModelBackgroundFetch::instance().start();  	}  	setFilterTextFromFilter(); @@ -736,6 +968,7 @@ void LLPanelMainInventory::draw()  	}  	LLPanel::draw();  	updateItemcountText(); +    updateCombinationVisibility();  }  void LLPanelMainInventory::updateItemcountText() @@ -775,6 +1008,21 @@ void LLPanelMainInventory::updateItemcountText()  	{  		text = getString("ItemcountUnknown", string_args);  	} + +    if (mSingleFolderMode) +    { +        LLInventoryModel::cat_array_t *cats; +        LLInventoryModel::item_array_t *items; + +        gInventory.getDirectDescendentsOf(getCurrentSFVRoot(), cats, items); + +        if (items && cats) +        { +            string_args["[ITEM_COUNT]"] = llformat("%d", items->size()); +            string_args["[CATEGORY_COUNT]"] = llformat("%d", cats->size()); +            text = getString("ItemcountCompleted", string_args); +        } +    }      mCounterCtrl->setValue(text);      mCounterCtrl->setToolTip(text); @@ -794,7 +1042,7 @@ void LLPanelMainInventory::onFocusReceived()  void LLPanelMainInventory::setFilterTextFromFilter()   {  -	mFilterText = mActivePanel->getFilter().getFilterText();  +	mFilterText = getCurrentFilter().getFilterText();  }  void LLPanelMainInventory::toggleFindOptions() @@ -809,8 +1057,17 @@ void LLPanelMainInventory::toggleFindOptions()  		LLFloater* parent_floater = gFloaterView->getParentFloater(this);  		if (parent_floater)  			parent_floater->addDependentFloater(mFinderHandle); -		// start background fetch of folders -		LLInventoryModelBackgroundFetch::instance().start(); + +        if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) +        { +            llassert(false); // this should have been done on startup +            LLInventoryModelBackgroundFetch::instance().start(); +        } + +        if (mSingleFolderMode) +        { +            finder->setTitle(getLocalizedRootName()); +        }  	}  	else  	{ @@ -1043,10 +1300,27 @@ void LLFloaterInventoryFinder::draw()  		filter &= ~(0x1 << LLInventoryType::IT_CATEGORY);  	} -	// update the panel, panel will update the filter -	mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ? -		LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); -	mPanelMainInventory->getPanel()->setFilterTypes(filter); + +    bool is_sf_mode = mPanelMainInventory->isSingleFolderMode(); +    if(is_sf_mode && mPanelMainInventory->isGalleryViewMode()) +    { +        mPanelMainInventory->mCombinationGalleryPanel->getFilter().setShowFolderState(getCheckShowEmpty() ? +            LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); +        mPanelMainInventory->mCombinationGalleryPanel->getFilter().setFilterObjectTypes(filter); +    } +    else +    { +        if(is_sf_mode && mPanelMainInventory->isCombinationViewMode()) +        { +            mPanelMainInventory->mCombinationGalleryPanel->getFilter().setShowFolderState(getCheckShowEmpty() ? +                LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); +            mPanelMainInventory->mCombinationGalleryPanel->getFilter().setFilterObjectTypes(filter); +        } +        // update the panel, panel will update the filter +        mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ? +            LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); +        mPanelMainInventory->getPanel()->setFilterTypes(filter); +    }  	if (getCheckSinceLogoff())  	{ @@ -1068,10 +1342,26 @@ void LLFloaterInventoryFinder::draw()  	}  	hours += days * 24; -	mPanelMainInventory->getPanel()->setHoursAgo(hours); -	mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff()); +  	mPanelMainInventory->setFilterTextFromFilter(); -	mPanelMainInventory->getPanel()->setDateSearchDirection(getDateSearchDirection()); +    if(is_sf_mode && mPanelMainInventory->isGalleryViewMode()) +    { +        mPanelMainInventory->mCombinationGalleryPanel->getFilter().setHoursAgo(hours); +        mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateRangeLastLogoff(getCheckSinceLogoff()); +        mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateSearchDirection(getDateSearchDirection()); +    } +    else +    { +        if(is_sf_mode && mPanelMainInventory->isCombinationViewMode()) +        { +            mPanelMainInventory->mCombinationGalleryPanel->getFilter().setHoursAgo(hours); +            mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateRangeLastLogoff(getCheckSinceLogoff()); +            mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateSearchDirection(getDateSearchDirection()); +        } +        mPanelMainInventory->getPanel()->setHoursAgo(hours); +        mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff()); +        mPanelMainInventory->getPanel()->setDateSearchDirection(getDateSearchDirection()); +    }  	LLPanel::draw();  } @@ -1083,15 +1373,15 @@ void LLFloaterInventoryFinder::onCreatorSelfFilterCommit()  	if(show_creator_self && show_creator_other)  	{ -		mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL); +        mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL);  	}  	else if(show_creator_self)  	{ -		mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF); +        mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF);  	}  	else if(!show_creator_self || !show_creator_other)  	{ -		mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS); +        mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS);  		mCreatorOthers->set(TRUE);  	}  } @@ -1103,15 +1393,15 @@ void LLFloaterInventoryFinder::onCreatorOtherFilterCommit()  	if(show_creator_self && show_creator_other)  	{ -		mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL); +        mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL);  	}  	else if(show_creator_other)  	{ -		mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS); +        mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS);  	}  	else if(!show_creator_other || !show_creator_self)  	{ -		mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF); +        mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF);  		mCreatorSelf->set(TRUE);  	}  } @@ -1182,26 +1472,25 @@ void LLFloaterInventoryFinder::selectNoTypes(void* user_data)  void LLPanelMainInventory::initListCommandsHandlers()  { -	childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this));  	childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this)); - -	mTrashButton = getChild<LLDragAndDropButton>("trash_btn"); -	mTrashButton->setDragAndDropHandler(boost::bind(&LLPanelMainInventory::handleDragAndDropToTrash, this -			,	_4 // BOOL drop -			,	_5 // EDragAndDropType cargo_type -			,	_7 // EAcceptance* accept -			)); +    childSetAction("view_mode_btn", boost::bind(&LLPanelMainInventory::onViewModeClick, this)); +    childSetAction("up_btn", boost::bind(&LLPanelMainInventory::onUpFolderClicked, this)); +    childSetAction("back_btn", boost::bind(&LLPanelMainInventory::onBackFolderClicked, this)); +    childSetAction("forward_btn", boost::bind(&LLPanelMainInventory::onForwardFolderClicked, this));  	mCommitCallbackRegistrar.add("Inventory.GearDefault.Custom.Action", boost::bind(&LLPanelMainInventory::onCustomAction, this, _2));  	mEnableCallbackRegistrar.add("Inventory.GearDefault.Check", boost::bind(&LLPanelMainInventory::isActionChecked, this, _2));  	mEnableCallbackRegistrar.add("Inventory.GearDefault.Enable", boost::bind(&LLPanelMainInventory::isActionEnabled, this, _2)); +    mEnableCallbackRegistrar.add("Inventory.GearDefault.Visible", boost::bind(&LLPanelMainInventory::isActionVisible, this, _2));  	mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); -	mGearMenuButton->setMenu(mMenuGearDefault, LLMenuButton::MP_TOP_LEFT, true); +	mGearMenuButton->setMenu(mMenuGearDefault, LLMenuButton::MP_BOTTOM_LEFT, true); +    mMenuViewDefault = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_view_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +    mViewMenuButton->setMenu(mMenuViewDefault, LLMenuButton::MP_BOTTOM_LEFT, true);  	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_add.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());  	mMenuAddHandle = menu->getHandle();  	mMenuVisibility = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_search_visibility.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); -	mVisibilityMenuButton->setMenu(mMenuVisibility, LLMenuButton::MP_BOTTOM_LEFT, true); +    mVisibilityMenuButton->setMenu(mMenuVisibility, LLMenuButton::MP_BOTTOM_LEFT, true);  	// Update the trash button when selected item(s) get worn or taken off.  	LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLPanelMainInventory::updateListCommands, this)); @@ -1209,9 +1498,6 @@ void LLPanelMainInventory::initListCommandsHandlers()  void LLPanelMainInventory::updateListCommands()  { -	bool trash_enabled = isActionEnabled("delete"); - -	mTrashButton->setEnabled(trash_enabled);  }  void LLPanelMainInventory::onAddButtonClick() @@ -1222,7 +1508,7 @@ void LLPanelMainInventory::onAddButtonClick()  	LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get();  	if (menu)  	{ -		menu->getChild<LLMenuItemGL>("New Folder")->setEnabled(!isRecentItemsPanelSelected()); +        disableAddIfNeeded();  		setUploadCostIfNeeded(); @@ -1230,6 +1516,215 @@ void LLPanelMainInventory::onAddButtonClick()  	}  } +void LLPanelMainInventory::setActivePanel() +{ +    // Todo: should cover gallery mode in some way +    if(mSingleFolderMode && isListViewMode()) +    { +        mActivePanel = getChild<LLInventoryPanel>("comb_single_folder_inv"); +    } +    else if(mSingleFolderMode && isCombinationViewMode()) +    { +        mActivePanel = getChild<LLInventoryPanel>("comb_single_folder_inv"); +    } +    else +    { +        mActivePanel = (LLInventoryPanel*)getChild<LLTabContainer>("inventory filter tabs")->getCurrentPanel(); +    } +    mViewModeBtn->setEnabled(mSingleFolderMode || (getAllItemsPanel() == getActivePanel())); +} + +void LLPanelMainInventory::initSingleFolderRoot(const LLUUID& start_folder_id) +{ +    mCombinationInventoryPanel->initFolderRoot(start_folder_id); +} + +void LLPanelMainInventory::initInventoryViews() +{ +    LLInventoryPanel* all_item = getChild<LLInventoryPanel>(ALL_ITEMS); +    all_item->initializeViewBuilding(); +    LLInventoryPanel* recent_item = getChild<LLInventoryPanel>(RECENT_ITEMS); +    recent_item->initializeViewBuilding(); +    LLInventoryPanel* worn_item = getChild<LLInventoryPanel>(WORN_ITEMS); +    worn_item->initializeViewBuilding(); +} + +void LLPanelMainInventory::toggleViewMode() +{ +    if(mSingleFolderMode && isCombinationViewMode()) +    { +        mCombinationInventoryPanel->getRootFolder()->setForceArrange(false); +    } + +    mSingleFolderMode = !mSingleFolderMode; +    mReshapeInvLayout = true; + +    if (mCombinationGalleryPanel->getRootFolder().isNull()) +    { +        mCombinationGalleryPanel->setRootFolder(mCombinationInventoryPanel->getSingleFolderRoot()); +        mCombinationGalleryPanel->updateRootFolder(); +    } + +    updatePanelVisibility(); +    setActivePanel(); +    updateTitle(); +    onFilterSelected(); + +    LLSidepanelInventory* sidepanel_inventory = getParentSidepanelInventory(); +    if (sidepanel_inventory) +    { +        if(mSingleFolderMode) +        { +            sidepanel_inventory->hideInbox(); +        } +        else +        { +            sidepanel_inventory->toggleInbox(); +        } +    } +} + +void LLPanelMainInventory::onViewModeClick() +{ +    LLUUID selected_folder; +    LLUUID new_root_folder; +    if(mSingleFolderMode) +    { +        selected_folder = getCurrentSFVRoot(); +    } +    else +    { +        LLFolderView* root = getActivePanel()->getRootFolder(); +        std::set<LLFolderViewItem*> selection_set = root->getSelectionList(); +        if (selection_set.size() == 1) +        { +            LLFolderViewItem* current_item = *selection_set.begin(); +            if (current_item) +            { +                const LLUUID& id = static_cast<LLFolderViewModelItemInventory*>(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; +                    } +                } +            } +        } +        mCombinationInventoryPanel->initFolderRoot(new_root_folder); +    } + +    toggleViewMode(); + +    if (mSingleFolderMode && new_root_folder.notNull()) +    { +        setSingleFolderViewRoot(new_root_folder, true); +        if(selected_folder.notNull() && isListViewMode()) +        { +            getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES); +        } +    } +    else +    { +        if(selected_folder.notNull()) +        { +            selectAllItemsPanel(); +            getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES); +        } +    } +} + +void LLPanelMainInventory::onUpFolderClicked() +{ +    const LLViewerInventoryCategory* cat = gInventory.getCategory(getCurrentSFVRoot()); +    if (cat) +    { +        if (cat->getParentUUID().notNull()) +        { +            if(isListViewMode()) +            { +                mCombinationInventoryPanel->changeFolderRoot(cat->getParentUUID()); +            } +            if(isGalleryViewMode()) +            { +                mCombinationGalleryPanel->setRootFolder(cat->getParentUUID()); +            } +            if(isCombinationViewMode()) +            { +                mCombinationInventoryPanel->changeFolderRoot(cat->getParentUUID()); +            } +        } +    } +} + +void LLPanelMainInventory::onBackFolderClicked() +{ +    if(isListViewMode()) +    { +        mCombinationInventoryPanel->onBackwardFolder(); +    } +    if(isGalleryViewMode()) +    { +        mCombinationGalleryPanel->onBackwardFolder(); +    } +    if(isCombinationViewMode()) +    { +        mCombinationInventoryPanel->onBackwardFolder(); +    } +} + +void LLPanelMainInventory::onForwardFolderClicked() +{ +    if(isListViewMode()) +    { +        mCombinationInventoryPanel->onForwardFolder(); +    } +    if(isGalleryViewMode()) +    { +        mCombinationGalleryPanel->onForwardFolder(); +    } +    if(isCombinationViewMode()) +    { +        mCombinationInventoryPanel->onForwardFolder(); +    } +} + +void LLPanelMainInventory::setSingleFolderViewRoot(const LLUUID& folder_id, bool clear_nav_history) +{ +    if(isListViewMode()) +    { +        mCombinationInventoryPanel->changeFolderRoot(folder_id); +        if(clear_nav_history) +        { +            mCombinationInventoryPanel->clearNavigationHistory(); +        } +    } +    else if(isGalleryViewMode()) +    { +        mCombinationGalleryPanel->setRootFolder(folder_id); +        if(clear_nav_history) +        { +            mCombinationGalleryPanel->clearNavigationHistory(); +        } +    } +    else if(isCombinationViewMode()) +    { +        mCombinationInventoryPanel->changeFolderRoot(folder_id); +    } +    updateNavButtons(); +} + +LLUUID LLPanelMainInventory::getSingleFolderViewRoot() +{ +    return mCombinationInventoryPanel->getSingleFolderRoot(); +} +  void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name)  {  	if (menu) @@ -1239,17 +1734,11 @@ void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_v  		LLView* spawning_view = getChild<LLView> (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);  	}  } -void LLPanelMainInventory::onTrashButtonClick() -{ -	onClipboardAction("delete"); -} -  void LLPanelMainInventory::onClipboardAction(const LLSD& userdata)  {  	std::string command_name = userdata.asString(); @@ -1258,13 +1747,22 @@ void LLPanelMainInventory::onClipboardAction(const LLSD& userdata)  void LLPanelMainInventory::saveTexture(const LLSD& userdata)  { -	LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); -	if (!current_item) -	{ -		return; -	} -	 -	const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); +    LLUUID item_id; +    if(mSingleFolderMode && isGalleryViewMode()) +    { +        item_id = mCombinationGalleryPanel->getFirstSelectedItemID(); +        if (item_id.isNull()) return; +    } +    else +    { +        LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); +        if (!current_item) +        { +            return; +        } +        item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); +    } +  	LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item_id), TAKE_FOCUS_YES);  	if (preview_texture)  	{ @@ -1278,6 +1776,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)  		return;  	const std::string command_name = userdata.asString(); +  	if (command_name == "new_window")  	{  		newWindow(); @@ -1347,35 +1846,69 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)  	}  	if (command_name == "find_original")  	{ +        if(mSingleFolderMode && isGalleryViewMode()) +        { +            LLInventoryObject *obj = gInventory.getObject(mCombinationGalleryPanel->getFirstSelectedItemID()); +            if (obj && obj->getIsLinkType()) +            { +                show_item_original(obj->getUUID()); +            } +        } +        else +        {  		LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();  		if (!current_item)  		{  			return;  		}  		static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(getActivePanel()->getModel(), "goto"); +        }  	}  	if (command_name == "find_links")  	{ -		LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); -		if (!current_item) -		{ -			return; -		} -		const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); -		const std::string &item_name = current_item->getViewModelItem()->getName(); -		mFilterSubString = item_name; - -		LLInventoryFilter &filter = mActivePanel->getFilter(); -		filter.setFindAllLinksMode(item_name, item_id); - -		mFilterEditor->setText(item_name); -		mFilterEditor->setFocus(TRUE); +        if(mSingleFolderMode && isGalleryViewMode()) +        { +            LLFloaterSidePanelContainer* inventory_container = newWindow(); +            if (inventory_container) +            { +                LLSidepanelInventory* sidepanel_inventory = dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true)); +                if (sidepanel_inventory) +                { +                    LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); +                    if (main_inventory) +                    { +                        LLInventoryObject *obj = gInventory.getObject(mCombinationGalleryPanel->getFirstSelectedItemID()); +                        if (obj) +                        { +                            main_inventory->findLinks(obj->getUUID(), obj->getName()); +                        } +                    } +                } +            } +        } +        else +        { +            LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); +            if (!current_item) +            { +                return; +            } +            const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); +            const std::string &item_name = current_item->getViewModelItem()->getName(); +            findLinks(item_id, item_name); +        }  	}  	if (command_name == "replace_links")  	{ -		LLSD params; +        LLSD params; +        if(mSingleFolderMode && isGalleryViewMode()) +        { +            params = LLSD(mCombinationGalleryPanel->getFirstSelectedItemID()); +        } +        else +        {  		LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();  		if (current_item)  		{ @@ -1390,23 +1923,72 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)  				}  			}  		} +        }  		LLFloaterReg::showInstance("linkreplace", params);  	} +    if (command_name == "close_inv_windows") +    { +        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* iv = dynamic_cast<LLFloaterSidePanelContainer*>(*iter++); +            if (iv) +            { +                iv->closeFloater(); +            } +        } +        LLFloaterReg::hideInstance("inventory_settings"); +    } + +    if (command_name == "toggle_search_outfits") +    { +        getCurrentFilter().toggleSearchVisibilityOutfits(); +    } +  	if (command_name == "toggle_search_trash")  	{ -		mActivePanel->getFilter().toggleSearchVisibilityTrash(); +        getCurrentFilter().toggleSearchVisibilityTrash();  	}  	if (command_name == "toggle_search_library")  	{ -		mActivePanel->getFilter().toggleSearchVisibilityLibrary(); +        getCurrentFilter().toggleSearchVisibilityLibrary();  	}  	if (command_name == "include_links")  	{ -		mActivePanel->getFilter().toggleSearchVisibilityLinks(); -	}		 +        getCurrentFilter().toggleSearchVisibilityLinks(); +	} + +    if (command_name == "share") +    { +        if(mSingleFolderMode && isGalleryViewMode()) +        { +            std::set<LLUUID> uuids{ mCombinationGalleryPanel->getFirstSelectedItemID()}; +            LLAvatarActions::shareWithAvatars(uuids, gFloaterView->getParentFloater(this)); +        } +        else +        { +            LLAvatarActions::shareWithAvatars(this); +        } +    } +    if (command_name == "shop") +    { +        LLWeb::loadURL(gSavedSettings.getString("MarketplaceURL")); +    } +    if (command_name == "list_view") +    { +        setViewMode(MODE_LIST); +    } +    if (command_name == "gallery_view") +    { +        setViewMode(MODE_GALLERY); +    } +    if (command_name == "combination_view") +    { +        setViewMode(MODE_COMBINATION); +    }  }  void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility ) @@ -1424,17 +2006,26 @@ void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility )  bool LLPanelMainInventory::isSaveTextureEnabled(const LLSD& userdata)  { -	LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); -	if (current_item)  -	{ -		LLViewerInventoryItem *inv_item = dynamic_cast<LLViewerInventoryItem*>(static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryObject()); +    LLViewerInventoryItem *inv_item = NULL; +    if(mSingleFolderMode && isGalleryViewMode()) +    { +        inv_item = gInventory.getItem(mCombinationGalleryPanel->getFirstSelectedItemID()); +    } +    else +    { +        LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); +        if (current_item) +        { +            inv_item = dynamic_cast<LLViewerInventoryItem*>(static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryObject()); +        } +    }  		if(inv_item)  		{  			bool can_save = inv_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); -			LLInventoryType::EType curr_type = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryType(); +            LLInventoryType::EType curr_type = inv_item->getInventoryType();  			return can_save && (curr_type == LLInventoryType::IT_TEXTURE || curr_type == LLInventoryType::IT_SNAPSHOT);  		} -	} +  	return false;  } @@ -1465,9 +2056,16 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)  	}  	if (command_name == "find_original")  	{ +        LLUUID item_id; +        if(mSingleFolderMode && isGalleryViewMode()) +        { +            item_id = mCombinationGalleryPanel->getFirstSelectedItemID(); +        } +        else{  		LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();  		if (!current_item) return FALSE; -		const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); +        item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); +        }  		const LLViewerInventoryItem *item = gInventory.getItem(item_id);  		if (item && item->getIsLinkType() && !item->getIsBrokenLink())  		{ @@ -1478,12 +2076,19 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)  	if (command_name == "find_links")  	{ +        LLUUID item_id; +        if(mSingleFolderMode && isGalleryViewMode()) +        { +            item_id = mCombinationGalleryPanel->getFirstSelectedItemID(); +        } +        else{  		LLFolderView* root = getActivePanel()->getRootFolder();  		std::set<LLFolderViewItem*> selection_set = root->getSelectionList();  		if (selection_set.size() != 1) return FALSE;  		LLFolderViewItem* current_item = root->getCurSelectedItem();  		if (!current_item) return FALSE; -		const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); +		item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); +        }  		const LLInventoryObject *obj = gInventory.getObject(item_id);  		if (obj && !obj->getIsLinkType() && LLAssetType::lookupCanLink(obj->getType()))  		{ @@ -1507,10 +2112,16 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)  	if (command_name == "share")  	{ +        if(mSingleFolderMode && isGalleryViewMode()) +        { +            return can_share_item(mCombinationGalleryPanel->getFirstSelectedItemID()); +        } +        else{  		LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem();  		if (!current_item) return FALSE;  		LLSidepanelInventory* parent = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory");  		return parent ? parent->canShare() : FALSE; +        }  	}  	if (command_name == "empty_trash")  	{ @@ -1528,9 +2139,24 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)  	return TRUE;  } +bool LLPanelMainInventory::isActionVisible(const LLSD& userdata) +{ +    const std::string param_str = userdata.asString(); +    if (param_str == "single_folder_view") +    { +        return mSingleFolderMode; +    } +    if (param_str == "multi_folder_view") +    { +        return !mSingleFolderMode; +    } + +    return true; +} +  BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata)  { -	U32 sort_order_mask = getActivePanel()->getSortOrder(); +	U32 sort_order_mask = (mSingleFolderMode && isGalleryViewMode()) ? mCombinationGalleryPanel->getSortOrder() :  getActivePanel()->getSortOrder();  	const std::string command_name = userdata.asString();  	if (command_name == "sort_by_name")  	{ @@ -1552,36 +2178,40 @@ BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata)  		return sort_order_mask & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;  	} +    if (command_name == "toggle_search_outfits") +    { +        return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_OUTFITS) != 0; +    } +  	if (command_name == "toggle_search_trash")  	{ -		return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_TRASH) != 0; +		return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_TRASH) != 0;  	}  	if (command_name == "toggle_search_library")  	{ -		return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LIBRARY) != 0; +		return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LIBRARY) != 0;  	}  	if (command_name == "include_links")  	{ -		return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) != 0;	 +        return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) != 0;  	}	 -	return FALSE; -} - -bool LLPanelMainInventory::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept) -{ -	*accept = ACCEPT_NO; - -	const bool is_enabled = isActionEnabled("delete"); -	if (is_enabled) *accept = ACCEPT_YES_MULTI; +    if (command_name == "list_view") +    { +        return isListViewMode(); +    } +    if (command_name == "gallery_view") +    { +        return isGalleryViewMode(); +    } +    if (command_name == "combination_view") +    { +        return isCombinationViewMode(); +    } -	if (is_enabled && drop) -	{ -		onClipboardAction("delete"); -	} -	return true; +	return FALSE;  }  void LLPanelMainInventory::setUploadCostIfNeeded() @@ -1599,10 +2229,410 @@ void LLPanelMainInventory::setUploadCostIfNeeded()  	}  } +bool is_add_allowed(LLUUID folder_id) +{ +    if(!gInventory.isObjectDescendentOf(folder_id, gInventory.getRootFolderID())) +    { +        return false; +    } + +    std::vector<LLFolderType::EType> not_allowed_types; +    not_allowed_types.push_back(LLFolderType::FT_LOST_AND_FOUND); +    not_allowed_types.push_back(LLFolderType::FT_FAVORITE); +    not_allowed_types.push_back(LLFolderType::FT_MARKETPLACE_LISTINGS); +    not_allowed_types.push_back(LLFolderType::FT_TRASH); +    not_allowed_types.push_back(LLFolderType::FT_CURRENT_OUTFIT); +    not_allowed_types.push_back(LLFolderType::FT_INBOX); + +    for (std::vector<LLFolderType::EType>::const_iterator it = not_allowed_types.begin(); +         it != not_allowed_types.end(); ++it) +    { +        if(gInventory.isObjectDescendentOf(folder_id, gInventory.findCategoryUUIDForType(*it))) +        { +            return false; +        } +    } + +    LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); +    if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) +    { +        return false; +    } +    return true; +} + +void LLPanelMainInventory::disableAddIfNeeded() +{ +    LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get(); +    if (menu) +    { +        bool enable = !mSingleFolderMode || is_add_allowed(getCurrentSFVRoot()); + +        menu->getChild<LLMenuItemGL>("New Folder")->setEnabled(enable && !isRecentItemsPanelSelected()); +        menu->getChild<LLMenuItemGL>("New Script")->setEnabled(enable); +        menu->getChild<LLMenuItemGL>("New Note")->setEnabled(enable); +        menu->getChild<LLMenuItemGL>("New Gesture")->setEnabled(enable); +        menu->setItemEnabled("New Clothes", enable); +        menu->setItemEnabled("New Body Parts", enable); +        menu->setItemEnabled("New Settings", enable); +    } +} +  bool LLPanelMainInventory::hasSettingsInventory()  {      return LLEnvironment::instance().isInventoryEnabled();  } +void LLPanelMainInventory::updateTitle() +{ +    LLFloater* inventory_floater = gFloaterView->getParentFloater(this); +    if(inventory_floater) +    { +        if(mSingleFolderMode) +        { +            inventory_floater->setTitle(getLocalizedRootName()); +            LLFloaterInventoryFinder *finder = getFinder(); +            if (finder) +            { +                finder->setTitle(getLocalizedRootName()); +            } +        } +        else +        { +            inventory_floater->setTitle(getString("inventory_title")); +        } +    } +    updateNavButtons(); +} + +void LLPanelMainInventory::onCombinationRootChanged(bool gallery_clicked) +{ +    if(gallery_clicked) +    { +        mCombinationInventoryPanel->changeFolderRoot(mCombinationGalleryPanel->getRootFolder()); +    } +    else +    { +        mCombinationGalleryPanel->setRootFolder(mCombinationInventoryPanel->getSingleFolderRoot()); +    } +    mForceShowInvLayout = false; +    updateTitle(); +    mReshapeInvLayout = true; +} + +void LLPanelMainInventory::onCombinationGallerySelectionChanged(const LLUUID& category_id) +{ +} + +void LLPanelMainInventory::onCombinationInventorySelectionChanged(const std::deque<LLFolderViewItem*>& items, BOOL user_action) +{ +    onSelectionChange(mCombinationInventoryPanel, items, user_action); +} + +void LLPanelMainInventory::updatePanelVisibility() +{ +    mDefaultViewPanel->setVisible(!mSingleFolderMode); +    mCombinationViewPanel->setVisible(mSingleFolderMode); +    mNavigationBtnsPanel->setVisible(mSingleFolderMode); +    mViewModeBtn->setImageOverlay(mSingleFolderMode ? getString("default_mode_btn") : getString("single_folder_mode_btn")); +    mViewModeBtn->setEnabled(mSingleFolderMode || (getAllItemsPanel() == getActivePanel())); +    if (mSingleFolderMode) +    { +        if (isCombinationViewMode()) +        { +            LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter(); +            comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_EXCLUDE_THUMBNAILS); +            comb_inv_filter.markDefault(); + +            LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter(); +            comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_ONLY_THUMBNAILS); +            comb_gallery_filter.markDefault(); + +            // visibility will be controled by updateCombinationVisibility() +            mCombinationGalleryLayoutPanel->setVisible(true); +            mCombinationGalleryPanel->setVisible(true); +            mCombinationListLayoutPanel->setVisible(true); +        } +        else +        { +            LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter(); +            comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_INCLUDE_THUMBNAILS); +            comb_inv_filter.markDefault(); + +            LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter(); +            comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_INCLUDE_THUMBNAILS); +            comb_gallery_filter.markDefault(); + +            mCombinationLayoutStack->setPanelSpacing(0); +            mCombinationGalleryLayoutPanel->setVisible(mSingleFolderMode && isGalleryViewMode()); +            mCombinationGalleryPanel->setVisible(mSingleFolderMode && isGalleryViewMode()); // to prevent or process updates +            mCombinationListLayoutPanel->setVisible(mSingleFolderMode && isListViewMode()); +        } +    } +    else +    { +        mCombinationGalleryLayoutPanel->setVisible(false); +        mCombinationGalleryPanel->setVisible(false); // to prevent updates +        mCombinationListLayoutPanel->setVisible(false); +    } +} + +void LLPanelMainInventory::updateCombinationVisibility() +{ +    if(mSingleFolderMode && isCombinationViewMode()) +    { +        bool is_gallery_empty = !mCombinationGalleryPanel->hasVisibleItems(); +        bool show_inv_pane = mCombinationInventoryPanel->hasVisibleItems() || is_gallery_empty || mForceShowInvLayout; + +        const S32 DRAG_HANDLE_PADDING = 12; // for drag handle to not overlap gallery when both inventories are visible +        mCombinationLayoutStack->setPanelSpacing(show_inv_pane ? DRAG_HANDLE_PADDING : 0); + +        mCombinationGalleryLayoutPanel->setVisible(!is_gallery_empty); +        mCombinationListLayoutPanel->setVisible(show_inv_pane); +        mCombinationInventoryPanel->getRootFolder()->setForceArrange(!show_inv_pane); +        if(mCombinationInventoryPanel->hasVisibleItems()) +        { +            mForceShowInvLayout = false; +        } +        if(is_gallery_empty) +        { +            mCombinationGalleryPanel->handleModifiedFilter(); +        } +         +        getActivePanel()->getRootFolder(); + +        if (mReshapeInvLayout +            && show_inv_pane +            && (mCombinationGalleryPanel->hasVisibleItems() || mCombinationGalleryPanel->areViewsInitialized()) +            && mCombinationInventoryPanel->areViewsInitialized()) +        { +            mReshapeInvLayout = false; + +            // force drop previous shape (because panel doesn't decrease shape properly) +            LLRect list_latout = mCombinationListLayoutPanel->getRect(); +            list_latout.mTop = list_latout.mBottom; // min height is at 100, so it should snap to be bigger +            mCombinationListLayoutPanel->setShape(list_latout, false); + +            LLRect inv_inner_rect = mCombinationInventoryPanel->getScrollableContainer()->getScrolledViewRect(); +            S32 inv_height = inv_inner_rect.getHeight() +                + (mCombinationInventoryPanel->getScrollableContainer()->getBorderWidth() * 2) +                + mCombinationInventoryPanel->getScrollableContainer()->getSize(); +            LLRect inner_galery_rect = mCombinationGalleryPanel->getScrollableContainer()->getScrolledViewRect(); +            S32 gallery_height = inner_galery_rect.getHeight() +                + (mCombinationGalleryPanel->getScrollableContainer()->getBorderWidth() * 2) +                + mCombinationGalleryPanel->getScrollableContainer()->getSize(); +            LLRect layout_rect = mCombinationViewPanel->getRect(); + +            // by default make it take 1/3 of the panel +            S32 list_default_height = layout_rect.getHeight() / 3; +            // Don't set height from gallery_default_height - needs to account for a resizer in such case +            S32 gallery_default_height = layout_rect.getHeight() - list_default_height; + +            if (inv_height > list_default_height +                && gallery_height < gallery_default_height) +            { +                LLRect gallery_latout = mCombinationGalleryLayoutPanel->getRect(); +                gallery_latout.mTop = gallery_latout.mBottom + gallery_height; +                mCombinationGalleryLayoutPanel->setShape(gallery_latout, true /*tell stack to account for new shape*/); +            } +            else if (inv_height < list_default_height +                     && gallery_height > gallery_default_height) +            { +                LLRect list_latout = mCombinationListLayoutPanel->getRect(); +                list_latout.mTop = list_latout.mBottom + inv_height; +                mCombinationListLayoutPanel->setShape(list_latout, true /*tell stack to account for new shape*/); +            } +            else +            { +                LLRect list_latout = mCombinationListLayoutPanel->getRect(); +                list_latout.mTop = list_latout.mBottom + list_default_height; +                mCombinationListLayoutPanel->setShape(list_latout, true /*tell stack to account for new shape*/); +            } +        } +    } + +    if (mSingleFolderMode +        && !isGalleryViewMode() +        && mCombInvUUIDNeedsRename.notNull() +        && mCombinationInventoryPanel->areViewsInitialized()) +    { +        mCombinationInventoryPanel->setSelectionByID(mCombInvUUIDNeedsRename, TRUE); +        mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection(); +        mCombinationInventoryPanel->getRootFolder()->setNeedsAutoRename(TRUE); +        mCombInvUUIDNeedsRename.setNull(); +    } +} + +void LLPanelMainInventory::updateNavButtons() +{ +    if(isListViewMode()) +    { +        mBackBtn->setEnabled(mCombinationInventoryPanel->isBackwardAvailable()); +        mForwardBtn->setEnabled(mCombinationInventoryPanel->isForwardAvailable()); +    } +    if(isGalleryViewMode()) +    { +        mBackBtn->setEnabled(mCombinationGalleryPanel->isBackwardAvailable()); +        mForwardBtn->setEnabled(mCombinationGalleryPanel->isForwardAvailable()); +    } +    if(isCombinationViewMode()) +    { +        mBackBtn->setEnabled(mCombinationInventoryPanel->isBackwardAvailable()); +        mForwardBtn->setEnabled(mCombinationInventoryPanel->isForwardAvailable()); +    } + +    const LLViewerInventoryCategory* cat = gInventory.getCategory(getCurrentSFVRoot()); +    bool up_enabled = (cat && cat->getParentUUID().notNull()); +    mUpBtn->setEnabled(up_enabled); +} + +LLSidepanelInventory* LLPanelMainInventory::getParentSidepanelInventory() +{ +    LLFloaterSidePanelContainer* inventory_container = dynamic_cast<LLFloaterSidePanelContainer*>(gFloaterView->getParentFloater(this)); +    if(inventory_container) +    { +        return dynamic_cast<LLSidepanelInventory*>(inventory_container->findChild<LLPanel>("main_panel", true)); +    } +    return NULL; +} + +void LLPanelMainInventory::setViewMode(EViewModeType mode) +{ +    if(mode != mViewMode) +    { +        std::list<LLUUID> forward_history; +        std::list<LLUUID> backward_history; +        U32 sort_order = 0; +        switch(mViewMode) +        { +            case MODE_LIST: +                forward_history = mCombinationInventoryPanel->getNavForwardList(); +                backward_history = mCombinationInventoryPanel->getNavBackwardList(); +                sort_order = mCombinationInventoryPanel->getSortOrder(); +                break; +            case MODE_GALLERY: +                forward_history = mCombinationGalleryPanel->getNavForwardList(); +                backward_history = mCombinationGalleryPanel->getNavBackwardList(); +                sort_order = mCombinationGalleryPanel->getSortOrder(); +                break; +            case MODE_COMBINATION: +                forward_history = mCombinationInventoryPanel->getNavForwardList(); +                backward_history = mCombinationInventoryPanel->getNavBackwardList(); +                mCombinationInventoryPanel->getRootFolder()->setForceArrange(false); +                sort_order = mCombinationInventoryPanel->getSortOrder(); +                break; +        } +             +        LLUUID cur_root = getCurrentSFVRoot(); +        mViewMode = mode; + +        updatePanelVisibility(); + +        if(isListViewMode()) +        { +            mCombinationInventoryPanel->changeFolderRoot(cur_root); +            mCombinationInventoryPanel->setNavForwardList(forward_history); +            mCombinationInventoryPanel->setNavBackwardList(backward_history); +            mCombinationInventoryPanel->setSortOrder(sort_order); +        } +        if(isGalleryViewMode()) +        { +            mCombinationGalleryPanel->setRootFolder(cur_root); +            mCombinationGalleryPanel->setNavForwardList(forward_history); +            mCombinationGalleryPanel->setNavBackwardList(backward_history); +            mCombinationGalleryPanel->setSortOrder(sort_order, true); +        } +        if(isCombinationViewMode()) +        { +            mCombinationInventoryPanel->changeFolderRoot(cur_root); +            mCombinationGalleryPanel->setRootFolder(cur_root); +            mCombinationInventoryPanel->setNavForwardList(forward_history); +            mCombinationInventoryPanel->setNavBackwardList(backward_history); +            mCombinationGalleryPanel->setNavForwardList(forward_history); +            mCombinationGalleryPanel->setNavBackwardList(backward_history); +            mCombinationInventoryPanel->setSortOrder(sort_order); +            mCombinationGalleryPanel->setSortOrder(sort_order, true); +        } + +        updateNavButtons(); + +        onFilterSelected(); +        if((isListViewMode() && (mActivePanel->getFilterSubString() != mFilterSubString)) || +           (isGalleryViewMode() && (mCombinationGalleryPanel->getFilterSubString() != mFilterSubString))) +        { +            onFilterEdit(mFilterSubString); +        } +    } +} + +std::string LLPanelMainInventory::getLocalizedRootName() +{ +    return mSingleFolderMode ? get_localized_folder_name(getCurrentSFVRoot()) : ""; +} + +LLUUID LLPanelMainInventory::getCurrentSFVRoot() +{ +    if(isListViewMode()) +    { +        return mCombinationInventoryPanel->getSingleFolderRoot(); +    } +    if(isGalleryViewMode()) +    { +        return mCombinationGalleryPanel->getRootFolder(); +    } +    if(isCombinationViewMode()) +    { +        return mCombinationInventoryPanel->getSingleFolderRoot(); +    } +    return LLUUID::null; +} + +LLInventoryFilter& LLPanelMainInventory::getCurrentFilter() +{ +    if(mSingleFolderMode && isGalleryViewMode()) +    { +        return mCombinationGalleryPanel->getFilter(); +    } +    else +    { +        return mActivePanel->getFilter(); +    } +} + +void LLPanelMainInventory::setGallerySelection(const LLUUID& item_id, bool new_window) +{ +    if(mSingleFolderMode && isGalleryViewMode()) +    { +        mCombinationGalleryPanel->changeItemSelection(item_id, true); +    } +    else if(mSingleFolderMode && isCombinationViewMode()) +    { +        if(mCombinationGalleryPanel->getFilter().checkAgainstFilterThumbnails(item_id)) +        { +            mCombinationGalleryPanel->changeItemSelection(item_id, false); +            scrollToGallerySelection(); +        } +        else +        { +            mCombinationInventoryPanel->setSelection(item_id, true); +            scrollToInvPanelSelection(); +        } +    } +    else if (mSingleFolderMode && isListViewMode()) +    { +        mCombinationInventoryPanel->setSelection(item_id, true); +    } +} + +void LLPanelMainInventory::scrollToGallerySelection() +{ +    mCombinationGalleryPanel->scrollToShowItem(mCombinationGalleryPanel->getFirstSelectedItemID()); +} + +void LLPanelMainInventory::scrollToInvPanelSelection() +{ +    mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection(); +} +  // List Commands                                                              //  //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index 7aae5a0b3c..79501e63bc 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -30,6 +30,7 @@  #include "llpanel.h"  #include "llinventoryobserver.h" +#include "llinventorypanel.h"  #include "lldndbutton.h"  #include "llfolderview.h" @@ -37,14 +38,17 @@  class LLComboBox;  class LLFolderViewItem;  class LLInventoryPanel; +class LLInventoryGallery;  class LLSaveFolderState;  class LLFilterEditor;  class LLTabContainer;  class LLFloaterInventoryFinder;  class LLMenuButton;  class LLMenuGL; +class LLSidepanelInventory;  class LLToggleableMenu;  class LLFloater; +class LLFloaterSidePanelContainer;  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLPanelMainInventory @@ -63,6 +67,13 @@ public:  	BOOL postBuild(); +    enum EViewModeType +    { +        MODE_LIST, +        MODE_GALLERY, +        MODE_COMBINATION +    }; +  	virtual BOOL handleKeyHere(KEY key, MASK mask);  	// Inherited functionality @@ -80,6 +91,7 @@ public:  	LLInventoryPanel* getAllItemsPanel();  	void selectAllItemsPanel();  	const LLInventoryPanel* getActivePanel() const { return mActivePanel; } +    void setActivePanel();  	bool isRecentItemsPanelSelected(); @@ -91,13 +103,40 @@ public:  	void setFocusFilterEditor(); -	static void newWindow(); +	static LLFloaterSidePanelContainer* newWindow(); +    static void newFolderWindow(LLUUID folder_id = LLUUID(), LLUUID item_to_select = LLUUID());  	void toggleFindOptions();      void resetFilters();      void resetAllItemsFilters(); +    void findLinks(const LLUUID& item_id, const std::string& item_name); + +    void onViewModeClick(); +    void toggleViewMode(); +    void initSingleFolderRoot(const LLUUID& start_folder_id = LLUUID::null); +    void initInventoryViews(); +    void onUpFolderClicked(); +    void onBackFolderClicked(); +    void onForwardFolderClicked(); +    void setSingleFolderViewRoot(const LLUUID& folder_id, bool clear_nav_history = true); +    void setGallerySelection(const LLUUID& item_id, bool new_window = false); +    LLUUID getSingleFolderViewRoot(); +    bool isSingleFolderMode() { return mSingleFolderMode; } + +    void scrollToGallerySelection(); +    void scrollToInvPanelSelection(); + +    void setViewMode(EViewModeType mode); +    bool isListViewMode() { return (mViewMode == MODE_LIST); } +    bool isGalleryViewMode() { return (mViewMode == MODE_GALLERY); } +    bool isCombinationViewMode() { return (mViewMode == MODE_COMBINATION); } +    LLUUID getCurrentSFVRoot(); +    std::string getLocalizedRootName(); + +    LLInventoryFilter& getCurrentFilter(); +  protected:  	//  	// Misc functions @@ -127,9 +166,15 @@ protected:  	bool isSaveTextureEnabled(const LLSD& userdata);  	void updateItemcountText(); +    void updatePanelVisibility(); +    void updateCombinationVisibility(); +  	void onFocusReceived();  	void onSelectSearchType();  	void updateSearchTypeCombo(); +    void setSearchType(LLInventoryFilter::ESearchType type); + +    LLSidepanelInventory* getParentSidepanelInventory();  private:  	LLFloaterInventoryFinder* getFinder(); @@ -150,7 +195,26 @@ private:  	std::string					mCategoryCountString;  	LLComboBox*					mSearchTypeCombo; +    LLButton* mBackBtn; +    LLButton* mForwardBtn; +    LLButton* mUpBtn; +    LLButton* mViewModeBtn; +    LLLayoutPanel* mNavigationBtnsPanel; + +    LLPanel* mDefaultViewPanel; +    LLPanel* mCombinationViewPanel; +    bool mSingleFolderMode; +    EViewModeType mViewMode; + +    LLInventorySingleFolderPanel* mCombinationInventoryPanel; +    LLInventoryGallery* mCombinationGalleryPanel; +    LLPanel* mCombinationGalleryLayoutPanel; +    LLLayoutPanel* mCombinationListLayoutPanel; +    LLLayoutStack* mCombinationLayoutStack; + +    boost::signals2::connection mListViewRootUpdatedConnection; +    boost::signals2::connection mGalleryRootUpdatedConnection;  	//////////////////////////////////////////////////////////////////////////////////  	// List Commands                                                                // @@ -159,26 +223,37 @@ protected:  	void updateListCommands();  	void onAddButtonClick();  	void showActionMenu(LLMenuGL* menu, std::string spawning_view_name); -	void onTrashButtonClick();  	void onClipboardAction(const LLSD& userdata);  	BOOL isActionEnabled(const LLSD& command_name);  	BOOL isActionChecked(const LLSD& userdata);  	void onCustomAction(const LLSD& command_name); -	bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept); +    bool isActionVisible(const LLSD& userdata);      static bool hasSettingsInventory(); +    void updateTitle(); +    void updateNavButtons(); +     +    void onCombinationRootChanged(bool gallery_clicked); +    void onCombinationGallerySelectionChanged(const LLUUID& category_id); +    void onCombinationInventorySelectionChanged(const std::deque<LLFolderViewItem*>& items, BOOL user_action);  	/**  	 * Set upload cost in "Upload" sub menu.  	 */  	void setUploadCostIfNeeded(); +    void disableAddIfNeeded();  private: -	LLDragAndDropButton*		mTrashButton;  	LLToggleableMenu*			mMenuGearDefault; +    LLToggleableMenu*           mMenuViewDefault;  	LLToggleableMenu*			mMenuVisibility;  	LLMenuButton*				mGearMenuButton; +    LLMenuButton*               mViewMenuButton;  	LLMenuButton*				mVisibilityMenuButton;  	LLHandle<LLView>			mMenuAddHandle;  	bool						mNeedUploadCost; + +    bool                        mForceShowInvLayout; +    bool                        mReshapeInvLayout; +    LLUUID                      mCombInvUUIDNeedsRename;  	// List Commands                                                              //  	////////////////////////////////////////////////////////////////////////////////  }; diff --git a/indra/newview/llpanelmarketplaceinbox.cpp b/indra/newview/llpanelmarketplaceinbox.cpp index 8a86f4f63d..3638ee14fc 100644 --- a/indra/newview/llpanelmarketplaceinbox.cpp +++ b/indra/newview/llpanelmarketplaceinbox.cpp @@ -75,9 +75,6 @@ BOOL LLPanelMarketplaceInbox::postBuild()  void LLPanelMarketplaceInbox::onSelectionChange()  { -	LLSidepanelInventory* sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); -		 -	sidepanel_inventory->updateVerbs();  } diff --git a/indra/newview/llpanelmarketplaceinboxinventory.cpp b/indra/newview/llpanelmarketplaceinboxinventory.cpp index 7a6631448b..e13bd0412d 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceinboxinventory.cpp @@ -62,10 +62,13 @@ LLInboxInventoryPanel::LLInboxInventoryPanel(const LLInboxInventoryPanel::Params  :	LLInventoryPanel(p)  {  	LLInboxNewItemsStorage::getInstance()->load(); +    LLInboxNewItemsStorage::getInstance()->addInboxPanel(this);  }  LLInboxInventoryPanel::~LLInboxInventoryPanel() -{} +{ +    LLInboxNewItemsStorage::getInstance()->removeInboxPanel(this); +}  void LLInboxInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)  { @@ -108,6 +111,21 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b  	return LLUICtrlFactory::create<LLInboxFolderViewItem>(params);  } +void LLInboxInventoryPanel::onRemoveItemFreshness(const LLUUID& item_id) +{ +    LLInboxFolderViewFolder* inbox_folder_view = dynamic_cast<LLInboxFolderViewFolder*>(getFolderByID(item_id)); +    if(inbox_folder_view) +    { +        inbox_folder_view->setFresh(false); +    } + +    LLInboxFolderViewItem* inbox_item_view = dynamic_cast<LLInboxFolderViewItem*>(getItemByID(item_id)); +    if(inbox_item_view) +    { +        inbox_item_view->setFresh(false); +    } +} +  //  // LLInboxFolderViewFolder Implementation  // @@ -340,4 +358,18 @@ void LLInboxNewItemsStorage::load()  		}  	}  } + +void LLInboxNewItemsStorage::removeItem(const LLUUID& id) +{ +    mNewItemsIDs.erase(id); + +    //notify inbox panels +    for (auto inbox : mInboxPanels) +    { +        if(inbox) +        { +            inbox->onRemoveItemFreshness(id); +        } +    } +}  // eof diff --git a/indra/newview/llpanelmarketplaceinboxinventory.h b/indra/newview/llpanelmarketplaceinboxinventory.h index 3e508e801b..9eef5f209c 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.h +++ b/indra/newview/llpanelmarketplaceinboxinventory.h @@ -49,6 +49,8 @@ public:  	void initFromParams(const LLInventoryPanel::Params&);  	LLFolderViewFolder*	createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop);  	LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); + +    void onRemoveItemFreshness(const LLUUID& item_id);  }; @@ -77,6 +79,7 @@ public:  	void deFreshify();  	bool isFresh() const { return mFresh; } +    void setFresh(bool is_fresh)  { mFresh = is_fresh; }  protected:  	bool mFresh; @@ -108,6 +111,7 @@ public:  	void deFreshify();  	bool isFresh() const { return mFresh; } +    void setFresh(bool is_fresh)  { mFresh = is_fresh; }  protected:  	bool mFresh; @@ -125,11 +129,16 @@ public:  	void load();  	void addFreshItem(const LLUUID& id) { mNewItemsIDs.insert(id); } -	void removeItem(const LLUUID& id) { mNewItemsIDs.erase(id); } +    void removeItem(const LLUUID& id);  	bool isItemFresh(const LLUUID& id) { return (mNewItemsIDs.find(id) != mNewItemsIDs.end()); } +    void addInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.insert(inbox); } +    void removeInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.erase(inbox); } +  private:  	std::set<LLUUID> mNewItemsIDs; + +    std::set<LLInboxInventoryPanel*> mInboxPanels;  };  #endif //LL_INBOXINVENTORYPANEL_H diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 6a82a3b35d..a3bbd00601 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -116,6 +116,7 @@ public:  	virtual PermissionMask getPermissionMask() const { return PERM_NONE; }  	/*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; }  	virtual const LLUUID& getUUID() const { return mUUID; } +    virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null;}  	virtual time_t getCreationDate() const;  	virtual void setCreationDate(time_t creation_date_utc); @@ -124,6 +125,7 @@ public:  	virtual BOOL canOpenItem() const { return FALSE; }  	virtual void closeItem() {}  	virtual void selectItem() {} +    virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {}  	virtual BOOL isItemRenameable() const;  	virtual BOOL renameItem(const std::string& new_name);  	virtual BOOL isItemMovable() const; @@ -1398,21 +1400,6 @@ void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object,  	{  		mInventoryNeedsUpdate = TRUE;  	} - -	// refresh any properties floaters that are hanging around. -	if(inventory) -	{ -		for (LLInventoryObject::object_list_t::const_iterator iter = inventory->begin(); -			 iter != inventory->end(); ) -		{ -			LLInventoryObject* item = *iter++; -			LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properties", item->getUUID()); -			if(floater) -			{ -				floater->refresh(); -			} -		} -	}  }  void LLPanelObjectInventory::updateInventory() diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 35582d2967..4a755a6e93 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -699,8 +699,12 @@ void LLPanelOutfitEdit::onFolderViewFilterCommitted(LLUICtrl* ctrl)  	LLOpenFoldersWithSelection opener;  	mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener);  	mInventoryItemsPanel->getRootFolder()->scrollToShowSelection(); -	 -	LLInventoryModelBackgroundFetch::instance().start(); + +    if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) +    { +        llassert(false); // this should have been done on startup +        LLInventoryModelBackgroundFetch::instance().start(); +    }  }  void LLPanelOutfitEdit::onListViewFilterCommitted(LLUICtrl* ctrl) @@ -737,8 +741,12 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string)  		mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener);  		mInventoryItemsPanel->getRootFolder()->scrollToShowSelection();  	} -	 -	LLInventoryModelBackgroundFetch::instance().start(); + +    if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) +    { +        llassert(false); // this should have been done on startup +        LLInventoryModelBackgroundFetch::instance().start(); +    }  	if (mInventoryItemsPanel->getFilterSubString().empty() && mSearchString.empty())  	{ diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 531073526b..d8c34d5c40 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -87,7 +87,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); @@ -166,7 +166,11 @@ void LLPanelOutfitsInventory::onSearchEdit(const std::string& string)  		mActivePanel->setFilterSubString(LLStringUtil::null);  	} -	LLInventoryModelBackgroundFetch::instance().start(); +    if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) +    { +        llassert(false); // this should have been done on startup +        LLInventoryModelBackgroundFetch::instance().start(); +    }  	if (mActivePanel->getFilterSubString().empty() && string.empty())  	{ diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 7aabd5247a..3333c832d2 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -71,6 +71,7 @@  #include "llpanelblockedlist.h"  #include "llpanelprofileclassifieds.h"  #include "llpanelprofilepicks.h" +#include "llthumbnailctrl.h"  #include "lltrans.h"  #include "llviewercontrol.h"  #include "llviewermenu.h" //is_agent_mappable @@ -366,7 +367,7 @@ LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::stri      httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];      status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); -    LL_WARNS("AvatarProperties") << result << LL_ENDL; +    LL_DEBUGS("AvatarProperties") << result << LL_ENDL;      if (!status)      { @@ -913,7 +914,7 @@ BOOL LLPanelProfileSecondLife::postBuild()  {      mGroupList              = getChild<LLGroupList>("group_list");      mShowInSearchCombo      = getChild<LLComboBox>("show_in_search"); -    mSecondLifePic          = getChild<LLIconCtrl>("2nd_life_pic"); +    mSecondLifePic          = getChild<LLThumbnailCtrl>("2nd_life_pic");      mSecondLifePicLayout    = getChild<LLPanel>("image_panel");      mDescriptionEdit        = getChild<LLTextEditor>("sl_description_edit");      mAgentActionMenuButton  = getChild<LLMenuButton>("agent_actions_menu"); @@ -1497,7 +1498,6 @@ void LLPanelProfileSecondLife::setLoaded()  } -  class LLProfileImagePicker : public LLFilePickerThread  {  public: @@ -1544,15 +1544,20 @@ void LLProfileImagePicker::notify(const std::vector<std::string>& filenames)      const S32 MAX_DIM = 256;      if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM))      { -        //todo: image not supported notification -        LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", failed to open image" << LL_ENDL; +        LLSD notif_args; +        notif_args["REASON"] = LLImage::getLastError().c_str(); +        LLNotificationsUtil::add("CannotUploadTexture", notif_args); +        LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", " << notif_args["REASON"].asString() << LL_ENDL;          return;      }      std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP);      if (cap_url.empty())      { -        LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", no cap found" << LL_ENDL; +        LLSD args; +        args["CAPABILITY"] = PROFILE_IMAGE_UPLOAD_CAP; +        LLNotificationsUtil::add("RegionCapabilityRequestError", args); +        LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", no cap found" << LL_ENDL;          return;      } @@ -1955,30 +1960,16 @@ void LLPanelProfileSecondLife::onShowTexturePicker()              mFloaterTexturePickerHandle = texture_floaterp->getHandle(); -            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id) +            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&)              {                  if (op == LLTextureCtrl::TEXTURE_SELECT)                  { -                    LLUUID image_asset_id; -                    LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterTexturePickerHandle.get(); -                    if (floaterp) -                    { -                        if (id.notNull()) -                        { -                            image_asset_id = id; -                        } -                        else -                        { -                            image_asset_id = floaterp->getAssetID(); -                        } -                    } - -                    onCommitProfileImage(image_asset_id); +                    onCommitProfileImage(asset_id);                  }              });              texture_floaterp->setLocalTextureEnabled(FALSE);              texture_floaterp->setBakeTextureEnabled(FALSE); -            texture_floaterp->setCanApply(false, true); +            texture_floaterp->setCanApply(false, true, false);              parent_floater->addDependentFloater(mFloaterTexturePickerHandle); @@ -2193,7 +2184,7 @@ LLPanelProfileFirstLife::~LLPanelProfileFirstLife()  BOOL LLPanelProfileFirstLife::postBuild()  {      mDescriptionEdit = getChild<LLTextEditor>("fl_description_edit"); -    mPicture = getChild<LLIconCtrl>("real_world_pic"); +    mPicture = getChild<LLThumbnailCtrl>("real_world_pic");      mUploadPhoto = getChild<LLButton>("fl_upload_image");      mChangePhoto = getChild<LLButton>("fl_change_image"); @@ -2296,29 +2287,15 @@ void LLPanelProfileFirstLife::onChangePhoto()              mFloaterTexturePickerHandle = texture_floaterp->getHandle(); -            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id) +            texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&)              {                  if (op == LLTextureCtrl::TEXTURE_SELECT)                  { -                    LLUUID image_asset_id; -                    LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterTexturePickerHandle.get(); -                    if (floaterp) -                    { -                        if (id.notNull()) -                        { -                            image_asset_id = id; -                        } -                        else -                        { -                            image_asset_id = floaterp->getAssetID(); -                        } -                    } - -                    onCommitPhoto(image_asset_id); +                    onCommitPhoto(asset_id);                  }              });              texture_floaterp->setLocalTextureEnabled(FALSE); -            texture_floaterp->setCanApply(false, true); +            texture_floaterp->setCanApply(false, true, false);              parent_floater->addDependentFloater(mFloaterTexturePickerHandle); diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index d32bb943bd..11632a10ae 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -58,6 +58,7 @@ class LLTextBase;  class LLMenuButton;  class LLLineEditor;  class LLTextEditor; +class LLThumbnailCtrl;  class LLPanelProfileClassifieds;  class LLPanelProfilePicks;  class LLViewerFetchedTexture; @@ -192,7 +193,7 @@ private:  	LLGroupList*		mGroupList;      LLComboBox*			mShowInSearchCombo; -    LLIconCtrl*			mSecondLifePic; +    LLThumbnailCtrl*	mSecondLifePic;  	LLPanel*			mSecondLifePicLayout;      LLTextEditor*		mDescriptionEdit;      LLMenuButton*		mAgentActionMenuButton; @@ -301,7 +302,7 @@ protected:      void onDiscardDescriptionChanges();  	LLTextEditor*	mDescriptionEdit; -    LLIconCtrl*		mPicture; +    LLThumbnailCtrl* mPicture;      LLButton* mUploadPhoto;      LLButton* mChangePhoto;      LLButton* mRemovePhoto; diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index bc9f0cef83..5242c4fef9 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -40,6 +40,7 @@  #include "llinventorymodel.h"  #include "llinventoryobserver.h"  #include "llmenubutton.h" +#include "lloutfitobserver.h"  #include "llscrolllistctrl.h"  #include "llviewermenu.h"  #include "llviewerregion.h" @@ -218,8 +219,6 @@ LLPanelWearing::LLPanelWearing()  	,	mIsInitialized(false)  	,	mAttachmentsChangedConnection()  { -	mCategoriesObserver = new LLInventoryCategoriesObserver(); -  	mGearMenu = new LLWearingGearMenu(this);  	mContextMenu = new LLWearingContextMenu();  	mAttachmentsMenu = new LLTempAttachmentsContextMenu(this); @@ -231,12 +230,6 @@ LLPanelWearing::~LLPanelWearing()  	delete mContextMenu;  	delete mAttachmentsMenu; -	if (gInventory.containsObserver(mCategoriesObserver)) -	{ -		gInventory.removeObserver(mCategoriesObserver); -	} -	delete mCategoriesObserver; -  	if (mAttachmentsChangedConnection.connected())  	{  		mAttachmentsChangedConnection.disconnect(); @@ -281,10 +274,8 @@ void LLPanelWearing::onOpen(const LLSD& /*info*/)  		if (!category)  			return; -		gInventory.addObserver(mCategoriesObserver); -  		// Start observing changes in Current Outfit category. -		mCategoriesObserver->addCategory(cof, boost::bind(&LLWearableItemsList::updateList, mCOFItemsList, cof)); +        LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLWearableItemsList::updateList, mCOFItemsList, cof));  		// Fetch Current Outfit contents and refresh the list to display  		// initially fetched items. If not all items are fetched now diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h index 715404a457..18e543eec6 100644 --- a/indra/newview/llpanelwearing.h +++ b/indra/newview/llpanelwearing.h @@ -90,7 +90,6 @@ private:  	void getAttachmentLimitsCoro(std::string url); -	LLInventoryCategoriesObserver* 	mCategoriesObserver;  	LLWearableItemsList* 			mCOFItemsList;  	LLScrollListCtrl*				mTempItemsList;  	LLWearingGearMenu*				mGearMenu; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index cab0b74cd0..ba7af63f76 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -57,7 +57,6 @@  #include "llviewerwindow.h"  #include "lldrawable.h"  #include "llfloaterinspect.h" -#include "llfloaterproperties.h"  #include "llfloaterreporter.h"  #include "llfloaterreg.h"  #include "llfloatertools.h" @@ -6858,8 +6857,6 @@ void dialog_refresh_all()  		gMenuAttachmentOther->arrange();  	} -	LLFloaterProperties::dirtyAll(); -  	LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance<LLFloaterInspect>("inspect");  	if(inspect_instance)  	{ diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index f5212a3026..eaa23d6a6c 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -95,6 +95,20 @@ namespace  //========================================================================= +void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb) +{ +    inventory_result_fn cb = NULL; + +    if (created_cb != NULL) +    { +        cb = [created_cb](LLUUID asset_id, LLUUID inventory_id, LLUUID object_id, LLSD results) +        { +            created_cb(inventory_id); +        }; +    } +    createNewInventoryItem(stype, parent_id, cb); +} +  void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID &parent_id, inventory_result_fn callback)  {      LLTransactionID tid; diff --git a/indra/newview/llsettingsvo.h b/indra/newview/llsettingsvo.h index 05ec0e9275..4f410ab7d9 100644 --- a/indra/newview/llsettingsvo.h +++ b/indra/newview/llsettingsvo.h @@ -49,6 +49,7 @@ public:      typedef std::function<void(LLInventoryItem *inv_item, LLSettingsBase::ptr_t settings, S32 status, LLExtStat extstat)> inventory_download_fn;      typedef std::function<void(LLUUID asset_id, LLUUID inventory_id, LLUUID object_id, LLSD results)>           inventory_result_fn; +    static void     createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID& parent_id, std::function<void(const LLUUID&)> created_cb);      static void     createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID &parent_id, inventory_result_fn callback = inventory_result_fn());      static void     createInventoryItem(const LLSettingsBase::ptr_t &settings, const LLUUID &parent_id, std::string settings_name, inventory_result_fn callback = inventory_result_fn());      static void     createInventoryItem(const LLSettingsBase::ptr_t &settings, U32 next_owner_perm, const LLUUID &parent_id, std::string settings_name, inventory_result_fn callback = inventory_result_fn()); diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index a5dcdc41ed..e970f70e92 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -73,6 +73,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; +  //  // Helpers  // @@ -115,21 +117,19 @@ private:  LLSidepanelInventory::LLSidepanelInventory()  	: LLPanel() -	, mItemPanel(NULL)  	, mPanelMainInventory(NULL)  	, mInboxEnabled(false)  	, mCategoriesObserver(NULL)  	, mInboxAddedObserver(NULL) +    , mInboxLayoutPanel(NULL)  {  	//buildFromFile( "panel_inventory.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()  }  LLSidepanelInventory::~LLSidepanelInventory()  { -	LLLayoutPanel* inbox_layout_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME); -  	// Save the InventoryMainPanelHeight in settings per account -	gSavedPerAccountSettings.setS32("InventoryInboxHeight", inbox_layout_panel->getTargetDim()); +	gSavedPerAccountSettings.setS32("InventoryInboxHeight", mInboxLayoutPanel->getTargetDim());  	if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver))  	{ @@ -158,29 +158,11 @@ BOOL LLSidepanelInventory::postBuild()  	// UI elements from inventory panel  	{  		mInventoryPanel = getChild<LLPanel>("sidepanel_inventory_panel"); - -		mInfoBtn = mInventoryPanel->getChild<LLButton>("info_btn"); -		mInfoBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onInfoButtonClicked, this)); -		 -		mShareBtn = mInventoryPanel->getChild<LLButton>("share_btn"); -		mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this)); -		 -		mShopBtn = mInventoryPanel->getChild<LLButton>("shop_btn"); -		mShopBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShopButtonClicked, this)); - -		mWearBtn = mInventoryPanel->getChild<LLButton>("wear_btn"); -		mWearBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onWearButtonClicked, this)); -		 -		mPlayBtn = mInventoryPanel->getChild<LLButton>("play_btn"); -		mPlayBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onPlayButtonClicked, this)); -		 -		mTeleportBtn = mInventoryPanel->getChild<LLButton>("teleport_btn"); -		mTeleportBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onTeleportButtonClicked, this));  		mPanelMainInventory = mInventoryPanel->getChild<LLPanelMainInventory>("panel_main_inventory");  		mPanelMainInventory->setSelectCallback(boost::bind(&LLSidepanelInventory::onSelectionChange, this, _1, _2)); -		LLTabContainer* tabs = mPanelMainInventory->getChild<LLTabContainer>("inventory filter tabs"); -		tabs->setCommitCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this)); +		//LLTabContainer* tabs = mPanelMainInventory->getChild<LLTabContainer>("inventory filter tabs"); +		//tabs->setCommitCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this));  		/*   		   EXT-4846 : "Can we suppress the "Landmarks" and "My Favorites" folder since they have their own Task Panel?" @@ -190,25 +172,7 @@ BOOL LLSidepanelInventory::postBuild()  		my_inventory_panel->addHideFolderType(LLFolderType::FT_FAVORITE);  		*/ -		LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this)); -	} - -	// UI elements from item panel -	{ -		mItemPanel = getChild<LLSidepanelItemInfo>("sidepanel__item_panel"); -		 -		LLButton* back_btn = mItemPanel->getChild<LLButton>("back_btn"); -		back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this)); -	} - -	// UI elements from task panel -	{ -		mTaskPanel = findChild<LLSidepanelTaskInfo>("sidepanel__task_panel"); -		if (mTaskPanel) -		{ -			LLButton* back_btn = mTaskPanel->getChild<LLButton>("back_btn"); -			back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this)); -		} +		//LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this));  	}  	// Received items inbox setup @@ -220,38 +184,55 @@ 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;  		// Restore the collapsed inbox panel state -		LLLayoutPanel* inbox_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME); -		inv_stack->collapsePanel(inbox_panel, is_inbox_collapsed); -		if (!is_inbox_collapsed) -		{ -			inbox_panel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); -		} - -		// Set the inbox visible based on debug settings (final setting comes from http request below) -		enableInbox(gSavedSettings.getBOOL("InventoryDisplayInbox")); - -		// 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)); +        mInboxLayoutPanel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME); +        inv_stack->collapsePanel(mInboxLayoutPanel, is_inbox_collapsed); +        if (!is_inbox_collapsed) +        { +            mInboxLayoutPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); +        } + +        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)); +        }  	}  	gSavedSettings.getControl("InventoryDisplayInbox")->getCommitSignal()->connect(boost::bind(&handleInventoryDisplayInboxChanged)); -	// Update the verbs buttons state. -	updateVerbs(); +    LLFloater *floater = dynamic_cast<LLFloater*>(getParent()); +    if (floater && floater->getKey().isUndefined() && !sLoginCompleted) +    { +        // Prefill inventory for primary inventory floater +        // Other floaters should fill on visibility change +        //  +        // see get_instance_num(); +        // Primary inventory floater will have undefined key +        initInventoryViews(); +    }  	return TRUE;  }  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()) @@ -336,8 +317,20 @@ void LLSidepanelInventory::enableInbox(bool enabled)  {  	mInboxEnabled = enabled; -	LLLayoutPanel * inbox_layout_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME); -	inbox_layout_panel->setVisible(enabled); +    if(!enabled || !mPanelMainInventory->isSingleFolderMode()) +    { +        toggleInbox(); +    } +} + +void LLSidepanelInventory::hideInbox() +{ +    mInboxLayoutPanel->setVisible(false); +} + +void LLSidepanelInventory::toggleInbox() +{ +    mInboxLayoutPanel->setVisible(mInboxEnabled);  }  void LLSidepanelInventory::openInbox() @@ -367,25 +360,24 @@ void LLSidepanelInventory::onInboxChanged(const LLUUID& inbox_id)  void LLSidepanelInventory::onToggleInboxBtn()  {  	LLButton* inboxButton = getChild<LLButton>(INBOX_BUTTON_NAME); -	LLLayoutPanel* inboxPanel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);  	LLLayoutStack* inv_stack = getChild<LLLayoutStack>(INVENTORY_LAYOUT_STACK_NAME);  	const bool inbox_expanded = inboxButton->getToggleState();  	// Expand/collapse the indicated panel -	inv_stack->collapsePanel(inboxPanel, !inbox_expanded); +	inv_stack->collapsePanel(mInboxLayoutPanel, !inbox_expanded);  	if (inbox_expanded)  	{ -		inboxPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); -		if (inboxPanel->isInVisibleChain()) +        mInboxLayoutPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); +		if (mInboxLayoutPanel->isInVisibleChain())  	{  		gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected());  	}  }  	else  	{ -		gSavedPerAccountSettings.setS32("InventoryInboxHeight", inboxPanel->getTargetDim()); +		gSavedPerAccountSettings.setS32("InventoryInboxHeight", mInboxLayoutPanel->getTargetDim());  	}  } @@ -409,47 +401,7 @@ void LLSidepanelInventory::onOpen(const LLSD& key)  	}  #endif -	if(key.size() == 0) -		return; - -	mItemPanel->reset(); - -	if (key.has("id")) -	{ -		mItemPanel->setItemID(key["id"].asUUID()); -		if (key.has("object")) -		{ -			mItemPanel->setObjectID(key["object"].asUUID()); -		} -		showItemInfoPanel(); -	} -	if (key.has("task")) -	{ -		if (mTaskPanel) -			mTaskPanel->setObjectSelection(LLSelectMgr::getInstance()->getSelection()); -		showTaskInfoPanel(); -	} -} - -void LLSidepanelInventory::onInfoButtonClicked() -{ -	LLInventoryItem *item = getSelectedItem(); -	if (item) -	{ -		mItemPanel->reset(); -		mItemPanel->setItemID(item->getUUID()); -		showItemInfoPanel(); -	} -} - -void LLSidepanelInventory::onShareButtonClicked() -{ -	LLAvatarActions::shareWithAvatars(this); -} - -void LLSidepanelInventory::onShopButtonClicked() -{ -	LLWeb::loadURL(gSavedSettings.getString("MarketplaceURL")); +    gAgent.showLatestFeatureNotification("inventory");  }  void LLSidepanelInventory::performActionOnSelection(const std::string &action) @@ -471,47 +423,6 @@ void LLSidepanelInventory::performActionOnSelection(const std::string &action)  	static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(mPanelMainInventory->getActivePanel()->getModel(), action);  } -void LLSidepanelInventory::onWearButtonClicked() -{ -	// Get selected items set. -	const std::set<LLUUID> selected_uuids_set = LLAvatarActions::getInventorySelectedUUIDs(); -	if (selected_uuids_set.empty()) return; // nothing selected - -	// Convert the set to a vector. -	uuid_vec_t selected_uuids_vec; -	for (std::set<LLUUID>::const_iterator it = selected_uuids_set.begin(); it != selected_uuids_set.end(); ++it) -	{ -		selected_uuids_vec.push_back(*it); -	} - -	// Wear all selected items. -	wear_multiple(selected_uuids_vec, true); -} - -void LLSidepanelInventory::onPlayButtonClicked() -{ -	const LLInventoryItem *item = getSelectedItem(); -	if (!item) -	{ -		return; -	} - -	switch(item->getInventoryType()) -	{ -	case LLInventoryType::IT_GESTURE: -		performActionOnSelection("play"); -		break; -	default: -		performActionOnSelection("open"); -		break; -	} -} - -void LLSidepanelInventory::onTeleportButtonClicked() -{ -	performActionOnSelection("teleport"); -} -  void LLSidepanelInventory::onBackButtonClicked()  {  	showInventoryPanel(); @@ -519,102 +430,17 @@ void LLSidepanelInventory::onBackButtonClicked()  void LLSidepanelInventory::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action)  { -	updateVerbs(); -} - -void LLSidepanelInventory::showItemInfoPanel() -{ -	mItemPanel->setVisible(TRUE); -	if (mTaskPanel) -		mTaskPanel->setVisible(FALSE); -	mInventoryPanel->setVisible(FALSE); -	mItemPanel->dirty(); -	mItemPanel->setIsEditing(FALSE); -} - -void LLSidepanelInventory::showTaskInfoPanel() -{ -	mItemPanel->setVisible(FALSE); -	mInventoryPanel->setVisible(FALSE); - -	if (mTaskPanel) -	{ -		mTaskPanel->setVisible(TRUE); -		mTaskPanel->dirty(); -		mTaskPanel->setIsEditing(FALSE); -	}  }  void LLSidepanelInventory::showInventoryPanel()  { -	mItemPanel->setVisible(FALSE); -	if (mTaskPanel) -		mTaskPanel->setVisible(FALSE);  	mInventoryPanel->setVisible(TRUE); -	updateVerbs();  } -void LLSidepanelInventory::updateVerbs() +void LLSidepanelInventory::initInventoryViews()  { -	mInfoBtn->setEnabled(FALSE); -	mShareBtn->setEnabled(FALSE); - -	mWearBtn->setVisible(FALSE); -	mWearBtn->setEnabled(FALSE); -	mPlayBtn->setVisible(FALSE); -	mPlayBtn->setEnabled(FALSE); -	mPlayBtn->setToolTip(std::string("")); - 	mTeleportBtn->setVisible(FALSE); - 	mTeleportBtn->setEnabled(FALSE); - 	mShopBtn->setVisible(TRUE); - -	mShareBtn->setEnabled(canShare()); - -	const LLInventoryItem *item = getSelectedItem(); -	if (!item) -		return; - -	bool is_single_selection = getSelectedCount() == 1; - -	mInfoBtn->setEnabled(is_single_selection); - -	switch(item->getInventoryType()) -	{ -		case LLInventoryType::IT_WEARABLE: -		case LLInventoryType::IT_OBJECT: -		case LLInventoryType::IT_ATTACHMENT: -			mWearBtn->setVisible(TRUE); -			mWearBtn->setEnabled(canWearSelected()); -		 	mShopBtn->setVisible(FALSE); -			break; -		case LLInventoryType::IT_SOUND: -			mPlayBtn->setVisible(TRUE); -			mPlayBtn->setEnabled(TRUE); -			mPlayBtn->setToolTip(LLTrans::getString("InventoryPlaySoundTooltip")); -			mShopBtn->setVisible(FALSE); -			break; -		case LLInventoryType::IT_GESTURE: -			mPlayBtn->setVisible(TRUE); -			mPlayBtn->setEnabled(TRUE); -			mPlayBtn->setToolTip(LLTrans::getString("InventoryPlayGestureTooltip")); -			mShopBtn->setVisible(FALSE); -			break; -		case LLInventoryType::IT_ANIMATION: -			mPlayBtn->setVisible(TRUE); -			mPlayBtn->setEnabled(TRUE); -			mPlayBtn->setEnabled(TRUE); -			mPlayBtn->setToolTip(LLTrans::getString("InventoryPlayAnimationTooltip")); -			mShopBtn->setVisible(FALSE); -			break; -		case LLInventoryType::IT_LANDMARK: -			mTeleportBtn->setVisible(TRUE); -			mTeleportBtn->setEnabled(TRUE); -		 	mShopBtn->setVisible(FALSE); -			break; -		default: -			break; -	} +    mPanelMainInventory->initInventoryViews();  }  bool LLSidepanelInventory::canShare() @@ -737,12 +563,10 @@ void LLSidepanelInventory::clearSelections(bool clearMain, bool clearInbox)  		}  	} -	if (clearInbox && mInboxEnabled && mInventoryPanelInbox.get()) +	if (clearInbox && mInboxEnabled && !mInventoryPanelInbox.isDead())  	{  		mInventoryPanelInbox.get()->getRootFolder()->clearSelection();  	} -	 -	updateVerbs();  }  std::set<LLFolderViewItem*> LLSidepanelInventory::getInboxSelectionList() diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index a3cd20a2c6..08989bb6af 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -66,9 +66,8 @@ public:  	void clearSelections(bool clearMain, bool clearInbox);      std::set<LLFolderViewItem*> getInboxSelectionList(); -	void showItemInfoPanel(); -	void showTaskInfoPanel();  	void showInventoryPanel(); +    void initInventoryViews();  	// checks can share selected item(s)  	bool canShare(); @@ -76,13 +75,13 @@ public:  	void onToggleInboxBtn();  	void enableInbox(bool enabled); +    void toggleInbox(); +    void hideInbox();  	void openInbox();  	bool isInboxEnabled() const { return mInboxEnabled; } -	void updateVerbs(); -  	static void cleanup();  protected: @@ -103,27 +102,14 @@ protected:  private:  	LLPanel*					mInventoryPanel; // Main inventory view  	LLHandle<LLInventoryPanel>	mInventoryPanelInbox; -	LLSidepanelItemInfo*		mItemPanel; // Individual item view -	LLSidepanelTaskInfo*		mTaskPanel; // Individual in-world object view  	LLPanelMainInventory*		mPanelMainInventory; +    LLLayoutPanel* mInboxLayoutPanel; +  protected: -	void 						onInfoButtonClicked(); -	void 						onShareButtonClicked(); -	void 						onShopButtonClicked(); -	void 						onWearButtonClicked(); -	void 						onPlayButtonClicked(); -	void 						onTeleportButtonClicked();  	void 						onBackButtonClicked();  private: -	LLButton*					mInfoBtn; -	LLButton*					mShareBtn; -	LLButton*					mWearBtn; -	LLButton*					mPlayBtn; -	LLButton*					mTeleportBtn; -	LLButton*					mShopBtn; -  	bool						mInboxEnabled;  	LLInventoryCategoriesObserver* 	mCategoriesObserver; diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index b23e24a222..d6d5a4ef2d 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -31,16 +31,23 @@  #include "llagent.h"  #include "llavataractions.h" +#include "llavatarnamecache.h"  #include "llbutton.h" +#include "llcallbacklist.h"  #include "llcombobox.h" +#include "llfloater.h"  #include "llfloaterreg.h"  #include "llgroupactions.h" +#include "llgroupmgr.h" +#include "lliconctrl.h"  #include "llinventorydefines.h" +#include "llinventoryicon.h"  #include "llinventorymodel.h"  #include "llinventoryobserver.h"  #include "lllineeditor.h"  #include "llradiogroup.h"  #include "llslurl.h" +#include "lltexteditor.h"  #include "llviewercontrol.h"  #include "llviewerinventory.h"  #include "llviewerobjectlist.h" @@ -73,49 +80,6 @@ private:  };  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLItemPropertiesObserver -// -// Helper class to watch for changes to the item. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLItemPropertiesObserver : public LLInventoryObserver -{ -public: -	LLItemPropertiesObserver(LLSidepanelItemInfo* floater) -		: mFloater(floater) -	{ -		gInventory.addObserver(this); -	} -	virtual ~LLItemPropertiesObserver() -	{ -		gInventory.removeObserver(this); -	} -	virtual void changed(U32 mask); -private: -	LLSidepanelItemInfo* mFloater; // Not a handle because LLSidepanelItemInfo is managing LLItemPropertiesObserver -}; - -void LLItemPropertiesObserver::changed(U32 mask) -{ -	const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs(); -	std::set<LLUUID>::const_iterator it; - -	const LLUUID& item_id = mFloater->getItemID(); - -	for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) -	{ -		// set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288) -		if (*it == item_id) -		{ -			// if there's a change we're interested in. -			if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) -			{ -				mFloater->dirty(); -			} -		} -	} -} - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLObjectInventoryObserver  //  // Helper class to watch for changes in an object inventory. @@ -158,36 +122,48 @@ static LLPanelInjector<LLSidepanelItemInfo> t_item_info("sidepanel_item_info");  // Default constructor  LLSidepanelItemInfo::LLSidepanelItemInfo(const LLPanel::Params& p) -	: LLSidepanelInventorySubpanel(p) +	: LLPanel(p)  	, mItemID(LLUUID::null)  	, mObjectInventoryObserver(NULL)  	, mUpdatePendingId(-1) +    , mIsDirty(false) /*Not ready*/ +    , mParentFloater(NULL)  { -	mPropertiesObserver = new LLItemPropertiesObserver(this); +    gInventory.addObserver(this); +    gIdleCallbacks.addFunction(&LLSidepanelItemInfo::onIdle, (void*)this);  }  // Destroys the object  LLSidepanelItemInfo::~LLSidepanelItemInfo()  { -	delete mPropertiesObserver; -	mPropertiesObserver = NULL; +    gInventory.removeObserver(this); +    gIdleCallbacks.deleteFunction(&LLSidepanelItemInfo::onIdle, (void*)this);  	stopObjectInventoryObserver(); +     +    if (mOwnerCacheConnection.connected()) +    { +        mOwnerCacheConnection.disconnect(); +    } +    if (mCreatorCacheConnection.connected()) +    { +        mCreatorCacheConnection.disconnect(); +    }  }  // virtual  BOOL LLSidepanelItemInfo::postBuild()  { -	LLSidepanelInventorySubpanel::postBuild(); - +    mChangeThumbnailBtn = getChild<LLUICtrl>("change_thumbnail_btn"); +    mItemTypeIcon = getChild<LLIconCtrl>("item_type_icon"); +    mLabelOwnerName = getChild<LLTextBox>("LabelOwnerName"); +    mLabelCreatorName = getChild<LLTextBox>("LabelCreatorName"); +      	getChild<LLLineEditor>("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);  	getChild<LLUICtrl>("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this)); -	getChild<LLLineEditor>("LabelItemDesc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe);  	getChild<LLUICtrl>("LabelItemDesc")->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this)); -	// Creator information -	getChild<LLUICtrl>("BtnCreator")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickCreator,this)); -	// owner information -	getChild<LLUICtrl>("BtnOwner")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickOwner,this)); +    // Thumnail edition +    mChangeThumbnailBtn->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onEditThumbnail, this));  	// acquired date  	// owner permissions  	// Permissions debug text @@ -226,6 +202,12 @@ void LLSidepanelItemInfo::setItemID(const LLUUID& item_id)          mItemID = item_id;          mUpdatePendingId = -1;      } +    dirty(); +} + +void LLSidepanelItemInfo::setParentFloater(LLFloater* parent) +{ +    mParentFloater = parent;  }  const LLUUID& LLSidepanelItemInfo::getObjectID() const @@ -249,12 +231,11 @@ void LLSidepanelItemInfo::onUpdateCallback(const LLUUID& item_id, S32 received_u  void LLSidepanelItemInfo::reset()  { -	LLSidepanelInventorySubpanel::reset(); -  	mObjectID = LLUUID::null;  	mItemID = LLUUID::null;  	stopObjectInventoryObserver(); +    dirty();  }  void LLSidepanelItemInfo::refresh() @@ -262,60 +243,37 @@ void LLSidepanelItemInfo::refresh()  	LLViewerInventoryItem* item = findItem();  	if(item)  	{ -		refreshFromItem(item); -		updateVerbs(); +        const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); +        bool in_trash = (item->getUUID() == trash_id) || gInventory.isObjectDescendentOf(item->getUUID(), trash_id); +        if (in_trash && mParentFloater) +        { +            // Close properties when moving to trash +            // Aren't supposed to view properties from trash +            mParentFloater->closeFloater(); +        } +        else +        { +            refreshFromItem(item); +        }  		return;  	} -	else -	{ -		if (getIsEditing()) -		{ -			setIsEditing(FALSE); -		} -	} - -	if (!getIsEditing()) -	{ -		const std::string no_item_names[]={ -			"LabelItemName", -			"LabelItemDesc", -			"LabelCreatorName", -			"LabelOwnerName" -		}; - -		for(size_t t=0; t<LL_ARRAY_SIZE(no_item_names); ++t) -		{ -			getChildView(no_item_names[t])->setEnabled(false); -		} - -		setPropertiesFieldsEnabled(false); - -		const std::string hide_names[]={ -			"BaseMaskDebug", -			"OwnerMaskDebug", -			"GroupMaskDebug", -			"EveryoneMaskDebug", -			"NextMaskDebug" -		}; -		for(size_t t=0; t<LL_ARRAY_SIZE(hide_names); ++t) -		{ -			getChildView(hide_names[t])->setVisible(false); -		} -	} -	if (!item) -	{ -		const std::string no_edit_mode_names[]={ -			"BtnCreator", -			"BtnOwner", -		}; -		for(size_t t=0; t<LL_ARRAY_SIZE(no_edit_mode_names); ++t) -		{ -			getChildView(no_edit_mode_names[t])->setEnabled(false); -		} -	} - -	updateVerbs(); +    if (mObjectID.notNull()) +    { +        LLViewerObject* object = gObjectList.findObject(mObjectID); +        if (object) +        { +            // Object exists, but object's content is not nessesary +            // loaded, so assume item exists as well +            return; +        } +    } +     +    if (mParentFloater) +    { +        // if we failed to get item, it likely no longer exists +        mParentFloater->closeFloater(); +    }  }  void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) @@ -333,7 +291,7 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)      }  	// do not enable the UI for incomplete items. -	BOOL is_complete = item->isFinished(); +	bool is_complete = item->isFinished();  	const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(item->getInventoryType());  	const BOOL is_calling_card = (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD);  	const BOOL is_settings = (item->getInventoryType() == LLInventoryType::IT_SETTINGS); @@ -385,8 +343,22 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  	getChild<LLUICtrl>("LabelItemName")->setValue(item->getName());  	getChildView("LabelItemDescTitle")->setEnabled(TRUE);  	getChildView("LabelItemDesc")->setEnabled(is_modifiable); -	getChildView("IconLocked")->setVisible(!is_modifiable);  	getChild<LLUICtrl>("LabelItemDesc")->setValue(item->getDescription()); +    getChild<LLUICtrl>("item_thumbnail")->setValue(item->getThumbnailUUID()); + +    LLUIImagePtr icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE); +    mItemTypeIcon->setImage(icon_img); +  +    // Style for creator and owner links +    LLStyle::Params style_params; +    LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); +    style_params.color = link_color; +    style_params.readonly_color = link_color; +    style_params.is_link = true; // link will be added later +    const LLFontGL* fontp = mLabelCreatorName->getFont(); +    style_params.font.name = LLFontGL::nameFromFont(fontp); +    style_params.font.size = LLFontGL::sizeFromFont(fontp); +    style_params.font.style = "UNDERLINE";  	//////////////////  	// CREATOR NAME // @@ -397,19 +369,34 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  	if (item->getCreatorUUID().notNull())  	{  		LLUUID creator_id = item->getCreatorUUID(); -		std::string name = -			LLSLURL("agent", creator_id, "completename").getSLURLString(); -		getChildView("BtnCreator")->setEnabled(TRUE); +		std::string slurl = +			LLSLURL("agent", creator_id, "inspect").getSLURLString(); + +        style_params.link_href = slurl; +         +        LLAvatarName av_name; +        if (LLAvatarNameCache::get(creator_id, &av_name)) +        { +            updateCreatorName(creator_id, av_name, style_params); +        } +        else +        { +            if (mCreatorCacheConnection.connected()) +            { +                mCreatorCacheConnection.disconnect(); +            } +            mLabelCreatorName->setText(LLTrans::getString("None")); +            mCreatorCacheConnection = LLAvatarNameCache::get(creator_id, boost::bind(&LLSidepanelItemInfo::updateCreatorName, this, _1, _2, style_params)); +        } +          		getChildView("LabelCreatorTitle")->setEnabled(TRUE); -		getChildView("LabelCreatorName")->setEnabled(FALSE); -		getChild<LLUICtrl>("LabelCreatorName")->setValue(name); +        mLabelCreatorName->setEnabled(TRUE);  	}  	else  	{ -		getChildView("BtnCreator")->setEnabled(FALSE);  		getChildView("LabelCreatorTitle")->setEnabled(FALSE); -		getChildView("LabelCreatorName")->setEnabled(FALSE); -		getChild<LLUICtrl>("LabelCreatorName")->setValue(getString("unknown_multiple")); +        mLabelCreatorName->setEnabled(FALSE); +        mLabelCreatorName->setValue(getString("unknown_multiple"));  	}  	//////////////// @@ -417,28 +404,60 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  	////////////////  	if(perm.isOwned())  	{ -		std::string name; +        std::string slurl;  		if (perm.isGroupOwned())  		{ -			gCacheName->getGroupName(perm.getGroup(), name); +            LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(perm.getGroup()); +             +            slurl = LLSLURL("group", perm.getGroup(), "inspect").getSLURLString(); +            style_params.link_href = slurl; +            if (group_data && group_data->isGroupPropertiesDataComplete()) +            { +                mLabelOwnerName->setText(group_data->mName, style_params); +            } +            else +            { +                // Triggers refresh +                LLGroupMgr::getInstance()->sendGroupPropertiesRequest(perm.getGroup()); +                 +                std::string name; +                gCacheName->getGroupName(perm.getGroup(), name); +                mLabelOwnerName->setText(name, style_params); +            }  		}  		else  		{  			LLUUID owner_id = perm.getOwner(); -			name = LLSLURL("agent", owner_id, "completename").getSLURLString(); +            slurl = LLSLURL("agent", owner_id, "inspect").getSLURLString(); +             +            style_params.link_href = slurl; +            LLAvatarName av_name; +            if (LLAvatarNameCache::get(owner_id, &av_name)) +            { +                updateOwnerName(owner_id, av_name, style_params); +            } +            else +            { +                if (mOwnerCacheConnection.connected()) +                { +                    mOwnerCacheConnection.disconnect(); +                } +                mLabelOwnerName->setText(LLTrans::getString("None")); +                mOwnerCacheConnection = LLAvatarNameCache::get(owner_id, boost::bind(&LLSidepanelItemInfo::updateOwnerName, this, _1, _2, style_params)); +            }  		} -		getChildView("BtnOwner")->setEnabled(TRUE);  		getChildView("LabelOwnerTitle")->setEnabled(TRUE); -		getChildView("LabelOwnerName")->setEnabled(FALSE); -		getChild<LLUICtrl>("LabelOwnerName")->setValue(name); +        mLabelOwnerName->setEnabled(TRUE);  	}  	else  	{ -		getChildView("BtnOwner")->setEnabled(FALSE);  		getChildView("LabelOwnerTitle")->setEnabled(FALSE); -		getChildView("LabelOwnerName")->setEnabled(FALSE); -		getChild<LLUICtrl>("LabelOwnerName")->setValue(getString("public")); +        mLabelOwnerName->setEnabled(FALSE); +        mLabelOwnerName->setValue(getString("public"));  	} + +    // Not yet supported for task inventories +    mChangeThumbnailBtn->setEnabled(mObjectID.isNull() && ALEXANDRIA_LINDEN_ID != perm.getOwner());  	////////////  	// ORIGIN // @@ -548,6 +567,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  	if( gSavedSettings.getBOOL("DebugPermissions") )  	{ +        childSetVisible("layout_debug_permissions", true); +          		BOOL slam_perm 			= FALSE;  		BOOL overwrite_group	= FALSE;  		BOOL overwrite_everyone	= FALSE; @@ -565,38 +586,29 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  		perm_string = "B: ";  		perm_string += mask_to_string(base_mask);  		getChild<LLUICtrl>("BaseMaskDebug")->setValue(perm_string); -		getChildView("BaseMaskDebug")->setVisible(TRUE);  		perm_string = "O: ";  		perm_string += mask_to_string(owner_mask);  		getChild<LLUICtrl>("OwnerMaskDebug")->setValue(perm_string); -		getChildView("OwnerMaskDebug")->setVisible(TRUE);  		perm_string = "G";  		perm_string += overwrite_group ? "*: " : ": ";  		perm_string += mask_to_string(group_mask);  		getChild<LLUICtrl>("GroupMaskDebug")->setValue(perm_string); -		getChildView("GroupMaskDebug")->setVisible(TRUE);  		perm_string = "E";  		perm_string += overwrite_everyone ? "*: " : ": ";  		perm_string += mask_to_string(everyone_mask);  		getChild<LLUICtrl>("EveryoneMaskDebug")->setValue(perm_string); -		getChildView("EveryoneMaskDebug")->setVisible(TRUE);  		perm_string = "N";  		perm_string += slam_perm ? "*: " : ": ";  		perm_string += mask_to_string(next_owner_mask);  		getChild<LLUICtrl>("NextMaskDebug")->setValue(perm_string); -		getChildView("NextMaskDebug")->setVisible(TRUE);  	}  	else  	{ -		getChildView("BaseMaskDebug")->setVisible(FALSE); -		getChildView("OwnerMaskDebug")->setVisible(FALSE); -		getChildView("GroupMaskDebug")->setVisible(FALSE); -		getChildView("EveryoneMaskDebug")->setVisible(FALSE); -		getChildView("NextMaskDebug")->setVisible(FALSE); +        childSetVisible("layout_debug_permissions", false);  	}  	///////////// @@ -731,6 +743,68 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item)  	}  } +void LLSidepanelItemInfo::updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params) +{ +    if (mCreatorCacheConnection.connected()) +    { +        mCreatorCacheConnection.disconnect(); +    } +    std::string name = creator_name.getCompleteName(); +    mLabelCreatorName->setText(name, style_params); +} + +void LLSidepanelItemInfo::updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params) +{ +    if (mOwnerCacheConnection.connected()) +    { +        mOwnerCacheConnection.disconnect(); +    } +    std::string name = owner_name.getCompleteName(); +    mLabelOwnerName->setText(name, style_params); +} + +void LLSidepanelItemInfo::changed(U32 mask) +{ +    const LLUUID& item_id = getItemID(); +    if (getObjectID().notNull() || item_id.isNull()) +    { +        // Task inventory or not set up yet +        return; +    } +     +    const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs(); +    std::set<LLUUID>::const_iterator it; + +    for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) +    { +        // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288) +        if (*it == item_id) +        { +            // if there's a change we're interested in. +            if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) +            { +                dirty(); +            } +        } +    } +} + +void LLSidepanelItemInfo::dirty() +{ +    mIsDirty = true; +} + +// static +void LLSidepanelItemInfo::onIdle( void* user_data ) +{ +    LLSidepanelItemInfo* self = reinterpret_cast<LLSidepanelItemInfo*>(user_data); + +    if( self->mIsDirty ) +    { +        self->refresh(); +        self->mIsDirty = false; +    } +}  void LLSidepanelItemInfo::setAssociatedExperience( LLHandle<LLSidepanelItemInfo> hInfo, const LLSD& experience )  { @@ -853,7 +927,7 @@ void LLSidepanelItemInfo::onCommitDescription()  	LLViewerInventoryItem* item = findItem();  	if(!item) return; -	LLLineEditor* labelItemDesc = getChild<LLLineEditor>("LabelItemDesc"); +    LLTextEditor* labelItemDesc = getChild<LLTextEditor>("LabelItemDesc");  	if(!labelItemDesc)  	{  		return; @@ -966,7 +1040,14 @@ void LLSidepanelItemInfo::updatePermissions()  	}  } -// static +void LLSidepanelItemInfo::onEditThumbnail() +{ +    LLSD data; +    data["task_id"] = mObjectID; +    data["item_id"] = mItemID; +    LLFloaterReg::showInstance("change_item_thumbnail", data); +} +  void LLSidepanelItemInfo::onCommitSaleInfo(LLUICtrl* ctrl)  {      if (ctrl) diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h index 5f29254182..b916f44520 100644 --- a/indra/newview/llsidepaneliteminfo.h +++ b/indra/newview/llsidepaneliteminfo.h @@ -27,42 +27,55 @@  #ifndef LL_LLSIDEPANELITEMINFO_H  #define LL_LLSIDEPANELITEMINFO_H -#include "llsidepanelinventorysubpanel.h" +#include "llinventoryobserver.h" +#include "llpanel.h" +#include "llstyle.h"  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  // Class LLSidepanelItemInfo  // Object properties for inventory side panel.  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLAvatarName;  class LLButton; +class LLFloater; +class LLIconCtrl;  class LLViewerInventoryItem;  class LLItemPropertiesObserver;  class LLObjectInventoryObserver;  class LLViewerObject;  class LLPermissions; +class LLTextBox; -class LLSidepanelItemInfo : public LLSidepanelInventorySubpanel +class LLSidepanelItemInfo : public LLPanel, public LLInventoryObserver  {  public:  	LLSidepanelItemInfo(const LLPanel::Params& p = getDefaultParams());  	virtual ~LLSidepanelItemInfo(); -	/*virtual*/ BOOL postBuild(); +	/*virtual*/ BOOL postBuild() override;  	/*virtual*/ void reset();  	void setObjectID(const LLUUID& object_id);  	void setItemID(const LLUUID& item_id); -	void setEditMode(BOOL edit); +    void setParentFloater(LLFloater* parent); // For simplicity  	const LLUUID& getObjectID() const;  	const LLUUID& getItemID() const;  	// if received update and item id (from callback) matches internal ones, update UI  	void onUpdateCallback(const LLUUID& item_id, S32 received_update_id); +     +    void changed(U32 mask) override; +    void dirty(); +     +    static void onIdle( void* user_data ); +    void updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params); +    void updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params);  protected: -	/*virtual*/ void refresh(); -	/*virtual*/ void save(); +	void refresh() override; +	void save();  	LLViewerInventoryItem* findItem() const;  	LLViewerObject*  findObject() const; @@ -75,14 +88,23 @@ private:  	void startObjectInventoryObserver();  	void stopObjectInventoryObserver();  	void setPropertiesFieldsEnabled(bool enabled); +     +    boost::signals2::connection mOwnerCacheConnection; +    boost::signals2::connection mCreatorCacheConnection;  	LLUUID mItemID; 	// inventory UUID for the inventory item.  	LLUUID mObjectID; 	// in-world task UUID, or null if in agent inventory. -	LLItemPropertiesObserver* mPropertiesObserver; // for syncing changes to item  	LLObjectInventoryObserver* mObjectInventoryObserver; // for syncing changes to items inside an object  	// We can send multiple properties updates simultaneously, make sure only last response counts and there won't be a race condition.  	S32 mUpdatePendingId; +    bool mIsDirty;         // item properties need to be updated +    LLFloater* mParentFloater; + +    LLUICtrl* mChangeThumbnailBtn; +    LLIconCtrl* mItemTypeIcon; +    LLTextBox* mLabelOwnerName; +    LLTextBox* mLabelCreatorName;  	//  	// UI Elements @@ -94,6 +116,7 @@ protected:  	void 						onCommitDescription();  	void 						onCommitPermissions(LLUICtrl* ctrl);  	void 						updatePermissions(); +    void 						onEditThumbnail();  	void 						onCommitSaleInfo(LLUICtrl* ctrl);  	void 						updateSaleInfo();  	void 						onCommitChanges(LLPointer<LLViewerInventoryItem> item); diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index 225751ab92..1d6b3cd80c 100644 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -42,6 +42,7 @@  #include "llresmgr.h"  #include "lltextbox.h"  #include "llbutton.h" +#include "llcallbacklist.h"  #include "llcheckboxctrl.h"  #include "llviewerobject.h"  #include "llselectmgr.h" @@ -75,9 +76,11 @@ static LLPanelInjector<LLSidepanelTaskInfo> t_task_info("sidepanel_task_info");  // Default constructor  LLSidepanelTaskInfo::LLSidepanelTaskInfo() +    : mVisibleDebugPermissions(true) // space was allocated by default  {  	setMouseOpaque(FALSE);  	LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLSidepanelTaskInfo::refreshAll, this)); +    gIdleCallbacks.addFunction(&LLSidepanelTaskInfo::onIdle, (void*)this);  } @@ -85,13 +88,12 @@ LLSidepanelTaskInfo::~LLSidepanelTaskInfo()  {  	if (sActivePanel == this)  		sActivePanel = NULL; +    gIdleCallbacks.deleteFunction(&LLSidepanelTaskInfo::onIdle, (void*)this);  }  // virtual  BOOL LLSidepanelTaskInfo::postBuild()  { -	LLSidepanelInventorySubpanel::postBuild(); -  	mOpenBtn = getChild<LLButton>("open_btn");  	mOpenBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onOpenButtonClicked, this));  	mPayBtn = getChild<LLButton>("pay_btn"); @@ -146,12 +148,12 @@ BOOL LLSidepanelTaskInfo::postBuild()  	mDALabelClickAction = getChildView("label click action");  	mDAComboClickAction = getChild<LLComboBox>("clickaction");  	mDAPathfindingAttributes = getChild<LLTextBase>("pathfinding_attributes_value"); -	mDAB = getChildView("B:"); -	mDAO = getChildView("O:"); -	mDAG = getChildView("G:"); -	mDAE = getChildView("E:"); -	mDAN = getChildView("N:"); -	mDAF = getChildView("F:"); +	mDAB = getChild<LLUICtrl>("B:"); +	mDAO = getChild<LLUICtrl>("O:"); +	mDAG = getChild<LLUICtrl>("G:"); +	mDAE = getChild<LLUICtrl>("E:"); +	mDAN = getChild<LLUICtrl>("N:"); +	mDAF = getChild<LLUICtrl>("F:");  	return TRUE;  } @@ -201,12 +203,22 @@ void LLSidepanelTaskInfo::disableAll()  	disablePermissions(); -	mDAB->setVisible(FALSE); -	mDAO->setVisible(FALSE); -	mDAG->setVisible(FALSE); -	mDAE->setVisible(FALSE); -	mDAN->setVisible(FALSE); -	mDAF->setVisible(FALSE); +    if (mVisibleDebugPermissions) +    { +        mDAB->setVisible(FALSE); +        mDAO->setVisible(FALSE); +        mDAG->setVisible(FALSE); +        mDAE->setVisible(FALSE); +        mDAN->setVisible(FALSE); +        mDAF->setVisible(FALSE); + +        LLFloater* parent_floater = gFloaterView->getParentFloater(this); +        LLRect parent_rect = parent_floater->getRect(); +        LLRect debug_rect = mDAB->getRect(); +        // use double the debug rect for padding (since it isn't trivial to extract top_pad) +        parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() - (debug_rect.getHeight() * 2)); +        mVisibleDebugPermissions = false; +    }  	mOpenBtn->setEnabled(FALSE);  	mPayBtn->setEnabled(FALSE); @@ -253,6 +265,8 @@ void LLSidepanelTaskInfo::disablePermissions()  void LLSidepanelTaskInfo::refresh()  { +    mIsDirty = false; +      	LLButton* btn_deed_to_group = mDeedBtn;   	if (btn_deed_to_group)  	{	 @@ -606,23 +620,23 @@ void LLSidepanelTaskInfo::refresh()  	if (gSavedSettings.getBOOL("DebugPermissions") )  	{ -		if (valid_base_perms) -		{ -			getChild<LLUICtrl>("B:")->setValue("B: " + mask_to_string(base_mask_on)); -			getChildView("B:")->setVisible(							TRUE); -			 -			getChild<LLUICtrl>("O:")->setValue("O: " + mask_to_string(owner_mask_on)); -			getChildView("O:")->setVisible(							TRUE); -			 -			getChild<LLUICtrl>("G:")->setValue("G: " + mask_to_string(group_mask_on)); -			getChildView("G:")->setVisible(							TRUE); -			 -			getChild<LLUICtrl>("E:")->setValue("E: " + mask_to_string(everyone_mask_on)); -			getChildView("E:")->setVisible(							TRUE); -			 -			getChild<LLUICtrl>("N:")->setValue("N: " + mask_to_string(next_owner_mask_on)); -			getChildView("N:")->setVisible(							TRUE); -		} +        if (valid_base_perms) +        { +            mDAB->setValue("B: " + mask_to_string(base_mask_on)); +            mDAB->setVisible(							TRUE); + +            mDAO->setValue("O: " + mask_to_string(owner_mask_on)); +            mDAO->setVisible(							TRUE); + +            mDAG->setValue("G: " + mask_to_string(group_mask_on)); +            mDAG->setVisible(							TRUE); + +            mDAE->setValue("E: " + mask_to_string(everyone_mask_on)); +            mDAE->setVisible(							TRUE); + +            mDAN->setValue("N: " + mask_to_string(next_owner_mask_on)); +            mDAN->setVisible(							TRUE); +        }  		U32 flag_mask = 0x0;  		if (objectp->permMove()) 		flag_mask |= PERM_MOVE; @@ -630,18 +644,35 @@ void LLSidepanelTaskInfo::refresh()  		if (objectp->permCopy()) 		flag_mask |= PERM_COPY;  		if (objectp->permTransfer()) 	flag_mask |= PERM_TRANSFER; -		getChild<LLUICtrl>("F:")->setValue("F:" + mask_to_string(flag_mask)); -		getChildView("F:")->setVisible(								TRUE); -	} -	else -	{ -		getChildView("B:")->setVisible(								FALSE); -		getChildView("O:")->setVisible(								FALSE); -		getChildView("G:")->setVisible(								FALSE); -		getChildView("E:")->setVisible(								FALSE); -		getChildView("N:")->setVisible(								FALSE); -		getChildView("F:")->setVisible(								FALSE); -	} +        mDAF->setValue("F:" + mask_to_string(flag_mask)); +        mDAF->setVisible(TRUE); + +        if (!mVisibleDebugPermissions) +        { +            LLFloater* parent_floater = gFloaterView->getParentFloater(this); +            LLRect parent_rect = parent_floater->getRect(); +            LLRect debug_rect = mDAB->getRect(); +            // use double the debug rect for padding (since it isn't trivial to extract top_pad) +            parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() + (debug_rect.getHeight() * 2)); +            mVisibleDebugPermissions = true; +        } +    } +    else if (mVisibleDebugPermissions) +    { +        mDAB->setVisible(FALSE); +        mDAO->setVisible(FALSE); +        mDAG->setVisible(FALSE); +        mDAE->setVisible(FALSE); +        mDAN->setVisible(FALSE); +        mDAF->setVisible(FALSE); + +        LLFloater* parent_floater = gFloaterView->getParentFloater(this); +        LLRect parent_rect = parent_floater->getRect(); +        LLRect debug_rect = mDAB->getRect(); +        // use double the debug rect for padding (since it isn't trivial to extract top_pad) +        parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() - (debug_rect.getHeight() * 2)); +        mVisibleDebugPermissions = false; +    }  	BOOL has_change_perm_ability = FALSE;  	BOOL has_change_sale_ability = FALSE; @@ -864,33 +895,6 @@ void LLSidepanelTaskInfo::refresh()  	getChildView("label click action")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume);  	getChildView("clickaction")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume); -	if (!getIsEditing()) -	{ -		const std::string no_item_names[] =  -			{ -				"Object Name", -				"Object Description", -				"button set group", -				"checkbox share with group", -				"button deed", -				"checkbox allow everyone move", -				"checkbox allow everyone copy", -				"checkbox for sale", -				"sale type", -				"Edit Cost", -				"checkbox next owner can modify", -				"checkbox next owner can copy", -				"checkbox next owner can transfer", -				"clickaction", -				"search_check", -				"perm_modify", -				"Group Name", -			}; -		for (size_t t=0; t<LL_ARRAY_SIZE(no_item_names); ++t) -		{ -			getChildView(no_item_names[t])->setEnabled(	FALSE); -		} -	}  	updateVerbs();  } @@ -1203,16 +1207,6 @@ void LLSidepanelTaskInfo::onCommitIncludeInSearch(LLUICtrl* ctrl, void* data)  // virtual  void LLSidepanelTaskInfo::updateVerbs()  { -	LLSidepanelInventorySubpanel::updateVerbs(); - -	/* -	mOpenBtn->setVisible(!getIsEditing()); -	mPayBtn->setVisible(!getIsEditing()); -	mBuyBtn->setVisible(!getIsEditing()); -	//const LLViewerObject *obj = getFirstSelectedObject(); -	//mEditBtn->setEnabled(obj && obj->permModify()); -	*/ -  	LLSafeHandle<LLObjectSelection> object_selection = LLSelectMgr::getInstance()->getSelection();  	const BOOL any_selected = (object_selection->getNumNodes() > 0); @@ -1297,6 +1291,23 @@ LLSidepanelTaskInfo* LLSidepanelTaskInfo::getActivePanel()  	return sActivePanel;  } +void LLSidepanelTaskInfo::dirty() +{ +    mIsDirty = true; +} + +// static +void LLSidepanelTaskInfo::onIdle( void* user_data ) +{ +    LLSidepanelTaskInfo* self = reinterpret_cast<LLSidepanelTaskInfo*>(user_data); + +    if( self->mIsDirty ) +    { +        self->refresh(); +        self->mIsDirty = false; +    } +} +  LLViewerObject* LLSidepanelTaskInfo::getObject()  {  	if (!mObject->isDead()) diff --git a/indra/newview/llsidepaneltaskinfo.h b/indra/newview/llsidepaneltaskinfo.h index dc259cb22d..852d36293b 100644 --- a/indra/newview/llsidepaneltaskinfo.h +++ b/indra/newview/llsidepaneltaskinfo.h @@ -27,8 +27,8 @@  #ifndef LL_LLSIDEPANELTASKINFO_H  #define LL_LLSIDEPANELTASKINFO_H -#include "llsidepanelinventorysubpanel.h"  #include "lluuid.h" +#include "llpanel.h"  #include "llselectmgr.h"  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -43,14 +43,14 @@ class LLNameBox;  class LLViewerObject;  class LLTextBase; -class LLSidepanelTaskInfo : public LLSidepanelInventorySubpanel +class LLSidepanelTaskInfo : public LLPanel  {  public:  	LLSidepanelTaskInfo();  	virtual ~LLSidepanelTaskInfo(); -	/*virtual*/	BOOL postBuild(); -	/*virtual*/ void onVisibilityChange ( BOOL new_visibility ); +	BOOL postBuild() override; +	void onVisibilityChange ( BOOL new_visibility ) override;  	void setObjectSelection(LLObjectSelectionHandle selection); @@ -58,10 +58,12 @@ public:  	LLViewerObject* getFirstSelectedObject();  	static LLSidepanelTaskInfo *getActivePanel(); +    void dirty(); +    static void onIdle( void* user_data );  protected: -	/*virtual*/ void refresh();	// refresh all labels as needed -	/*virtual*/ void save(); -	/*virtual*/ void updateVerbs(); +	void refresh() override;	// refresh all labels as needed +	void save(); +	void updateVerbs();  	void refreshAll(); // ignore current keyboard focus and update all fields @@ -103,6 +105,8 @@ private:  	LLUUID			mCreatorID;  	LLUUID			mOwnerID;  	LLUUID			mLastOwnerID; +     +    bool mIsDirty;  protected:  	void 						onOpenButtonClicked(); @@ -121,6 +125,10 @@ protected:  private:  	LLPointer<LLViewerObject>	mObject;  	LLObjectSelectionHandle		mObjectSelection; + +    // mVisibleDebugPermissions doesn't nessesarily matche state +    // of viewes and is primarily for floater resize +    bool                        mVisibleDebugPermissions;  	static LLSidepanelTaskInfo* sActivePanel;  private: @@ -148,12 +156,12 @@ private:  	LLView*		mDALabelClickAction;  	LLComboBox*	mDAComboClickAction;  	LLTextBase* mDAPathfindingAttributes; -	LLView*		mDAB; -	LLView*		mDAO; -	LLView*		mDAG; -	LLView*		mDAE; -	LLView*		mDAN; -	LLView*		mDAF; +    LLUICtrl*   mDAB; +    LLUICtrl*   mDAO; +    LLUICtrl*   mDAG; +    LLUICtrl*   mDAE; +    LLUICtrl*   mDAN; +    LLUICtrl*   mDAF;  }; diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index ed7e18fadc..b7a1832b17 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -51,6 +51,7 @@  #include "llviewercontrol.h"  #include "llviewermenufile.h"	// upload_new_resource()  #include "llviewerstats.h" +#include "llviewertexturelist.h"  #include "llwindow.h"  #include "llworld.h"  #include <boost/filesystem.hpp> @@ -873,6 +874,11 @@ LLPointer<LLImageRaw> LLSnapshotLivePreview::getEncodedImage()      return mPreviewImageEncoded;  } +bool LLSnapshotLivePreview::createUploadFile(const std::string &out_filename, const S32 max_image_dimentions, const S32 min_image_dimentions) +{ +    return LLViewerTextureList::createUploadFile(mPreviewImage, out_filename, max_image_dimentions, min_image_dimentions); +} +  // We actually estimate the data size so that we do not require actual compression when showing the preview  // Note : whenever formatted image is computed, mDataSize will be updated to reflect the true size  void LLSnapshotLivePreview::estimateDataSize() diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h index 1f81307976..6e38a957b4 100644 --- a/indra/newview/llsnapshotlivepreview.h +++ b/indra/newview/llsnapshotlivepreview.h @@ -106,6 +106,7 @@ public:  	LLPointer<LLImageFormatted>	getFormattedImage();  	LLPointer<LLImageRaw>		getEncodedImage(); +    bool createUploadFile(const std::string &out_file, const S32 max_image_dimentions, const S32 min_image_dimentions);  	/// Sets size of preview thumbnail image and the surrounding rect.  	void setThumbnailPlaceholderRect(const LLRect& rect) {mThumbnailPlaceholderRect = rect; } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 1b0a814c45..ad87fca25b 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -256,7 +256,7 @@ static bool mLoginStatePastUI = false;  static bool mBenefitsSuccessfullyInit = false;  const F32 STATE_AGENT_WAIT_TIMEOUT = 240; //seconds -const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN = 3; // Give region 3 chances +const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_ABORT = 4; // Give region 4 chances  std::unique_ptr<LLEventPump> LLStartUp::sStateWatcher(new LLEventStream("StartupState"));  std::unique_ptr<LLStartupListener> LLStartUp::sListener(new LLStartupListener()); @@ -1412,11 +1412,18 @@ bool idle_startup()  		else  		{  			U32 num_retries = regionp->getNumSeedCapRetries(); -            if (num_retries > MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN) +            if (num_retries > MAX_SEED_CAP_ATTEMPTS_BEFORE_ABORT)              { -                // Region will keep trying to get capabilities, -                // but for now continue as if caps were granted -                LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); +                LL_WARNS("AppInit") << "Failed to get capabilities. Backing up to login screen!" << LL_ENDL; +                if (gRememberPassword) +                { +                    LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); +                } +                else +                { +                    LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status); +                } +                reset_login();              }  			else if (num_retries > 0)  			{ @@ -1915,6 +1922,34 @@ bool idle_startup()          LLInventoryModelBackgroundFetch::instance().start();  		gInventory.createCommonSystemCategories(); +        LLStartUp::setStartupState(STATE_INVENTORY_CALLBACKS ); +        display_startup(); + +        return FALSE; +    } + +    //--------------------------------------------------------------------- +    // STATE_INVENTORY_CALLBACKS  +    //--------------------------------------------------------------------- +    if (STATE_INVENTORY_CALLBACKS  == LLStartUp::getStartupState()) +    { +        if (!LLInventoryModel::isSysFoldersReady()) +        { +            display_startup(); +            return FALSE; +        } +        LLInventoryModelBackgroundFetch::instance().start(); +        LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); +        LLViewerInventoryCategory* cof = gInventory.getCategory(cof_id); +        if (cof +            && cof->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) +        { +            // Special case, dupplicate request prevention. +            // Cof folder will be requested via FetchCOF +            // in appearance manager, prevent recursive fetch +            cof->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE); +        } +  		// It's debatable whether this flag is a good idea - sets all  		// bits, and in general it isn't true that inventory @@ -2176,7 +2211,7 @@ bool idle_startup()  			gAgentWearables.notifyLoadingStarted();  			gAgent.setOutfitChosen(TRUE);  			gAgentWearables.sendDummyAgentWearablesUpdate(); -			callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), set_flags_and_update_appearance); +            callAfterCOFFetch(set_flags_and_update_appearance);  		}  		display_startup(); @@ -2787,7 +2822,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") @@ -2840,8 +2875,15 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name,  		bool do_append = false;  		LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);  		// Need to fetch cof contents before we can wear. -		callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), +        if (do_copy) +        { +            callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(),  							   boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append)); +        } +        else +        { +            callAfterCategoryLinksFetch(cat_id, boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append)); +        }  		LL_DEBUGS() << "initial outfit category id: " << cat_id << LL_ENDL;  	} @@ -2894,6 +2936,7 @@ std::string LLStartUp::startupStateToString(EStartupState state)  		RTNENUM( STATE_AGENT_SEND );  		RTNENUM( STATE_AGENT_WAIT );  		RTNENUM( STATE_INVENTORY_SEND ); +        RTNENUM(STATE_INVENTORY_CALLBACKS );  		RTNENUM( STATE_MISC );  		RTNENUM( STATE_PRECACHE );  		RTNENUM( STATE_WEARABLES_WAIT ); @@ -2948,6 +2991,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<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(""); @@ -3621,7 +3669,7 @@ bool process_login_success_response()  		std::string flag = login_flags["ever_logged_in"];  		if(!flag.empty())  		{ -			gAgent.setFirstLogin((flag == "N") ? TRUE : FALSE); +			gAgent.setFirstLogin(flag == "N");  		}  		/*  Flag is currently ignored by the viewer. @@ -3712,7 +3760,7 @@ bool process_login_success_response()  	std::string fake_initial_outfit_name = gSavedSettings.getString("FakeInitialOutfitName");  	if (!fake_initial_outfit_name.empty())  	{ -		gAgent.setFirstLogin(TRUE); +		gAgent.setFirstLogin(true);  		sInitialOutfit = fake_initial_outfit_name;  		if (sInitialOutfitGender.empty())  		{ @@ -3747,7 +3795,9 @@ bool process_login_success_response()  	// Only save mfa_hash for future logins if the user wants their info remembered. -	if(response.has("mfa_hash") && gSavedSettings.getBOOL("RememberUser") && gSavedSettings.getBOOL("RememberPassword")) +	if(response.has("mfa_hash") +       && gSavedSettings.getBOOL("RememberUser") +       && LLLoginInstance::getInstance()->saveMFA())  	{  		std::string grid(LLGridManager::getInstance()->getGridId());  		std::string user_id(gUserCredential->userID()); @@ -3755,6 +3805,13 @@ bool process_login_success_response()  		// TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically  		gSecAPIHandler->syncProtectedMap();  	} +    else if (!LLLoginInstance::getInstance()->saveMFA()) +    { +        std::string grid(LLGridManager::getInstance()->getGridId()); +        std::string user_id(gUserCredential->userID()); +        gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id); +        gSecAPIHandler->syncProtectedMap(); +    }  	bool success = false;  	// JC: gesture loading done below, when we have an asset system diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index fe8e215f76..921f088423 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -71,6 +71,7 @@ typedef enum {  	STATE_AGENT_SEND,				// Connect to a region  	STATE_AGENT_WAIT,				// Wait for region  	STATE_INVENTORY_SEND,			// Do inventory transfer +	STATE_INVENTORY_CALLBACKS,		// Wait for missing system folders and register callbacks  	STATE_MISC,						// Do more things (set bandwidth, start audio, save location, etc)  	STATE_PRECACHE,					// Wait a bit for textures to download  	STATE_WEARABLES_WAIT,			// Wait for clothing to download diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 0ce82a1297..2e137a8e12 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -176,6 +176,9 @@ LLFloaterTexturePicker::LLFloaterTexturePicker(  	mSelectedItemPinned( FALSE ),  	mCanApply(true),  	mCanPreview(true), +    mLimitsSet(false), +    mMaxDim(S32_MAX), +    mMinDim(0),  	mPreviewSettingChanged(false),  	mOnFloaterCommitCallback(NULL),  	mOnFloaterCloseCallback(NULL), @@ -270,19 +273,37 @@ void LLFloaterTexturePicker::stopUsingPipette()  	}  } -void LLFloaterTexturePicker::updateImageStats() +bool LLFloaterTexturePicker::updateImageStats()  { +    bool result = true;  	if (mTexturep.notNull())  	{  		//RN: have we received header data for this image? -		if (mTexturep->getFullWidth() > 0 && mTexturep->getFullHeight() > 0) +        S32 width = mTexturep->getFullWidth(); +        S32 height = mTexturep->getFullHeight(); +		if (width > 0 && height > 0)  		{ -			std::string formatted_dims = llformat("%d x %d", mTexturep->getFullWidth(),mTexturep->getFullHeight()); -			mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims); -			if (mOnUpdateImageStatsCallback) -			{ -				mOnUpdateImageStatsCallback(mTexturep); -			} +            if ((mLimitsSet && (width != height)) +                || width < mMinDim +                || width > mMaxDim +                || height < mMinDim +                || height > mMaxDim +                ) +            { +                std::string formatted_dims = llformat("%dx%d", width, height); +                mResolutionWarning->setTextArg("[TEXDIM]", formatted_dims); +                result = false; +            } +            else +            { +                std::string formatted_dims = llformat("%d x %d", width, height); +                mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims); +            } + +            if (mOnUpdateImageStatsCallback) +            { +                mOnUpdateImageStatsCallback(mTexturep); +            }  		}  		else  		{ @@ -293,6 +314,21 @@ void LLFloaterTexturePicker::updateImageStats()  	{  		mResolutionLabel->setTextArg("[DIMENSIONS]", std::string(""));  	} +    mResolutionLabel->setVisible(result); +    mResolutionWarning->setVisible(!result); + +    // Hide buttons and pipete to make space for mResolutionWarning +    // Hiding buttons is suboptimal, but at the moment limited to inventory thumbnails +    // may be this should be an info/warning icon with a tooltip? +    S32 index = mModeSelector->getValue().asInteger(); +    if (index == 0) +    { +        mDefaultBtn->setVisible(result); +        mNoneBtn->setVisible(result); +        mBlankBtn->setVisible(result); +        mPipetteBtn->setVisible(result); +    } +    return result;  }  // virtual @@ -410,11 +446,22 @@ BOOL LLFloaterTexturePicker::postBuild()  	mTentativeLabel = getChild<LLTextBox>("Multiple");  	mResolutionLabel = getChild<LLTextBox>("size_lbl"); +    mResolutionWarning = getChild<LLTextBox>("over_limit_lbl"); -	childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this); -	childSetAction("None", LLFloaterTexturePicker::onBtnNone,this); -	childSetAction("Blank", LLFloaterTexturePicker::onBtnBlank,this); +    mDefaultBtn = getChild<LLButton>("Default"); +    mNoneBtn = getChild<LLButton>("None"); +    mBlankBtn = getChild<LLButton>("Blank"); +    mPipetteBtn = getChild<LLButton>("Pipette"); +    mSelectBtn = getChild<LLButton>("Select"); +    mCancelBtn = getChild<LLButton>("Cancel"); + +    mDefaultBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSetToDefault,this)); +    mNoneBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnNone, this)); +    mBlankBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnBlank, this)); +    mPipetteBtn->setCommitCallback(boost::bind(&LLFloaterTexturePicker::onBtnPipette, this)); +    mSelectBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSelect, this)); +    mCancelBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnCancel, this));  	childSetCommitCallback("show_folders_check", onShowFolders, this); @@ -481,10 +528,6 @@ BOOL LLFloaterTexturePicker::postBuild()  		getChildView("show_folders_check")->setEnabled(FALSE);  	} -	getChild<LLUICtrl>("Pipette")->setCommitCallback( boost::bind(&LLFloaterTexturePicker::onBtnPipette, this)); -	childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this); -	childSetAction("Select", LLFloaterTexturePicker::onBtnSelect,this); -  	// update permission filter once UI is fully initialized  	updateFilterPermMask();  	mSavedFolderState.setApply(FALSE); @@ -504,13 +547,13 @@ void LLFloaterTexturePicker::draw()      static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);      drawConeToOwner(mContextConeOpacity, max_opacity, mOwner); -	updateImageStats(); +	bool valid_dims = updateImageStats();  	// if we're inactive, gray out "apply immediate" checkbox  	getChildView("show_folders_check")->setEnabled(mActive && mCanApplyImmediately && !mNoCopyTextureSelected); -	getChildView("Select")->setEnabled(mActive && mCanApply); -	getChildView("Pipette")->setEnabled(mActive); -	getChild<LLUICtrl>("Pipette")->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); +	mSelectBtn->setEnabled(mActive && mCanApply && valid_dims); +	mPipetteBtn->setEnabled(mActive); +    mPipetteBtn->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance());  	//BOOL allow_copy = FALSE;  	if( mOwner )  @@ -544,9 +587,9 @@ void LLFloaterTexturePicker::draw()  			mTentativeLabel->setVisible( FALSE  );  		} -		getChildView("Default")->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative); -		getChildView("Blank")->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative); -		getChildView("None")->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative)); +		mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative); +		mBlankBtn->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative); +		mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative));  		LLFloater::draw(); @@ -672,17 +715,76 @@ PermissionMask LLFloaterTexturePicker::getFilterPermMask()  void LLFloaterTexturePicker::commitIfImmediateSet()  { -	if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply) +	if (!mNoCopyTextureSelected && mCanApply)  	{ -		mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, LLUUID::null); +        commitCallback(LLTextureCtrl::TEXTURE_CHANGE);  	}  } +void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op) +{ +    if (!mOnFloaterCommitCallback) +    { +        return; +    } +    LLUUID asset_id = mImageAssetID; +    LLUUID inventory_id; +    LLPickerSource mode = (LLPickerSource)mModeSelector->getValue().asInteger(); + +    switch (mode) +    { +        case PICKER_INVENTORY: +            { +                LLFolderView* root_folder = mInventoryPanel->getRootFolder(); +                if (root_folder && root_folder->getCurSelectedItem()) +                { +                    LLFolderViewItem* last_selected = root_folder->getCurSelectedItem(); +                    LLFolderViewModelItemInventory* inv_view = static_cast<LLFolderViewModelItemInventory*>(last_selected->getViewModelItem()); + +                    LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID()); +                    if (itemp && itemp->getAssetUUID() == mImageAssetID) +                    { +                        inventory_id = inv_view->getUUID(); +                    } +                    else +                    { +                        mode = PICKER_UNKNOWN; // source of id unknown +                    } +                } +                else +                { +                    mode = PICKER_UNKNOWN; // source of id unknown +                } +                break; +            } +        case PICKER_LOCAL: +            { +                if (!mLocalScrollCtrl->getAllSelected().empty()) +                { +                    LLUUID temp_id = mLocalScrollCtrl->getFirstSelected()->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID(); +                    asset_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id); +                } +                else +                { +                    asset_id = mImageAssetID; +                    mode = PICKER_UNKNOWN; // source of id unknown +                } +                break; +            } +        case PICKER_BAKE: +            break; +        default: +            mode = PICKER_UNKNOWN; // source of id unknown +            break; +    } + +    mOnFloaterCommitCallback(op, mode, asset_id, inventory_id); +}  void LLFloaterTexturePicker::commitCancel()  {  	if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply)  	{ -		mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, LLUUID::null); +		mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, mOriginalImageAssetID, LLUUID::null);  	}  } @@ -735,7 +837,7 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata)  	self->setImageID( self->mOriginalImageAssetID );  	if (self->mOnFloaterCommitCallback)  	{ -		self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, LLUUID::null); +		self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, self->mOriginalImageAssetID, LLUUID::null);  	}  	self->mViewModel->resetDirty();  	self->closeFloater(); @@ -745,20 +847,7 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata)  void LLFloaterTexturePicker::onBtnSelect(void* userdata)  {  	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; -	LLUUID local_id = LLUUID::null; -	if (self->mOwner) -	{ -		if (self->mLocalScrollCtrl->getVisible() && !self->mLocalScrollCtrl->getAllSelected().empty()) -		{ -			LLUUID temp_id = self->mLocalScrollCtrl->getFirstSelected()->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID(); -			local_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id); -		} -	} -	 -	if (self->mOnFloaterCommitCallback) -	{ -		self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_SELECT, local_id); -	} +	self->commitCallback(LLTextureCtrl::TEXTURE_SELECT);  	self->closeFloater();  } @@ -820,25 +909,25 @@ void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata)  	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;      int index = self->mModeSelector->getValue().asInteger(); -	self->getChild<LLButton>("Default")->setVisible(index == 0 ? TRUE : FALSE); -	self->getChild<LLButton>("Blank")->setVisible(index == 0 ? TRUE : FALSE); -	self->getChild<LLButton>("None")->setVisible(index == 0 ? TRUE : FALSE); -	self->getChild<LLButton>("Pipette")->setVisible(index == 0 ? TRUE : FALSE); -	self->getChild<LLFilterEditor>("inventory search editor")->setVisible(index == 0 ? TRUE : FALSE); -	self->getChild<LLInventoryPanel>("inventory panel")->setVisible(index == 0 ? TRUE : FALSE); +	self->mDefaultBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); +	self->mBlankBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); +	self->mNoneBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); +	self->mPipetteBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); +	self->getChild<LLFilterEditor>("inventory search editor")->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); +	self->getChild<LLInventoryPanel>("inventory panel")->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE);  	/*self->getChild<LLCheckBox>("show_folders_check")->setVisible(mode);  	  no idea under which conditions the above is even shown, needs testing. */ -	self->getChild<LLButton>("l_add_btn")->setVisible(index == 1 ? TRUE : FALSE); -	self->getChild<LLButton>("l_rem_btn")->setVisible(index == 1 ? TRUE : FALSE); -	self->getChild<LLButton>("l_upl_btn")->setVisible(index == 1 ? TRUE : FALSE); -	self->getChild<LLScrollListCtrl>("l_name_list")->setVisible(index == 1 ? TRUE : FALSE); +	self->getChild<LLButton>("l_add_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); +	self->getChild<LLButton>("l_rem_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); +	self->getChild<LLButton>("l_upl_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); +	self->getChild<LLScrollListCtrl>("l_name_list")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); -	self->getChild<LLComboBox>("l_bake_use_texture_combo_box")->setVisible(index == 2 ? TRUE : FALSE); +	self->getChild<LLComboBox>("l_bake_use_texture_combo_box")->setVisible(index == PICKER_BAKE ? TRUE : FALSE);  	self->getChild<LLCheckBoxCtrl>("hide_base_mesh_region")->setVisible(FALSE);// index == 2 ? TRUE : FALSE); -	if (index == 2) +	if (index == PICKER_BAKE)  	{  		self->stopUsingPipette(); @@ -979,7 +1068,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)  		{  			if (self->mOnFloaterCommitCallback)  			{ -				self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, inworld_id); +				self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, PICKER_LOCAL, inworld_id, LLUUID::null);  			}  		}  	} @@ -1098,10 +1187,10 @@ void LLFloaterTexturePicker::updateFilterPermMask()  	//mInventoryPanel->setFilterPermMask( getFilterPermMask() );  Commented out due to no-copy texture loss.  } -void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply) +void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply, bool inworld_image)  { -	getChildRef<LLUICtrl>("Select").setEnabled(can_apply); -	getChildRef<LLUICtrl>("preview_disabled").setVisible(!can_preview); +	mSelectBtn->setEnabled(can_apply); +	getChildRef<LLUICtrl>("preview_disabled").setVisible(!can_preview && inworld_image);  	getChildRef<LLUICtrl>("apply_immediate_check").setVisible(can_preview);  	mCanApply = can_apply; @@ -1109,6 +1198,15 @@ void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply)  	mPreviewSettingChanged = true;  } +void LLFloaterTexturePicker::setMinDimentionsLimits(S32 min_dim) +{ +    mMinDim = min_dim; +    mLimitsSet = true; + +    std::string formatted_dims = llformat("%dx%d", mMinDim, mMinDim); +    mResolutionWarning->setTextArg("[MINTEXDIM]", formatted_dims); +} +  void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string )  {  	std::string upper_case_search_string = search_string; @@ -1405,7 +1503,7 @@ void LLTextureCtrl::showPicker(BOOL take_focus)  		}  		if (texture_floaterp)  		{ -			texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2)); +			texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2, _3, _4));  		}  		if (texture_floaterp)  		{ @@ -1478,8 +1576,11 @@ BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask)  			showPicker(FALSE);  			//grab textures first...  			LLInventoryModelBackgroundFetch::instance().start(gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE)); -			//...then start full inventory fetch. -			LLInventoryModelBackgroundFetch::instance().start(); +			//...then start full inventory fetch (should have been done on startup, but just in case. +            if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) +            { +                LLInventoryModelBackgroundFetch::instance().start(); +            }  			handled = TRUE;  		}  		else @@ -1519,7 +1620,7 @@ void LLTextureCtrl::onFloaterClose()  	mFloaterHandle.markDead();  } -void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) +void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inv_id)  {  	LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get(); @@ -1533,22 +1634,29 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id)  		else if (mCommitOnSelection || op == TEXTURE_SELECT)  			mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? -		if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work. +		if(floaterp->isDirty() || asset_id.notNull()) // mModelView->setDirty does not work.  		{  			setTentative( FALSE ); -			if (id.notNull()) -			{ -				mImageItemID = id; -				mImageAssetID = id; -			} -			else -			{ -			mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); -			LL_DEBUGS() << "mImageItemID: " << mImageItemID << LL_ENDL; -			mImageAssetID = floaterp->getAssetID(); -			LL_DEBUGS() << "mImageAssetID: " << mImageAssetID << LL_ENDL; -			} +            switch(source) +            { +                case PICKER_INVENTORY: +                    mImageItemID = inv_id; +                    mImageAssetID = asset_id; +                    break; +                case PICKER_BAKE: +                case PICKER_LOCAL: +                    mImageItemID = LLUUID::null; +                    mImageAssetID = asset_id; +                    break; +                case PICKER_UNKNOWN: +                default: +                    mImageItemID = floaterp->findItemID(asset_id, FALSE); +                    mImageAssetID = asset_id; +                    break; +            } + +            LL_DEBUGS() << "mImageAssetID: " << mImageAssetID << ", mImageItemID: " << mImageItemID << LL_ENDL;  			if (op == TEXTURE_SELECT && mOnSelectCallback)  			{ diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index fbb38c4464..e2bfe286d3 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -63,6 +63,14 @@ bool get_is_predefined_texture(LLUUID asset_id);  LLUUID get_copy_free_item_by_asset_id(LLUUID image_id, bool no_trans_perm = false);  bool get_can_copy_texture(LLUUID image_id); +enum LLPickerSource +{ +    PICKER_INVENTORY, +    PICKER_LOCAL, +    PICKER_BAKE, +    PICKER_UNKNOWN, // on cancel, default ids +}; +  //////////////////////////////////////////////////////////////////////////////////////////  // LLTextureCtrl @@ -188,7 +196,7 @@ public:  	void			closeDependentFloater();  	void			onFloaterClose(); -	void			onFloaterCommit(ETexturePickOp op, LLUUID id); +	void			onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& local_id, const LLUUID& inv_id);  	// This call is returned when a drag is detected. Your callback  	// should return TRUE if the drag is acceptable. @@ -256,7 +264,7 @@ private:  //////////////////////////////////////////////////////////////////////////////////////////  // LLFloaterTexturePicker -typedef boost::function<void(LLTextureCtrl::ETexturePickOp op, LLUUID id)> floater_commit_callback; +typedef boost::function<void(LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inventory_id)> floater_commit_callback;  typedef boost::function<void()> floater_close_callback;  typedef boost::function<void(const LLUUID& asset_id)> set_image_asset_id_callback;  typedef boost::function<void(LLPointer<LLViewerTexture> texture)> set_on_update_image_stats_callback; @@ -295,7 +303,7 @@ public:  	// New functions  	void setImageID(const LLUUID& image_asset_id, bool set_selection = true); -	void updateImageStats(); +	bool updateImageStats(); // true if within limits  	const LLUUID&	getAssetID() { return mImageAssetID; }  	const LLUUID&	findItemID(const LLUUID& asset_id, BOOL copyable_only, BOOL ignore_library = FALSE);  	void			setCanApplyImmediately(BOOL b); @@ -309,11 +317,13 @@ public:  	void updateFilterPermMask();  	void commitIfImmediateSet(); +    void commitCallback(LLTextureCtrl::ETexturePickOp op);  	void commitCancel();  	void onFilterEdit(const std::string& search_string); -	void setCanApply(bool can_preview, bool can_apply); +	void setCanApply(bool can_preview, bool can_apply, bool inworld_image = true); +    void setMinDimentionsLimits(S32 min_dim);  	void setTextureSelectedCallback(const texture_selected_callback& cb) { mTextureSelectedCallback = cb; }  	void setOnFloaterCloseCallback(const floater_close_callback& cb) { mOnFloaterCloseCallback = cb; }  	void setOnFloaterCommitCallback(const floater_commit_callback& cb) { mOnFloaterCommitCallback = cb; } @@ -364,6 +374,7 @@ protected:  	LLTextBox*			mTentativeLabel;  	LLTextBox*			mResolutionLabel; +    LLTextBox*          mResolutionWarning;  	std::string			mPendingName;  	BOOL				mActive; @@ -381,11 +392,20 @@ protected:  	LLComboBox*			mModeSelector;  	LLScrollListCtrl*	mLocalScrollCtrl; +    LLButton*           mDefaultBtn; +    LLButton*           mNoneBtn; +    LLButton*           mBlankBtn; +    LLButton*           mPipetteBtn; +    LLButton*           mSelectBtn; +    LLButton*           mCancelBtn;  private:  	bool mCanApply;  	bool mCanPreview;  	bool mPreviewSettingChanged; +    bool mLimitsSet; +    S32 mMaxDim; +    S32 mMinDim;  	texture_selected_callback mTextureSelectedCallback; diff --git a/indra/newview/llthumbnailctrl.cpp b/indra/newview/llthumbnailctrl.cpp new file mode 100644 index 0000000000..04130fc724 --- /dev/null +++ b/indra/newview/llthumbnailctrl.cpp @@ -0,0 +1,239 @@ +/**  + * @file llthumbnailctrl.cpp + * @brief LLThumbnailCtrl base class + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llthumbnailctrl.h" + +#include "linden_common.h" +#include "llagent.h" +#include "lluictrlfactory.h" +#include "lluuid.h" +#include "lltrans.h" +#include "llviewborder.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" +#include "llwindow.h" + +static LLDefaultChildRegistry::Register<LLThumbnailCtrl> r("thumbnail"); + +LLThumbnailCtrl::Params::Params() +: border("border") +, border_color("border_color") +, fallback_image("fallback_image") +, image_name("image_name") +, border_visible("show_visible", false) +, interactable("interactable", false) +, show_loading("show_loading", true) +{} + +LLThumbnailCtrl::LLThumbnailCtrl(const LLThumbnailCtrl::Params& p) +:	LLUICtrl(p) +,   mBorderColor(p.border_color()) +,   mBorderVisible(p.border_visible()) +,   mFallbackImagep(p.fallback_image) +,   mInteractable(p.interactable()) +,   mShowLoadingPlaceholder(p.show_loading()) +,	mPriority(LLGLTexture::BOOST_PREVIEW) +{ +    mLoadingPlaceholderString = LLTrans::getString("texture_loading"); +     +    LLRect border_rect = getLocalRect(); +    LLViewBorder::Params vbparams(p.border); +    vbparams.name("border"); +    vbparams.rect(border_rect); +    mBorder = LLUICtrlFactory::create<LLViewBorder> (vbparams); +    addChild(mBorder); +     +    if (p.image_name.isProvided()) +    { +        setValue(p.image_name()); +    } +} + +LLThumbnailCtrl::~LLThumbnailCtrl() +{ +	mTexturep = nullptr; +    mImagep = nullptr; +    mFallbackImagep = nullptr; +} + + +void LLThumbnailCtrl::draw() +{ +    LLRect draw_rect = getLocalRect(); +     +    if (mBorderVisible) +    { +        mBorder->setKeyboardFocusHighlight(hasFocus()); +         +        gl_rect_2d( draw_rect, mBorderColor.get(), FALSE ); +        draw_rect.stretch( -1 ); +    } + +    // If we're in a focused floater, don't apply the floater's alpha to the texture. +    const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); +    if( mTexturep ) +    { +        if( mTexturep->getComponents() == 4 ) +        { +            const LLColor4 color(.098f, .098f, .098f); +            gl_rect_2d( draw_rect, color, TRUE); +        } +         +        gl_draw_scaled_image( draw_rect.mLeft, draw_rect.mBottom, draw_rect.getWidth(), draw_rect.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha); +         +        mTexturep->setKnownDrawSize(draw_rect.getWidth(), draw_rect.getHeight()); +    } +    else if( mImagep.notNull() ) +    { +        mImagep->draw(draw_rect, UI_VERTEX_COLOR % alpha ); +    } +    else if (mFallbackImagep.notNull()) +    { +        if (draw_rect.getWidth() > mFallbackImagep->getWidth() +            && draw_rect.getHeight() > mFallbackImagep->getHeight()) +        { +            S32 img_width = mFallbackImagep->getWidth(); +            S32 img_height = mFallbackImagep->getHeight(); +            S32 rect_width = draw_rect.getWidth(); +            S32 rect_height = draw_rect.getHeight(); + +            LLRect fallback_rect; +            fallback_rect.mLeft = draw_rect.mLeft + (rect_width - img_width) / 2; +            fallback_rect.mRight = fallback_rect.mLeft + img_width; +            fallback_rect.mBottom = draw_rect.mBottom + (rect_height - img_height) / 2; +            fallback_rect.mTop = fallback_rect.mBottom + img_height; + +            mFallbackImagep->draw(fallback_rect, UI_VERTEX_COLOR % alpha); +        } +        else +        { +            mFallbackImagep->draw(draw_rect, UI_VERTEX_COLOR % alpha); +        } +    } +    else +    { +        gl_rect_2d( draw_rect, LLColor4::grey % alpha, TRUE ); + +        // Draw X +        gl_draw_x( draw_rect, LLColor4::black ); +    } + +    // Show "Loading..." string on the top left corner while this texture is loading. +    // Using the discard level, do not show the string if the texture is almost but not +    // fully loaded. +    if (mTexturep.notNull() +        && mShowLoadingPlaceholder +        && !mTexturep->isFullyLoaded()) +    { +        U32 v_offset = 25; +        LLFontGL* font = LLFontGL::getFontSansSerif(); + +        // Don't show as loaded if the texture is almost fully loaded (i.e. discard1) unless god +        if ((mTexturep->getDiscardLevel() > 1) || gAgent.isGodlike()) +        { +            font->renderUTF8( +                mLoadingPlaceholderString, +                0, +                llfloor(draw_rect.mLeft+3), +                llfloor(draw_rect.mTop-v_offset), +                LLColor4::white, +                LLFontGL::LEFT, +                LLFontGL::BASELINE, +                LLFontGL::DROP_SHADOW); +        } +    } + +    LLUICtrl::draw(); +} + +void LLThumbnailCtrl::clearTexture() +{ +    mImageAssetID = LLUUID::null; +    mTexturep = nullptr; +    mImagep = nullptr; +} + +// virtual +// value might be a string or a UUID +void LLThumbnailCtrl::setValue(const LLSD& value) +{ +	LLSD tvalue(value); +	if (value.isString() && LLUUID::validate(value.asString())) +	{ +		//RN: support UUIDs masquerading as strings +		tvalue = LLSD(LLUUID(value.asString())); +	} +     +	LLUICtrl::setValue(tvalue); +     +    mImageAssetID = LLUUID::null; +    mTexturep = nullptr; +    mImagep = nullptr; +     +	if (tvalue.isUUID()) +	{ +        mImageAssetID = tvalue.asUUID(); +        if (mImageAssetID.notNull()) +        { +            // Should it support baked textures? +            mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); +             +            mTexturep->setBoostLevel(mPriority); +            mTexturep->forceToSaveRawImage(0); +             +            S32 desired_draw_width = mTexturep->getWidth(); +            S32 desired_draw_height = mTexturep->getHeight(); +             +            mTexturep->setKnownDrawSize(desired_draw_width, desired_draw_height); +        } +	} +    else if (tvalue.isString()) +    { +        mImagep = LLUI::getUIImage(tvalue.asString(), LLGLTexture::BOOST_UI); +        if (mImagep) +        { +            LLViewerFetchedTexture* texture = dynamic_cast<LLViewerFetchedTexture*>(mImagep->getImage().get()); +            if(texture) +            { +                mImageAssetID = texture->getID(); +            } +        } +    } +} + +BOOL LLThumbnailCtrl::handleHover(S32 x, S32 y, MASK mask) +{ +    if (mInteractable && getEnabled()) +    { +        getWindow()->setCursor(UI_CURSOR_HAND); +        return TRUE; +    } +    return LLUICtrl::handleHover(x, y, mask); +} + + diff --git a/indra/newview/llthumbnailctrl.h b/indra/newview/llthumbnailctrl.h new file mode 100644 index 0000000000..686603b373 --- /dev/null +++ b/indra/newview/llthumbnailctrl.h @@ -0,0 +1,88 @@ +/**  + * @file llthumbnailctrl.h + * @brief LLThumbnailCtrl base class + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023 Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLTHUMBNAILCTRL_H +#define LL_LLTHUMBNAILCTRL_H + +#include "llui.h" +#include "lluictrl.h" +#include "llviewborder.h" // for params + +class LLUICtrlFactory; +class LLUUID; +class LLViewerFetchedTexture; + +// +// Classes +// + +//  +class LLThumbnailCtrl +: public LLUICtrl +{ +public: +	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> +	{ +        Optional<LLViewBorder::Params> border; +        Optional<LLUIColor>        border_color; +        Optional<std::string>      image_name; +        Optional<LLUIImage*>       fallback_image; +        Optional<bool>             border_visible; +        Optional<bool>             interactable; +        Optional<bool>             show_loading; +         +		Params(); +	}; +protected: +    LLThumbnailCtrl(const Params&); +	friend class LLUICtrlFactory; + +public: +	virtual ~LLThumbnailCtrl(); + +	virtual void draw() override; + +	virtual void setValue(const LLSD& value ) override; +    void clearTexture(); +     +    virtual BOOL handleHover(S32 x, S32 y, MASK mask) override; +	 +private: +	S32 mPriority; +    bool mBorderVisible; +    bool mInteractable; +    bool mShowLoadingPlaceholder; +    std::string mLoadingPlaceholderString; +    LLUUID mImageAssetID; +    LLViewBorder* mBorder; +    LLUIColor mBorderColor; +     +	LLPointer<LLViewerFetchedTexture> mTexturep; +    LLPointer<LLUIImage> mImagep; +    LLPointer<LLUIImage> mFallbackImagep; +}; + +#endif diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 692e8d91a9..d35833fac9 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -279,6 +279,10 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal  	if (!edit_text_name.empty())  	{  		S32 y = VPAD + BTN_HEIGHT + VPAD/2; +        if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO) +        { +            y += EDITOR_HEIGHT; +        }  		mLineEditor = LLUICtrlFactory::getInstance()->createFromFile<LLLineEditor>("alert_line_editor.xml", this, LLPanel::child_registry_t::instance());  		if (mLineEditor) @@ -522,6 +526,10 @@ void LLToastAlertPanel::onButtonPressed( const LLSD& data, S32 button )  	{  		response[mLineEditor->getName()] = mLineEditor->getValue();  	} +    if (mNotification->getForm()->getIgnoreType() != LLNotificationForm::IGNORE_NO) +    { +        response["ignore"] = mNotification->isIgnored(); +    }  	response[button_data->mButton->getName()] = true;  	// If we declared a URL and chose the URL option, go to the url diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index 4f47c465c4..f6628293ee 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -322,9 +322,9 @@ bool LLToolBarView::loadToolbars(bool force_default)  	}      // SL-18581: Don't show the starter avatar toolbar button for NUX users -    LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));      if (gAgent.isFirstLogin())      { +        LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));          LL_WARNS() << "First login: checking for NUX user." << LL_ENDL;          if (my_outfits_cat != NULL && my_outfits_cat->getDescendentCount() > 0)          { diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index b16b26b96e..78e068f808 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -764,7 +764,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++) @@ -1732,8 +1732,8 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(  		return ACCEPT_NO;  	} -	const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); -	if(gInventory.isObjectDescendentOf(item->getUUID(), outbox_id)) +	const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX); +	if(outbox_id.notNull() && gInventory.isObjectDescendentOf(item->getUUID(), outbox_id))  	{  		// Legacy  		return ACCEPT_NO; @@ -2159,8 +2159,8 @@ EAcceptance LLToolDragAndDrop::dad3dWearCategory(  			return ACCEPT_NO;  		} -		const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); -		if(gInventory.isObjectDescendentOf(category->getUUID(), outbox_id)) +		const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX); +		if(outbox_id.notNull() && gInventory.isObjectDescendentOf(category->getUUID(), outbox_id))  		{  			// Legacy  			return ACCEPT_NO; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 0f2fe1e1cd..15b95d70a9 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -56,6 +56,7 @@  #include "llfloaterbvhpreview.h"  #include "llfloatercamera.h"  #include "llfloatercamerapresets.h" +#include "llfloaterchangeitemthumbnail.h"  #include "llfloaterchatvoicevolume.h"  #include "llfloaterclassified.h"  #include "llfloaterconversationlog.h" @@ -84,6 +85,7 @@  #include "llfloaterimagepreview.h"  #include "llfloaterimsession.h"  #include "llfloaterinspect.h" +#include "llfloaterinventorysettings.h"  #include "llfloaterjoystick.h"  #include "llfloaterlagmeter.h"  #include "llfloaterland.h" @@ -98,12 +100,12 @@  #include "llfloatermyscripts.h"  #include "llfloatermyenvironment.h"  #include "llfloaternamedesc.h" +#include "llfloaternewfeaturenotification.h"  #include "llfloaternotificationsconsole.h"  #include "llfloaternotificationstabbed.h"  #include "llfloaterobjectweights.h"  #include "llfloateropenobject.h" -#include "llfloateroutfitphotopreview.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h"  #include "llfloaterpathfindingcharacters.h"  #include "llfloaterpathfindingconsole.h"  #include "llfloaterpathfindinglinksets.h" @@ -116,7 +118,6 @@  #include "llfloaterpreferenceviewadvanced.h"  #include "llfloaterpreviewtrash.h"  #include "llfloaterprofile.h" -#include "llfloaterproperties.h"  #include "llfloaterregiondebugconsole.h"  #include "llfloaterregioninfo.h"  #include "llfloaterregionrestarting.h" @@ -229,6 +230,7 @@ public:                  "avatar_picker",                  "camera",                  "camera_presets", +                "change_item_thumbnail"                  "classified",                  "add_landmark",                  "delete_pref_preset", @@ -247,6 +249,7 @@ public:                  "message_critical", // Modal!!! Login specific. If this is in use elsewhere, better to create a non modal variant                  "message_tos", // Modal!!! Login specific.                  "mute_object_by_name", +                "new_feature_notification",                  "publish_classified",                  "save_pref_preset",                  "save_camera_preset", @@ -330,6 +333,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>);  	LLFloaterReg::add("camera_presets", "floater_camera_presets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCameraPresets>);  	LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>); +    LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChangeItemThumbnail>);  	LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater);      LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>);  	LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>); @@ -373,6 +377,8 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);  	LLFloaterReg::add("inspect", "floater_inspect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInspect>);  	LLFloaterReg::add("item_properties", "floater_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>); +    LLFloaterReg::add("task_properties", "floater_task_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterItemProperties>); +    LLFloaterReg::add("inventory_settings", "floater_inventory_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInventorySettings>);  	LLInspectAvatarUtil::registerFloater();  	LLInspectGroupUtil::registerFloater();  	LLInspectObjectUtil::registerFloater(); @@ -400,6 +406,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMove>);  	LLFloaterReg::add("mute_object_by_name", "floater_mute_object.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGetBlockedObjectName>);  	LLFloaterReg::add("mini_map", "floater_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMap>); +    LLFloaterReg::add("new_feature_notification", "floater_new_feature_notification.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNewFeatureNotification>);  	LLFloaterReg::add("notifications_console", "floater_notifications_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotificationConsole>); @@ -408,7 +415,6 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("object_weights", "floater_object_weights.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterObjectWeights>);  	LLFloaterReg::add("openobject", "floater_openobject.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOpenObject>);  	LLFloaterReg::add("outgoing_call", "floater_outgoing_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLOutgoingCallDialog>); -	LLFloaterReg::add("outfit_photo_preview", "floater_outfit_photo_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOutfitPhotoPreview>);  	LLFloaterPayUtil::registerFloater();  	LLFloaterReg::add("pathfinding_characters", "floater_pathfinding_characters.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPathfindingCharacters>); @@ -436,7 +442,6 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("preview_sound", "floater_preview_sound.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewSound>, "preview");  	LLFloaterReg::add("preview_texture", "floater_preview_texture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewTexture>, "preview");  	LLFloaterReg::add("preview_trash", "floater_preview_trash.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreviewTrash>); -	LLFloaterReg::add("properties", "floater_inventory_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProperties>);  	LLFloaterReg::add("publish_classified", "floater_publish_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPublishClassifiedFloater>);  	LLFloaterReg::add("save_pref_preset", "floater_save_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSavePrefPreset>);  	LLFloaterReg::add("save_camera_preset", "floater_save_camera_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSaveCameraPreset>); @@ -470,7 +475,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("scene_load_stats", "floater_scene_load_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSceneLoadStats>);  	LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>);  	LLFloaterReg::add("snapshot", "floater_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSnapshot>); -    LLFloaterReg::add("simple_outfit_snapshot", "floater_simple_outfit_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSimpleOutfitSnapshot>); +    LLFloaterReg::add("simple_snapshot", "floater_simple_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSimpleSnapshot>);      LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearch>);      LLFloaterReg::add("profile", "floater_profile.xml",(LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProfile>);  	LLFloaterReg::add("guidebook", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHowTo>); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 793eb56734..c0a74e828e 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -314,7 +314,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& uuid,  											 time_t creation_date_utc) :  	LLInventoryItem(uuid, parent_uuid, perm, asset_uuid, type, inv_type,  					name, desc, sale_info, flags, creation_date_utc), -	mIsComplete(TRUE) +	mIsComplete(true)  {  } @@ -323,7 +323,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id,  											 const std::string& name,  											 LLInventoryType::EType inv_type) :  	LLInventoryItem(), -	mIsComplete(FALSE) +	mIsComplete(false)  {  	mUUID = item_id;  	mParentUUID = parent_id; @@ -333,7 +333,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id,  LLViewerInventoryItem::LLViewerInventoryItem() :  	LLInventoryItem(), -	mIsComplete(FALSE) +	mIsComplete(false)  {  } @@ -350,7 +350,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other)  LLViewerInventoryItem::LLViewerInventoryItem(const LLInventoryItem *other) :  	LLInventoryItem(other), -	mIsComplete(TRUE) +	mIsComplete(true)  {  } @@ -432,48 +432,43 @@ void LLViewerInventoryItem::fetchFromServer(void) const  {  	if(!mIsComplete)  	{ -		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; -		} - -		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(); -		} +        if (AISAPI::isAvailable()) // AIS v 3 +        { +            LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(mUUID); +        } +        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; +            } + +            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"); +            } +        }  	}  } @@ -568,7 +563,8 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid,  	LLInventoryCategory(uuid, parent_uuid, pref, name),  	mOwnerID(owner_id),  	mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN), -	mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) +	mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN), +    mFetching(FETCH_NONE)  {  	mDescendentsRequested.reset();  } @@ -576,7 +572,8 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid,  LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) :  	mOwnerID(owner_id),  	mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN), -	mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) +	mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN), +    mFetching(FETCH_NONE)  {  	mDescendentsRequested.reset();  } @@ -584,6 +581,7 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) :  LLViewerInventoryCategory::LLViewerInventoryCategory(const LLViewerInventoryCategory* other)  {  	copyViewerCategory(other); +    mFetching = FETCH_NONE;  }  LLViewerInventoryCategory::~LLViewerInventoryCategory() @@ -659,7 +657,6 @@ bool LLViewerInventoryCategory::fetch()  		mDescendentsRequested.reset();  		mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY); -  		std::string url;  		if (gAgent.getRegion())  		{ @@ -669,15 +666,52 @@ 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);			 +			LLInventoryModelBackgroundFetch::instance().start(mUUID, false);  		}  		return true;  	}  	return false;  } +LLViewerInventoryCategory::EFetchType LLViewerInventoryCategory::getFetching() +{ +    // if timer hasn't expired, request was scheduled, but not in progress +    // if mFetching request was actually started +    if (mDescendentsRequested.hasExpired()) +    { +        mFetching = FETCH_NONE; +    } +    return mFetching; +} + +void LLViewerInventoryCategory::setFetching(LLViewerInventoryCategory::EFetchType fetching) +{ +    if (fetching > mFetching) // allow a switch from normal to recursive +    { +        if (mDescendentsRequested.hasExpired() || (mFetching == FETCH_NONE)) +        { +            mDescendentsRequested.reset(); +            if (AISAPI::isAvailable()) +            { +                mDescendentsRequested.setTimerExpirySec(AISAPI::HTTP_TIMEOUT); +            } +            else +            { +                const F32 FETCH_TIMER_EXPIRY = 30.0f; +                mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY); +            } +        } +        mFetching = fetching; +    } +    else if (fetching == FETCH_NONE) +    { +        mDescendentsRequested.stop(); +        mFetching = fetching; +    } +} +  S32 LLViewerInventoryCategory::getViewerDescendentCount() const  {  	LLInventoryModel::cat_array_t* cats; @@ -1002,13 +1036,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<LLInventoryCallback> 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<LLInventoryCallback> cb)  {  	//check if name is equal to one of special inventory items names  	//EXT-5839 @@ -1029,6 +1068,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<LLViewerInventoryItem> 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); @@ -1036,7 +1123,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); @@ -1292,17 +1379,15 @@ void update_inventory_category(  	LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL;  	if(obj)  	{ -		if (LLFolderType::lookupIsProtectedType(obj->getPreferredType())) +        if (LLFolderType::lookupIsProtectedType(obj->getPreferredType()) +            && (updates.size() != 1 || !updates.has("thumbnail")))  		{  			LLNotificationsUtil::add("CannotModifyProtectedCategories");  			return;  		} -		LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(obj); -		new_cat->fromLLSD(updates); -        LLSD new_llsd = new_cat->asLLSD();          AISAPI::completion_t cr = boost::bind(&doInventoryCb, cb, _1); -        AISAPI::UpdateCategory(cat_id, new_llsd, cr); +        AISAPI::UpdateCategory(cat_id, updates, cr);  	}  } @@ -1354,25 +1439,10 @@ void remove_inventory_item(  				gInventory.onObjectDeletedFromServer(item_id);  			}  		} -		else // no cap -		{ -			LLMessageSystem* msg = gMessageSystem; -			msg->newMessageFast(_PREHASH_RemoveInventoryItem); -			msg->nextBlockFast(_PREHASH_AgentData); -			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); -			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());  -			msg->nextBlockFast(_PREHASH_InventoryData); -			msg->addUUIDFast(_PREHASH_ItemID, item_id); -			gAgent.sendReliableMessage(); - -			// Update inventory and call callback immediately since -			// message-based system has no callback mechanism (!) -			gInventory.onObjectDeletedFromServer(item_id); -			if (cb) -			{ -				cb->fire(item_id); -			} -		} +        else +        { +            LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL; +        }  	}  	else  	{ @@ -1501,28 +1571,10 @@ void purge_descendents_of(const LLUUID& id, LLPointer<LLInventoryCallback> cb)  			AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t();  			AISAPI::PurgeDescendents(id, cr);  		} -		else // no cap -		{ -			// Fast purge -			LL_DEBUGS(LOG_INV) << "purge_descendents_of fast case " << cat->getName() << LL_ENDL; - -			// send it upstream -			LLMessageSystem* msg = gMessageSystem; -			msg->newMessage("PurgeInventoryDescendents"); -			msg->nextBlock("AgentData"); -			msg->addUUID("AgentID", gAgent.getID()); -			msg->addUUID("SessionID", gAgent.getSessionID()); -			msg->nextBlock("InventoryData"); -			msg->addUUID("FolderID", id); -			gAgent.sendReliableMessage(); - -			// Update model immediately because there is no callback mechanism. -			gInventory.onDescendentsPurgedFromServer(id); -			if (cb) -			{ -				cb->fire(id); -			} -		} +        else +        { +            LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL; +        }  	}  } @@ -1594,13 +1646,14 @@ void create_new_item(const std::string& name,  				   const LLUUID& parent_id,  				   LLAssetType::EType asset_type,  				   LLInventoryType::EType inv_type, -				   U32 next_owner_perm) +				   U32 next_owner_perm, +                   std::function<void(const LLUUID&)> created_cb = NULL)  {  	std::string desc;  	LLViewerAssetType::generateDescriptionFor(asset_type, desc);  	next_owner_perm = (next_owner_perm) ? next_owner_perm : PERM_MOVE | PERM_TRANSFER; -	LLPointer<LLInventoryCallback> cb = NULL; +	LLPointer<LLBoostFuncInventoryCallback> cb = NULL;  	switch (inv_type)  	{ @@ -1624,9 +1677,17 @@ void create_new_item(const std::string& name,  			next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Notecards");  			break;  		} -		default: -			break; -	} + +        default: +        { +            cb = new LLBoostFuncInventoryCallback(); +            break; +        } +    } +    if (created_cb != NULL) +    { +        cb->addOnFireFunc(created_cb); +    }  	create_inventory_item(gAgent.getID(),  						  gAgent.getSessionID(), @@ -1678,57 +1739,86 @@ const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probabl  // ! REFACTOR ! Really need to refactor this so that it's not a bunch of if-then statements...  void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid)  { -	std::string type_name = userdata.asString(); -	 -	if (("inbox" == type_name) || ("category" == type_name) || ("current" == type_name) || ("outfit" == type_name) || ("my_otfts" == type_name)) -	{ -		LLFolderType::EType preferred_type = LLFolderType::lookup(type_name); +    menu_create_inventory_item(panel, bridge ? bridge->getUUID() : LLUUID::null, userdata, default_parent_uuid); +} -		LLUUID parent_id; -		if (bridge) -		{ -			parent_id = bridge->getUUID(); -		} -		else if (default_parent_uuid.notNull()) -		{ -			parent_id = default_parent_uuid; -		} -		else -		{ -			parent_id = gInventory.getRootFolderID(); -		} +void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const LLSD& userdata, const LLUUID& default_parent_uuid, std::function<void(const LLUUID&)> created_cb) +{ +    std::string type_name = userdata.asString(); +     +    if (("inbox" == type_name) || ("category" == type_name) || ("current" == type_name) || ("outfit" == type_name) || ("my_otfts" == type_name)) +    { +        LLFolderType::EType preferred_type = LLFolderType::lookup(type_name); -		LLUUID category = gInventory.createNewCategory(parent_id, preferred_type, LLStringUtil::null); -		gInventory.notifyObservers(); -		panel->setSelectionByID(category, TRUE); -	} -	else if ("lsl" == type_name) -	{ -		const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT); -		create_new_item(NEW_LSL_NAME, -					  parent_id, -					  LLAssetType::AT_LSL_TEXT, -					  LLInventoryType::IT_LSL, -					  PERM_MOVE | PERM_TRANSFER);	// overridden in create_new_item -	} -	else if ("notecard" == type_name) -	{ -		const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_NOTECARD); -		create_new_item(NEW_NOTECARD_NAME, -					  parent_id, -					  LLAssetType::AT_NOTECARD, -					  LLInventoryType::IT_NOTECARD, -					  PERM_ALL);	// overridden in create_new_item -	} -	else if ("gesture" == type_name) -	{ -		const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); -		create_new_item(NEW_GESTURE_NAME, -					  parent_id, -					  LLAssetType::AT_GESTURE, -					  LLInventoryType::IT_GESTURE, -					  PERM_ALL);	// overridden in create_new_item -	} +        LLUUID parent_id; +        if (dest_id.notNull()) +        { +            parent_id = dest_id; +        } +        else if (default_parent_uuid.notNull()) +        { +            parent_id = default_parent_uuid; +        } +        else +        { +            parent_id = gInventory.getRootFolderID(); +        } + +        std::function<void(const LLUUID&)> callback_cat_created = NULL; +        if (panel) +        { +            LLHandle<LLPanel> handle = panel->getHandle(); +            callback_cat_created = [handle](const LLUUID& new_category_id) +            { +                gInventory.notifyObservers(); +                LLInventoryPanel* panel = static_cast<LLInventoryPanel*>(handle.get()); +                if (panel) +                { +                    panel->setSelectionByID(new_category_id, TRUE); +                } +                LL_DEBUGS(LOG_INV) << "Done creating inventory: " << new_category_id << LL_ENDL; +            }; +        } +        else if (created_cb != NULL) +        { +            callback_cat_created = created_cb; +        } +        gInventory.createNewCategory( +            parent_id, +            preferred_type, +            LLStringUtil::null, +            callback_cat_created); +    } +    else if ("lsl" == type_name) +    { +        const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT); +        create_new_item(NEW_LSL_NAME, +                      parent_id, +                      LLAssetType::AT_LSL_TEXT, +                      LLInventoryType::IT_LSL, +                      PERM_MOVE | PERM_TRANSFER, +                      created_cb);    // overridden in create_new_item +    } +    else if ("notecard" == type_name) +    { +        const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_NOTECARD); +        create_new_item(NEW_NOTECARD_NAME, +                      parent_id, +                      LLAssetType::AT_NOTECARD, +                      LLInventoryType::IT_NOTECARD, +                      PERM_ALL, +                      created_cb);    // overridden in create_new_item +    } +    else if ("gesture" == type_name) +    { +        const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); +        create_new_item(NEW_GESTURE_NAME, +                      parent_id, +                      LLAssetType::AT_GESTURE, +                      LLInventoryType::IT_GESTURE, +                      PERM_ALL, +                      created_cb);    // overridden in create_new_item +    }      else if (("sky" == type_name) || ("water" == type_name) || ("daycycle" == type_name))      {          LLSettingsType::type_e stype(LLSettingsType::ST_NONE); @@ -1751,25 +1841,28 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge,              return;          } -        LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); +        LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); -        LLSettingsVOBase::createNewInventoryItem(stype, parent_id); +        LLSettingsVOBase::createNewInventoryItem(stype, parent_id, created_cb); +    } +    else +    { +        // Use for all clothing and body parts.  Adding new wearable types requires updating LLWearableDictionary. +        LLWearableType::EType wearable_type = LLWearableType::getInstance()->typeNameToType(type_name); +        if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT) +        { +            const LLUUID parent_id = dest_id; +            LLAgentWearables::createWearable(wearable_type, false, parent_id, created_cb); +        } +        else +        { +            LL_WARNS(LOG_INV) << "Can't create unrecognized type " << type_name << LL_ENDL; +        } +    } +    if(panel) +    { +        panel->getRootFolder()->setNeedsAutoRename(TRUE);      } -	else -	{ -		// Use for all clothing and body parts.  Adding new wearable types requires updating LLWearableDictionary. -		LLWearableType::EType wearable_type = LLWearableType::getInstance()->typeNameToType(type_name); -		if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT) -		{ -			const LLUUID parent_id = bridge ? bridge->getUUID() : LLUUID::null; -			LLAgentWearables::createWearable(wearable_type, false, parent_id); -		} -		else -		{ -			LL_WARNS(LOG_INV) << "Can't create unrecognized type " << type_name << LL_ENDL; -		} -	} -	panel->getRootFolder()->setNeedsAutoRename(TRUE);	  }  LLAssetType::EType LLViewerInventoryItem::getType() const @@ -1895,6 +1988,25 @@ const LLSaleInfo& LLViewerInventoryItem::getSaleInfo() const  	return LLInventoryItem::getSaleInfo();  } +const LLUUID& LLViewerInventoryItem::getThumbnailUUID() const +{ +    if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_TEXTURE) +    { +        return mAssetUUID; +    } +    if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK) +    { +        LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID); +        return linked_item ? linked_item->getThumbnailUUID() : LLUUID::null; +    } +    if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK_FOLDER) +    { +        LLViewerInventoryCategory *linked_cat = gInventory.getCategory(mAssetUUID); +        return linked_cat ? linked_cat->getThumbnailUUID() : LLUUID::null; +    } +    return mThumbnailUUID; +} +  LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const  {  	if (const LLViewerInventoryItem *linked_item = getLinkedItem()) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 24b632632b..6d3676ba2e 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -72,6 +72,7 @@ public:  	virtual const LLUUID& getCreatorUUID() const;  	virtual const std::string& getDescription() const;  	virtual const LLSaleInfo& getSaleInfo() const; +    virtual const LLUUID& getThumbnailUUID() const;  	virtual LLInventoryType::EType getInventoryType() const;  	virtual bool isWearableType() const;  	virtual LLWearableType::EType getWearableType() const; @@ -134,8 +135,8 @@ public:  	virtual BOOL importLegacyStream(std::istream& input_stream);  	// new methods -	BOOL isFinished() const { return mIsComplete; } -	void setComplete(BOOL complete) { mIsComplete = complete; } +	bool isFinished() const { return mIsComplete; } +	void setComplete(bool complete) { mIsComplete = complete; }  	//void updateAssetOnServer() const;  	virtual void setTransactionID(const LLTransactionID& transaction_id); @@ -163,7 +164,7 @@ public:  	BOOL regenerateLink();  public: -	BOOL mIsComplete; +	bool mIsComplete;  	LLTransactionID mTransactionID;  }; @@ -208,9 +209,18 @@ public:  	S32 getVersion() const;  	void setVersion(S32 version); -	// Returns true if a fetch was issued. +	// Returns true if a fetch was issued (not nessesary in progress).  	bool fetch(); +    typedef enum { +        FETCH_NONE = 0, +        FETCH_NORMAL, +        FETCH_RECURSIVE, +    } EFetchType; +    EFetchType getFetching(); +    // marks as fetch being in progress or as done +    void setFetching(EFetchType); +  	// used to help make caching more robust - for example, if  	// someone is getting 4 packets but logs out after 3. the viewer  	// may never know the cache is wrong. @@ -239,12 +249,14 @@ protected:  	LLUUID mOwnerID;  	S32 mVersion;  	S32 mDescendentCount; +    EFetchType mFetching;  	LLFrameTimer mDescendentsRequested;  };  class LLInventoryCallback : public LLRefCount  {  public: +    virtual ~LLInventoryCallback() {}  	virtual void fire(const LLUUID& inv_item) = 0;  }; @@ -283,17 +295,29 @@ class LLBoostFuncInventoryCallback: public LLInventoryCallback  {  public: -	LLBoostFuncInventoryCallback(inventory_func_type fire_func = no_op_inventory_func, +	LLBoostFuncInventoryCallback(inventory_func_type fire_func,  								 nullary_func_type destroy_func = no_op): -		mFireFunc(fire_func),  		mDestroyFunc(destroy_func)  	{ +        mFireFuncs.push_back(fire_func);  	} +    LLBoostFuncInventoryCallback() +    { +    } + +    void addOnFireFunc(inventory_func_type fire_func) +    { +        mFireFuncs.push_back(fire_func); +    } +  	// virtual  	void fire(const LLUUID& item_id)      { -		mFireFunc(item_id); +        for (inventory_func_type &func: mFireFuncs) +        { +            func(item_id); +        }  	}  	// virtual @@ -304,7 +328,7 @@ public:  private: -	inventory_func_type mFireFunc; +	std::list<inventory_func_type> mFireFuncs;  	nullary_func_type mDestroyFunc;  }; @@ -445,6 +469,8 @@ void menu_create_inventory_item(LLInventoryPanel* root,  								const LLSD& userdata,  								const LLUUID& default_parent_uuid = LLUUID::null); +void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null, std::function<void(const LLUUID&)> folder_created_cb = NULL); +  void slam_inventory_folder(const LLUUID& folder_id,  						   const LLSD& contents,  						   LLPointer<LLInventoryCallback> cb); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index cc59e559e9..8686fad3e6 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -402,14 +402,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;          }      } @@ -3065,9 +3065,7 @@ void handle_object_inspect()  	LLViewerObject* selected_objectp = selection->getFirstRootObject();  	if (selected_objectp)  	{ -		LLSD key; -		key["task"] = "task"; -		LLFloaterSidePanelContainer::showPanel("inventory", key); +        LLFloaterReg::showInstance("task_properties");  	}  	/* diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 2634c8d908..17e89e1850 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -38,7 +38,7 @@  #include "llfloatermap.h"  #include "llfloatermodelpreview.h"  #include "llfloatersnapshot.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h"  #include "llimage.h"  #include "llimagebmp.h"  #include "llimagepng.h" @@ -735,9 +735,7 @@ class LLFileEnableCloseAllWindows : public view_listener_t  	bool handleEvent(const LLSD& userdata)  	{  		LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance(); -		LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance(); -		bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain()) -			|| (floater_outfit_snapshot && floater_outfit_snapshot->isInVisibleChain()); +		bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain());  		bool open_children = gFloaterView->allChildrenClosed() && !is_floaters_snapshot_opened;  		return !open_children && !LLNotificationsUI::LLToast::isAlertToastShown();  	} @@ -752,9 +750,6 @@ class LLFileCloseAllWindows : public view_listener_t  		LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance();  		if (floater_snapshot)  			floater_snapshot->closeFloater(app_quitting); -        LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance(); -		if (floater_outfit_snapshot) -			floater_outfit_snapshot->closeFloater(app_quitting);  		if (gMenuHolder) gMenuHolder->hideMenus();  		return true;  	} diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 7421bba733..f3288a5300 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -76,6 +76,7 @@  #include "llnotifications.h"  #include "llnotificationsutil.h"  #include "llpanelgrouplandmoney.h" +#include "llpanelmaininventory.h"  #include "llrecentpeople.h"  #include "llscriptfloater.h"  #include "llscriptruntimeperms.h" @@ -1537,11 +1538,35 @@ void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_nam  		}  		//////////////////////////////////////////////////////////////////////////////// +        static LLUICachedControl<bool> find_original_new_floater("FindOriginalOpenWindow", false); +        //show in a new single-folder window +        if(find_original_new_floater && !from_name.empty()) +        { +            const LLInventoryObject *obj = gInventory.getObject(obj_id); +            if (obj && obj->getParentUUID().notNull()) +            { +                if (obj->getActualType() == LLAssetType::AT_CATEGORY) +                { +                    LLPanelMainInventory::newFolderWindow(obj_id); +                } +                else +                { +                    LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), obj_id); +                } +            } +        } +        else +        {  		// Highlight item  		const BOOL auto_open =   			gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false  			!from_name.empty(); // don't open if it's not from anyone. -		LLInventoryPanel::openInventoryPanelAndSetSelection(auto_open, obj_id); +            if(auto_open) +            { +                LLFloaterReg::showInstance("inventory"); +            } +		LLInventoryPanel::openInventoryPanelAndSetSelection(auto_open, obj_id, true); +        }  	}  } @@ -5918,42 +5943,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<LLViewerInventoryItem> 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<LLViewerInventoryItem> 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/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index f47f0b4572..9275cfb86d 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -63,7 +63,6 @@  #include "llcontrolavatar.h"  #include "lldrawable.h"  #include "llface.h" -#include "llfloaterproperties.h"  #include "llfloatertools.h"  #include "llfollowcam.h"  #include "llhudtext.h" @@ -3474,9 +3473,6 @@ void LLViewerObject::doInventoryCallback()  void LLViewerObject::removeInventory(const LLUUID& item_id)  { -	// close any associated floater properties -	LLFloaterReg::hideInstance("properties", item_id); -  	LLMessageSystem* msg = gMessageSystem;  	msg->newMessageFast(_PREHASH_RemoveTaskInventory);  	msg->nextBlockFast(_PREHASH_AgentData); @@ -6038,7 +6034,7 @@ LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 para        }  	  default:  	  { -		  LL_INFOS() << "Unknown param type." << LL_ENDL; +		  LL_INFOS_ONCE() << "Unknown param type: " << param_type << LL_ENDL;  		  break;  	  }  	}; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 4a1dd1b8d6..84956d3b3d 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -3027,6 +3027,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)  	capabilityNames.append("InterestList"); +    capabilityNames.append("InventoryThumbnailUpload");  	capabilityNames.append("GetDisplayNames");  	capabilityNames.append("GetExperiences");  	capabilityNames.append("AgentExperiences"); diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index d2701f0aff..c28fed9ca4 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -36,6 +36,7 @@  #include "llfloatersidepanelcontainer.h"  #include "llfloaterworldmap.h"  #include "llfocusmgr.h" +#include "llinspecttexture.h"  #include "llinventorybridge.h"  #include "llinventorydefines.h"  #include "llinventorymodel.h" @@ -245,6 +246,21 @@ public:  	}  	virtual BOOL				handleToolTip(S32 x, S32 y, MASK mask )  	{  +		if (mItem->getThumbnailUUID().notNull()) +		{ +            LLSD params; +            params["inv_type"] = mItem->getInventoryType(); +            params["thumbnail_id"] = mItem->getThumbnailUUID(); +            params["asset_id"] = mItem->getAssetUUID(); +             +			LLToolTipMgr::instance().show(LLToolTip::Params() +					.message(mToolTip) +					.create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) +					.create_params(params)); + +			return TRUE; +		} +  		if (!mToolTip.empty())  		{  			LLToolTipMgr::instance().show(mToolTip); diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index b48290776a..ddb7014648 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1295,11 +1295,52 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)  	<< LL_ENDL;  } +bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image, +                                           const std::string& out_filename, +                                           const S32 max_image_dimentions, +                                           const S32 min_image_dimentions) +{ +    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + +    // make a copy, since convertToUploadFile scales raw image +    LLPointer<LLImageRaw> scale_image = new LLImageRaw( +        raw_image->getData(), +        raw_image->getWidth(), +        raw_image->getHeight(), +        raw_image->getComponents()); + +    LLPointer<LLImageJ2C> compressedImage = LLViewerTextureList::convertToUploadFile(scale_image, max_image_dimentions); +    if (compressedImage->getWidth() < min_image_dimentions || compressedImage->getHeight() < min_image_dimentions) +    { +        std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", +                                      min_image_dimentions, +                                      min_image_dimentions, +                                      compressedImage->getWidth(), +                                      compressedImage->getHeight()); +        compressedImage->setLastError(reason); +        return false; +    } +    if (compressedImage.isNull()) +    { +        compressedImage->setLastError("Couldn't convert the image to jpeg2000."); +        LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL; +        return false; +    } +    if (!compressedImage->save(out_filename)) +    { +        compressedImage->setLastError("Couldn't create the jpeg2000 image for upload."); +        LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL; +        return false; +    } +    return true; +}  BOOL LLViewerTextureList::createUploadFile(const std::string& filename,  										 const std::string& out_filename,  										 const U8 codec, -										 const S32 max_image_dimentions) +										 const S32 max_image_dimentions, +										 const S32 min_image_dimentions, +										 bool force_square)  {	      LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;  	// Load the image @@ -1327,8 +1368,18 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename,  		image->setLastError("Image files with less than 3 or more than 4 components are not supported.");  		return FALSE;  	} +    if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions) +    { +        std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", +            min_image_dimentions, +            min_image_dimentions, +            image->getWidth(), +            image->getHeight()); +        image->setLastError(reason); +        return FALSE; +    }  	// Convert to j2c (JPEG2000) and save the file locally -	LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions); +	LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);  	if (compressedImage.isNull())  	{  		image->setLastError("Couldn't convert the image to jpeg2000."); @@ -1353,10 +1404,20 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename,  }  // note: modifies the argument raw_image!!!! -LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImageRaw> raw_image, const S32 max_image_dimentions) +LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImageRaw> raw_image, const S32 max_image_dimentions, bool force_square)  {  	LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -	raw_image->biasedScaleToPowerOfTwo(max_image_dimentions); +    if (force_square) +    { +        S32 biggest_side = llmax(raw_image->getWidth(), raw_image->getHeight()); +        S32 square_size = raw_image->biasedDimToPowerOfTwo(biggest_side, max_image_dimentions); + +        raw_image->scale(square_size, square_size); +    } +    else +    { +        raw_image->biasedScaleToPowerOfTwo(max_image_dimentions); +    }  	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();  	if (gSavedSettings.getBOOL("LosslessJ2CUpload") && diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 0018e78d45..82dec6b329 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -92,11 +92,19 @@ class LLViewerTextureList  	friend class LLLocalBitmap;  public: +    static bool createUploadFile(LLPointer<LLImageRaw> raw_image, +                                 const std::string& out_filename, +                                 const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT, +                                 const S32 min_image_dimentions = 0);      static BOOL createUploadFile(const std::string& filename,                                   const std::string& out_filename,                                   const U8 codec, -                                 const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); -	static LLPointer<LLImageJ2C> convertToUploadFile(LLPointer<LLImageRaw> raw_image, const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); +                                 const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT, +                                 const S32 min_image_dimentions = 0, +                                 bool force_square = false); +	static LLPointer<LLImageJ2C> convertToUploadFile(LLPointer<LLImageRaw> raw_image, +                                                     const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT, +                                                     bool force_square = false);  	static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data );  public: diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 348442cf18..185e8612bc 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -9455,7 +9455,14 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )  	// RequestAgentUpdateAppearanceResponder::onRequestRequested()  	// assumes that cof version is only updated with server-bake  	// appearance messages. -    LL_INFOS("Avatar") << "Processing appearance message version " << thisAppearanceVersion << LL_ENDL; +    if (isSelf()) +    { +        LL_INFOS("Avatar") << "Processing appearance message version " << thisAppearanceVersion << LL_ENDL; +    } +    else +    { +        LL_INFOS("Avatar") << "Processing appearance message for " << getID() << ", version " << thisAppearanceVersion << LL_ENDL; +    }      // Note:      // locally the COF is maintained via LLInventoryModel::accountForUpdate diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 86c6567c83..c0d4dd0181 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -5114,8 +5114,6 @@ void LLControlAVBridge::updateSpatialExtents()  {  	LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE -	LLControlAvatar* controlAvatar = getVObj()->getControlAvatar(); -  	LLSpatialGroup* root = (LLSpatialGroup*)mOctree->getListener(0);  	bool rootWasDirty = root->isDirty(); @@ -5126,7 +5124,11 @@ void LLControlAVBridge::updateSpatialExtents()  	// disappear when root goes off-screen"  	//  	// Expand extents to include Control Avatar placed outside of the bounds -	if (controlAvatar && (rootWasDirty || controlAvatar->mPlaying)) +    LLControlAvatar* controlAvatar = getVObj() ? getVObj()->getControlAvatar() : NULL; +    if (controlAvatar +        && controlAvatar->mDrawable +        && controlAvatar->mDrawable->getEntry() +        && (rootWasDirty || controlAvatar->mPlaying))  	{  		root->expandExtents(controlAvatar->mDrawable->getSpatialExtents(), *mDrawable->getXform());  	} diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index a6820086fc..c977cd3bef 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -407,7 +407,7 @@       value="0.43 0.06 0.06 1" />      <color       name="HTMLLinkColor" -     reference="EmphasisColor" /> +     value="0.3 0.82 1 1" />      <color       name="HealthTextColor"       reference="White" /> diff --git a/indra/newview/skins/default/textures/icons/copy_clipboard.png b/indra/newview/skins/default/textures/icons/copy_clipboard.png Binary files differnew file mode 100644 index 0000000000..bb1ceff1ce --- /dev/null +++ b/indra/newview/skins/default/textures/icons/copy_clipboard.png diff --git a/indra/newview/skins/default/textures/icons/delete_icon.png b/indra/newview/skins/default/textures/icons/delete_icon.png Binary files differnew file mode 100644 index 0000000000..37ce374653 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/delete_icon.png diff --git a/indra/newview/skins/default/textures/icons/file_upload.png b/indra/newview/skins/default/textures/icons/file_upload.png Binary files differnew file mode 100644 index 0000000000..58f2757136 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/file_upload.png diff --git a/indra/newview/skins/default/textures/icons/multi_folder_mode.png b/indra/newview/skins/default/textures/icons/multi_folder_mode.png Binary files differnew file mode 100644 index 0000000000..8cda3efc36 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/multi_folder_mode.png diff --git a/indra/newview/skins/default/textures/icons/paste_clipboard.png b/indra/newview/skins/default/textures/icons/paste_clipboard.png Binary files differnew file mode 100644 index 0000000000..e1589ab098 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/paste_clipboard.png diff --git a/indra/newview/skins/default/textures/icons/single_folder_back.png b/indra/newview/skins/default/textures/icons/single_folder_back.png Binary files differnew file mode 100644 index 0000000000..b614e9ef9b --- /dev/null +++ b/indra/newview/skins/default/textures/icons/single_folder_back.png diff --git a/indra/newview/skins/default/textures/icons/single_folder_forward.png b/indra/newview/skins/default/textures/icons/single_folder_forward.png Binary files differnew file mode 100644 index 0000000000..c7bee3522d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/single_folder_forward.png diff --git a/indra/newview/skins/default/textures/icons/single_folder_mode.png b/indra/newview/skins/default/textures/icons/single_folder_mode.png Binary files differnew file mode 100644 index 0000000000..f70b754123 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/single_folder_mode.png diff --git a/indra/newview/skins/default/textures/icons/single_folder_up.png b/indra/newview/skins/default/textures/icons/single_folder_up.png Binary files differnew file mode 100644 index 0000000000..651b2b1af1 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/single_folder_up.png diff --git a/indra/newview/skins/default/textures/icons/snapshot_icon.png b/indra/newview/skins/default/textures/icons/snapshot_icon.png Binary files differnew file mode 100644 index 0000000000..41d524678f --- /dev/null +++ b/indra/newview/skins/default/textures/icons/snapshot_icon.png diff --git a/indra/newview/skins/default/textures/icons/texture_icon.png b/indra/newview/skins/default/textures/icons/texture_icon.png Binary files differnew file mode 100644 index 0000000000..278760a5b0 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/texture_icon.png diff --git a/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png b/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png Binary files differnew file mode 100644 index 0000000000..8d5ca624af --- /dev/null +++ b/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index d0fa19f1a1..656c9b146e 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -253,10 +253,16 @@ with the same filename but different name    <texture name="Icon_Close_Foreground" file_name="windows/Icon_Close_Foreground.png" preload="true" />    <texture name="Icon_Close_Press" file_name="windows/Icon_Close_Press.png" preload="true" />    <texture name="Icon_Close_Toast" file_name="windows/Icon_Close_Toast.png" preload="true" /> +   +  <texture name="Icon_Copy" file_name="icons/copy_clipboard.png" preload="true" /> +   +  <texture name="Icon_Delete" file_name="icons/delete_icon.png" preload="true" />    <texture name="Icon_Dock_Foreground" file_name="windows/Icon_Dock_Foreground.png" preload="true" />    <texture name="Icon_Dock_Press" file_name="windows/Icon_Dock_Press.png" preload="true" /> +  <texture name="Icon_File_Upload" file_name="icons/file_upload.png" preload="true" /> +      <texture name="Icon_For_Sale" file_name="icons/Icon_For_Sale.png" preload="false" />    <texture name="Icon_Gear_Background" file_name="windows/Icon_Gear_Background.png" preload="false" /> @@ -269,10 +275,16 @@ with the same filename but different name    <texture name="Icon_Minimize_Foreground" file_name="windows/Icon_Minimize_Foreground.png" preload="true" />    <texture name="Icon_Minimize_Press" file_name="windows/Icon_Minimize_Press.png" preload="true" /> +   +  <texture name="Icon_Paste" file_name="icons/paste_clipboard.png" preload="true" />    <texture name="Icon_Restore_Foreground" file_name="windows/Icon_Restore_Foreground.png" preload="false" />    <texture name="Icon_Restore_Press" file_name="windows/Icon_Restore_Press.png" preload="false" /> +  <texture name="Icon_Snapshot" file_name="icons/snapshot_icon.png" preload="true" /> +   +  <texture name="Icon_Use_Texture" file_name="icons/texture_icon.png" preload="true" /> +    <texture name="Info" file_name="icons/Info.png" preload="false" />    <texture name="Info_Small" file_name="icons/Info_Small.png" preload="false" />    <texture name="Info_Off" file_name="navbar/Info_Off.png" preload="false" /> @@ -685,6 +697,7 @@ with the same filename but different name    <texture name="TextField_Active" file_name="widgets/TextField_Active.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" />    <texture name="TextField_Search_Highlight" file_name="widgets/TextField_Search_Highlight.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" /> +  <texture name="Thumbnail_Fallback" file_name="icons/thumbnail_fallback_icon.png" preload="true" />    <texture name="Toast_CloseBtn" file_name="windows/Toast_CloseBtn.png" preload="true" />    <texture name="Toast_Background" file_name="windows/Toast_Background.png" preload="true" @@ -881,4 +894,10 @@ with the same filename but different name    <texture name="System_Notification" file_name="icons/SL_Logo.png" preload="true"/>    <texture name="Icon_Attachment_Small" file_name="icons/Icon_Attachment_Small.png"	preload="true"/>    <texture name="Icon_Attachment_Large" file_name="icons/Icon_Attachment_Large.png"	preload="true"/> + +  <texture name="Single_Folder_Mode" file_name="icons/single_folder_mode.png" preload="true"/> +  <texture name="Multi_Folder_Mode" file_name="icons/multi_folder_mode.png" preload="true"/> +  <texture name="Single_Folder_Back" file_name="icons/single_folder_back.png" preload="true"/> +  <texture name="Single_Folder_Forward" file_name="icons/single_folder_forward.png" preload="true"/> +  <texture name="Single_Folder_Up" file_name="icons/single_folder_up.png" preload="true"/>  </textures> diff --git a/indra/newview/skins/default/xui/da/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/da/floater_inventory_item_properties.xml deleted file mode 100644 index 59dcc87140..0000000000 --- a/indra/newview/skins/default/xui/da/floater_inventory_item_properties.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="OPLYSNINGER OM BEHOLDNINGSGENSTAND"> -	<floater.string name="unknown"> -		(ukendt) -	</floater.string> -	<floater.string name="public"> -		(offentlig) -	</floater.string> -	<floater.string name="you_can"> -		Du kan: -	</floater.string> -	<floater.string name="owner_can"> -		Ejer kan: -	</floater.string> -	<floater.string name="acquiredDate"> -		[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] -	</floater.string> -	<text name="LabelItemNameTitle"> -		Navn: -	</text> -	<text name="LabelItemDescTitle"> -		Beskrivelse: -	</text> -	<text name="LabelCreatorTitle"> -		Skaber: -	</text> -	<button label="Profil..." label_selected="" name="BtnCreator"/> -	<text name="LabelOwnerTitle"> -		Ejer: -	</text> -	<button label="Profil..." label_selected="" name="BtnOwner"/> -	<text name="LabelAcquiredTitle"> -		Erhvervet: -	</text> -	<text name="LabelAcquiredDate"> -		Wed May 24 12:50:46 2006 -	</text> -	<text name="OwnerLabel"> -		Dig: -	</text> -	<check_box label="Redigér" name="CheckOwnerModify"/> -	<check_box label="Kopiere" name="CheckOwnerCopy"/> -	<check_box label="Sælg" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel"> -		Enhver: -	</text> -	<check_box label="Kopiér" name="CheckEveryoneCopy"/> -	<text name="GroupLabel"> -		Gruppe: -	</text> -	<check_box label="Del" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel"> -		Næste ejer: -	</text> -	<check_box label="Redigér" name="CheckNextOwnerModify"/> -	<check_box label="Kopiere" name="CheckNextOwnerCopy"/> -	<check_box label="Sælg" name="CheckNextOwnerTransfer"/> -	<check_box label="Til salg" name="CheckPurchase"/> -	<combo_box name="combobox sale copy"> -		<combo_box.item label="Kopiér" name="Copy"/> -		<combo_box.item label="Original" name="Original"/> -	</combo_box> -	<spinner label="Pris:" name="Edit Cost"/> -	<text name="CurrencySymbol"> -		L$ -	</text> -</floater> diff --git a/indra/newview/skins/default/xui/de/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/de/floater_inventory_item_properties.xml deleted file mode 100644 index 92c038057f..0000000000 --- a/indra/newview/skins/default/xui/de/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="EIGENSCHAFTEN: INVENTAROBJEKT"> -	<floater.string name="unknown">(unbekannt)</floater.string> -	<floater.string name="public">(öffentlich)</floater.string> -	<floater.string name="you_can">Sie können:</floater.string> -	<floater.string name="owner_can">Eigentümer kann:</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">Name:</text> -	<text name="LabelItemDescTitle">Beschreibung:</text> -	<text name="LabelCreatorTitle">Ersteller:</text> -	<button label="Profil..." label_selected="" name="BtnCreator"/> -	<text name="LabelOwnerTitle">Eigentümer:</text> -	<button label="Profil..." label_selected="" name="BtnOwner"/> -	<text name="LabelAcquiredTitle">Erworben:</text> -	<text name="LabelAcquiredDate">Mittwoch, 24. Mai 2006, 12:50:46</text> -	<text name="OwnerLabel">Sie:</text> -	<check_box label="Bearbeiten" name="CheckOwnerModify"/> -	<check_box label="Kopieren" left_delta="85" name="CheckOwnerCopy"/> -	<check_box label="Wiederverkaufen" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel">Jeder:</text> -	<check_box label="Kopieren" name="CheckEveryoneCopy"/> -	<text name="GroupLabel">Gruppe:</text> -	<check_box label="Teilen" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel" width="150">Nächster Eigentümer:</text> -	<check_box label="Bearbeiten" name="CheckNextOwnerModify"/> -	<check_box label="Kopieren" left_delta="55" name="CheckNextOwnerCopy"/> -	<check_box label="Wiederverkaufen" name="CheckNextOwnerTransfer"/> -	<check_box label="Zum Verkauf" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Kopie" name="Copy"/> -		<combo_box.item label="Inhalt" name="Contents"/> -		<combo_box.item label="Original" name="Original"/> -	</combo_box> -	<spinner label="Preis:" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/de/panel_main_inventory.xml b/indra/newview/skins/default/xui/de/panel_main_inventory.xml index a3adea9fa2..175f6e1003 100644 --- a/indra/newview/skins/default/xui/de/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/de/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		Geholte [ITEM_COUNT] Bestellungen und [CATEGORY_COUNT] Ordner [FILTER]  	</panel.string> +    <panel.string name="inventory_title">INVENTAR</panel.string>  	<text name="ItemcountText">  		Objekte:  	</text> diff --git a/indra/newview/skins/default/xui/en/floater_change_item_thumbnail.xml b/indra/newview/skins/default/xui/en/floater_change_item_thumbnail.xml new file mode 100644 index 0000000000..726cb38481 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_change_item_thumbnail.xml @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_resize="false" + height="366" + layout="topleft" + name="change_item_thumbnail" + help_topic="change_item_thumbnail" + title="CHANGE ITEM IMAGE" + width="319"> + +  <floater.string +    name="title_item_thumbnail"> +    CHANGE ITEM IMAGE +  </floater.string> +  <floater.string +    name="title_folder_thumbnail"> +    CHANGE FOLDER IMAGE +  </floater.string> +  <floater.string +    name="tooltip_upload_local"> +    Upload from computer +  </floater.string> +  <floater.string +    name="tooltip_upload_snapshot"> +    Use snapshot tool +  </floater.string> +  <floater.string +    name="tooltip_use_texture"> +    Choose texture +  </floater.string> +  <floater.string +    name="tooltip_copy_to_clipboard"> +    Copy to clipboard +  </floater.string> +  <floater.string +    name="tooltip_paste_from_clipboard"> +    Paste from clipboard +  </floater.string> +  <floater.string +    name="tooltip_remove_image"> +    Remove image +  </floater.string> +   +  <icon +    follows="top|left" +    height="16" +    image_name="Inv_Object" +    layout="topleft" +    left="10" +    mouse_opaque="true" +    name="item_type_icon" +    top="4" +    width="16" /> +  <text +    name="item_name" +    font="SansSerif" +    use_ellipses="true" +    follows="left|top|right" +    layout="topleft" +    height="19" +    top_delta="1" +    left_pad="3" +    width="286"/> + +  <thumbnail +    name="item_thumbnail" +    fallback_image="Thumbnail_Fallback" +    follows="top|left" +    layout="topleft" +    left="32" +    top_pad="9" +    height="256" +    width="256" +        /> + +  <button +    follows="right|bottom" +    layout="topleft" +    image_hover_unselected="Toolbar_Middle_Over" +    image_overlay="Icon_File_Upload" +    image_selected="Toolbar_Middle_Selected" +    image_unselected="Toolbar_Middle_Off" +    name="upload_local" +    left="38" +    top_pad="9" +    height="32" +    width="32" /> +  <button +    follows="right|bottom" +    layout="topleft" +    image_hover_unselected="Toolbar_Middle_Over" +    image_overlay="Icon_Snapshot" +    image_selected="Toolbar_Middle_Selected" +    image_unselected="Toolbar_Middle_Off" +    name="upload_snapshot" +    left_pad="10" +    top_delta="0" +    height="32" +    width="32" /> +  <button +    follows="right|bottom" +    layout="topleft" +    image_hover_unselected="Toolbar_Middle_Over" +    image_overlay="Icon_Use_Texture" +    image_selected="Toolbar_Middle_Selected" +    image_unselected="Toolbar_Middle_Off" +    name="use_texture" +    left_pad="10" +    top_delta="0" +    height="32" +    width="32" /> +  <button +    follows="right|bottom" +    layout="topleft" +    image_hover_unselected="Toolbar_Middle_Over" +    image_overlay="Icon_Copy" +    image_selected="Toolbar_Middle_Selected" +    image_unselected="Toolbar_Middle_Off" +    name="copy_to_clipboard" +    left_pad="10" +    top_delta="0" +    height="32" +    width="32" /> +  <button +    follows="right|bottom" +    layout="topleft" +    image_hover_unselected="Toolbar_Middle_Over" +    image_overlay="Icon_Paste" +    image_selected="Toolbar_Middle_Selected" +    image_unselected="Toolbar_Middle_Off" +    name="paste_from_clipboard" +    left_pad="10" +    top_delta="0" +    height="32" +    width="32" /> +  <button +    follows="right|bottom" +    layout="topleft" +    image_hover_unselected="Toolbar_Middle_Over" +    image_overlay="Icon_Delete" +    image_selected="Toolbar_Middle_Selected" +    image_unselected="Toolbar_Middle_Off" +    name="remove_image" +    left_pad="10" +    top_delta="0" +    height="32" +    width="32" /> + +  <text +    type="string" +    halign="center" +    length="1" +    follows="left|top" +    height="17" +    layout="topleft" +    left="5" +    right="-5" +    name="tooltip_text" +    top_pad="12" +    width="78"> +    tooltip +  </text> +   +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_inventory_settings.xml b/indra/newview/skins/default/xui/en/floater_inventory_settings.xml new file mode 100644 index 0000000000..2e619d91fb --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_inventory_settings.xml @@ -0,0 +1,179 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_close="true" + can_minimize="true" + can_resize="false" + save_rect="true" + height="370" + width="370" + name="inventory_settings" + title="INVENTORY SETTINGS"> + <icon +  follows="top|left" +  height="18" +  image_name="Multi_Folder_Mode" +  layout="topleft" +  left="18" +  mouse_opaque="true" +  name="multi_folder_icon" +  top="25" +  width="18" /> + <text +   type="string" +   length="1" +   follows="left|top|right" +   height="13" +   layout="topleft" +   left_pad="12" +   top_delta="2" +   name="multi_folder_txt" +   font="SansSerifMedium" +   text_color="White" +   width="300"> +   Double-click on folder in multi-folder view: + </text> + <radio_group +  control_name="MultiModeDoubleClickFolder" +  follows="left|top" +  top_pad="8" +  layout="topleft" +  font="SansSerifMedium" +  left="60" +  width="300" +  height="70" +  name="multi_double_click_setting"> +     <radio_item +      height="20" +      label="Expands & collapses folder" +      label_text.text_color="White" +      follows="left|top" +      layout="topleft" +      name="0" +      width="200"/> +     <radio_item +      height="20" +      follows="left|top" +      label="Opens a new window" +      label_text.text_color="White" +      layout="topleft" +      left_delta="0" +      name="1" +      top_pad ="5" +      width="200" /> +     <radio_item +      height="20" +      follows="left|top" +      label="Switches view" +      label_text.text_color="White" +      layout="topleft" +      left_delta="0" +      name="2" +      top_pad ="5" +      width="200" /> + </radio_group> + <icon +  follows="top|left" +  height="18" +  image_name="Single_Folder_Mode" +  layout="topleft" +  left="18" +  mouse_opaque="true" +  name="single_folder_icon" +  top_pad="30" +  width="18" /> + <text +   type="string" +   length="1" +   follows="left|top|right" +   height="13" +   layout="topleft" +   left_pad="12" +   top_delta="2" +   name="single_folder_txt" +   font="SansSerifMedium" +   text_color="White" +   width="300"> +   Double-click on folder in single-folder view: + </text> + <radio_group +  control_name="SingleModeDoubleClickOpenWindow" +  follows="left|top" +  top_pad="8" +  layout="topleft" +  font="SansSerifMedium" +  left="60" +  width="300" +  height="45" +  name="single_double_click_setting"> +     <radio_item +      height="20" +      label="Stays in current window" +      label_text.text_color="White" +      follows="left|top" +      layout="topleft" +      name="false" +      width="200"/> +     <radio_item +      height="20" +      follows="left|top" +      label="Opens a new window" +      label_text.text_color="White" +      layout="topleft" +      left_delta="0" +      name="true" +      top_pad ="5" +      width="200" /> + </radio_group> + <text +   type="string" +   length="1" +   follows="left|top|right" +   height="13" +   layout="topleft" +   left="48" +   name="find_original_txt" +   font="SansSerifMedium" +   text_color="White" +   top_pad="30" +   width="300"> +   Clicking on "Show in inventory" or "Find original" + </text> + <radio_group +  control_name="FindOriginalOpenWindow" +  follows="left|top" +  top_pad="8" +  layout="topleft" +  font="SansSerifMedium" +  left="60" +  width="300" +  height="45" +  name="find_original_settings"> +     <radio_item +      height="20" +      label="Shows item in main inventory window" +      label_text.text_color="White" +      follows="left|top" +      layout="topleft" +      name="false" +      width="200"/> +     <radio_item +      height="20" +      follows="left|top" +      label="Opens a new single-folder window" +      label_text.text_color="White" +      layout="topleft" +      left_delta="0" +      name="true" +      top_pad ="5" +      width="200" /> + </radio_group> + <button +  height="20" +  label="OK" +  layout="topleft" +  left="140" +  bottom="-20" +  name="ok_btn" +  label_color="White" +  width="90" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_item_properties.xml b/indra/newview/skins/default/xui/en/floater_item_properties.xml index 0fc54a9c8b..336bb902ca 100644 --- a/indra/newview/skins/default/xui/en/floater_item_properties.xml +++ b/indra/newview/skins/default/xui/en/floater_item_properties.xml @@ -13,7 +13,7 @@     left="0"     class="sidepanel_item_info"     filename="sidepanel_item_info.xml" -   name="item_panel" +   name="sidepanel"     top="20"     label=""     height="570" diff --git a/indra/newview/skins/default/xui/en/floater_my_inventory.xml b/indra/newview/skins/default/xui/en/floater_my_inventory.xml index f182d27da8..a9900f05b7 100644 --- a/indra/newview/skins/default/xui/en/floater_my_inventory.xml +++ b/indra/newview/skins/default/xui/en/floater_my_inventory.xml @@ -5,14 +5,14 @@   can_resize="true"   height="570"   help_topic="sidebar_inventory" - min_width="333" - min_height="590" + min_width="363" + min_height="270"   name="floater_my_inventory"   save_rect="true"   save_visibility="true"   reuse_instance="true"   title="INVENTORY" - width="333" > + width="363" >     <panel      class="sidepanel_inventory"      name="main_panel" diff --git a/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml new file mode 100644 index 0000000000..5f0eeab71c --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_new_feature_notification.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + height="130" + width="300" + layout="topleft" + name="floater_new_feature_notification" + title="NEW FEATURE" + show_title="false" + header_height="0" + bg_opaque_image="Window_NoTitle_Foreground" + bg_alpha_image="Window_NoTitle_Background" + can_resize="false" + can_drag_on_left="false" + can_minimize="false" + can_close="false"> +    <floater.string name="title_txt_inventory"> +New inventory features +    </floater.string> +    <floater.string name="description_txt_inventory"> +You can now add preview images to inventory items and view a folder in its own window. +Learn more in this [https://community.secondlife.com/blogs/entry/13637-new-features-inventory-item-preview-and-single-folder-view/ blogpost] +    </floater.string> +  <text +   type="string" +   length="1" +   follows="top|left|right" +   font="SansSerifLargeBold" +   text_color="White" +   layout="topleft" +   left="10" +   height="14" +   top="10" +   right="-10" +   name="title_txt"> +New feature +  </text> +  <text +   type="string" +   length="1" +   follows="top|left|right" +   text_color="White" +   layout="topleft" +   left="10" +   height="40" +   top_pad="14" +   right="-10" +   word_wrap="true" +   name="description_txt"> +Feature description +  </text> +  <button +   follows="bottom|left|right" +   layout="topleft" +   height="24" +   label="Got it!" +   left="104" +   bottom="-10" +   name="close_btn" +   width="90"/> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_outfit_photo_preview.xml b/indra/newview/skins/default/xui/en/floater_outfit_photo_preview.xml deleted file mode 100644 index bfc1c39e9d..0000000000 --- a/indra/newview/skins/default/xui/en/floater_outfit_photo_preview.xml +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<floater - legacy_header_height="18" - can_resize="false" - height="325" - layout="topleft" - name="outfit_photo_preview" - help_topic="preview_texture" - width="410"> -   <floater.string -     name="Title"> -        Texture: [NAME] -    </floater.string> -    <floater.string -     name="exceed_limits"> -        Max outfit photo size is [MAX_WIDTH]*[MAX_HEIGHT]. Please select another texture. -    </floater.string> -    <floater.string -     name="photo_confirmation"> -         Set this as Outfit Photo for [OUTFIT]? -    </floater.string> -    <text -     type="string" -     halign="right" -     length="1" -     follows="right|bottom" -     height="16" -     layout="topleft" -     left="110" -     name="dimensions" -     top="255" -     width="200"> -        [WIDTH]px x [HEIGHT]px -    </text> -    <text -     type="string" -     follows="left|top" -     height="16" -     layout="topleft" -     name="notification" -     left="25" -     halign="center" -     top_pad="5" -     width="360"> -    </text> -    <button -     follows="right|bottom" -     height="22" -     label="OK" -     layout="topleft" -     name="ok_btn" -     top_pad="5" -     right="-115" -     top_delta="0" -     width="90" /> -    <button -     follows="right|bottom" -     height="22" -     label="Cancel" -     layout="topleft" -     name="cancel_btn" -     right="-20" -     top_delta="0" -     width="90" /> -</floater> diff --git a/indra/newview/skins/default/xui/en/floater_simple_outfit_snapshot.xml b/indra/newview/skins/default/xui/en/floater_simple_snapshot.xml index 5ece7b85d5..484ad159d1 100644 --- a/indra/newview/skins/default/xui/en/floater_simple_outfit_snapshot.xml +++ b/indra/newview/skins/default/xui/en/floater_simple_snapshot.xml @@ -2,17 +2,17 @@  <floater   positioning="cascading"   legacy_header_height="18" - can_minimize="true" + can_minimize="false"   can_resize="false"   can_close="true"   height="305"   layout="topleft" - name="simple_outfit_snapshot" - single_instance="true" + name="simple_snapshot" + single_instance="false"   help_topic="snapshot"   save_rect="true"   save_visibility="false" - title="OUTFIT SNAPSHOT" + title="ITEM SNAPSHOT"   width="351">    <ui_ctrl     layout="topleft" @@ -36,7 +36,7 @@     height="22"     layout="topleft"     left_pad="10" -   label="Save (L$[UPLOAD_COST])" +   label="Save"     name="save_btn"     width="90" />    <button diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml index f441e3cbd7..fcd24d83bb 100644 --- a/indra/newview/skins/default/xui/en/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml @@ -440,7 +440,7 @@      length="1"      halign="right"      name="360_label" -    text_color="0.3 0.82 1 1" +    text_color="HTMLLinkColor"      top_delta="0"      type="string"      width="115"> diff --git a/indra/newview/skins/default/xui/en/floater_task_properties.xml b/indra/newview/skins/default/xui/en/floater_task_properties.xml new file mode 100644 index 0000000000..56c236eab4 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_task_properties.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + legacy_header_height="18" + height="590" + layout="topleft" + name="Task Properties" + help_topic="item+properties" + title="ITEM PROPERTIES" + single_instance="true" + width="330"> + <panel +   follows="all" +   layout="topleft" +   left="0" +   class="sidepanel_task_info" +   filename="sidepanel_task_info.xml" +   name="sidepanel" +   top="20" +   label="" +   height="570" +   visible="true" +   width="330"> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml index 3a66911389..8081af6673 100644 --- a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml @@ -83,6 +83,22 @@       top_pad="4">          [DIMENSIONS]      </text> +    <text +     type="string" +     text_color="Yellow" +     length="1" +     word_wrap="true" +     follows="left|top" +     height="56" +     width="164" +     layout="topleft" +     left="8" +     name="over_limit_lbl" +     visible="false" +     top_delta="0"> +        Selected texture is [TEXDIM]. Inventory image must be square, no less than [MINTEXDIM]. +    </text> +      <!--  middle: inventory mode -->      <button diff --git a/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml new file mode 100644 index 0000000000..d82c453e5f --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_gallery_inventory.xml @@ -0,0 +1,515 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<context_menu +    layout="topleft" +    name="Gallery"> +    <menu_item_call +     label="Share" +     layout="topleft" +     name="Share"> +        <menu_item_call.on_click +         function="Inventory.Share" /> +    </menu_item_call> +    <menu_item_call +     label="Empty Trash" +     layout="topleft" +     name="Empty Trash"> +        <menu_item_call.on_click +         function="Inventory.EmptyTrash"/> +    </menu_item_call> +    <menu_item_call +     label="Empty Lost And Found" +     layout="topleft" +     name="Empty Lost And Found"> +        <menu_item_call.on_click +         function="Inventory.EmptyLostAndFound"/> +    </menu_item_call> +    <menu_item_call +     label="Teleport" +     layout="topleft" +     name="Landmark Open"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="open" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft" +     name="Folder Wearables Separator" /> +    <menu_item_call +     label="Replace Current Outfit" +     layout="topleft" +     name="Replace Outfit"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="replaceoutfit" /> +    </menu_item_call> +    <menu_item_call +     label="Add To Current Outfit" +     layout="topleft" +     name="Add To Outfit"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="addtooutfit" /> +    </menu_item_call> +    <menu_item_call +     label="Remove From Current Outfit" +     layout="topleft" +     name="Remove From Outfit"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="removefromoutfit" /> +    </menu_item_call> +    <menu_item_call +     label="Copy outfit list to clipboard" +     layout="topleft" +     name="Copy outfit list to clipboard"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="copyoutfittoclipboard" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft" +     name="Outfit Separator" /> +    <menu_item_call +     label="Find Original" +     layout="topleft" +     name="Find Original"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="goto" /> +    </menu_item_call> +    <menu_item_call +     label="Purge Item" +     layout="topleft" +     name="Purge Item"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="purge"/> +    </menu_item_call> +    <menu_item_call +     label="Restore Item" +     layout="topleft" +     name="Restore Item"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="restore" /> +    </menu_item_call> +    <menu_item_call +     label="Open" +     layout="topleft" +     name="Open"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="open" /> +    </menu_item_call> +    <menu_item_call +     label="Open Original" +     layout="topleft" +     name="Open Original"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="open_original" /> +    </menu_item_call> +    <menu_item_call +     label="Properties" +     layout="topleft" +     name="Properties"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="properties" /> +    </menu_item_call> +    <menu_item_call +     label="Image..." +     layout="topleft" +     name="thumbnail"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="thumbnail" /> +    </menu_item_call> +    <menu_item_call +     label="Copy Asset UUID" +     layout="topleft" +     name="Copy Asset UUID"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="copy_uuid" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Copy Separator" /> +    <menu_item_call +     label="Open" +     layout="topleft" +     name="open_in_current_window"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="open_selected_folder" /> +    </menu_item_call> +    <menu_item_call +     label="Open in new window" +     layout="topleft" +     name="open_in_new_window"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="open_in_new_window" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft" +     name="Open Folder Separator" /> +    <menu_item_call +     label="Rename" +     layout="topleft" +     name="Rename"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="rename" /> +    </menu_item_call> +    <menu_item_call +     label="Cut" +     layout="topleft" +     name="Cut"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="cut" /> +    </menu_item_call> +    <menu_item_call +     label="Copy" +     layout="topleft" +     name="Copy"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste" +     layout="topleft" +     name="Paste"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="paste" /> +    </menu_item_call> +    <menu_item_call +     label="Paste As Link" +     layout="topleft" +     name="Paste As Link"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="paste_link" /> +    </menu_item_call> +    <menu_item_call +     label="Replace Links" +     layout="topleft" +     name="Replace Links"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="replace_links" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Paste Separator" /> +    <menu_item_call +     label="Delete" +     layout="topleft" +     name="Delete"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="delete" /> +    </menu_item_call> +    <menu_item_call +     label="Delete System Folder" +     layout="topleft" +     name="Delete System Folder"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="delete_system_folder" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft" /> +    <menu_item_separator +     layout="topleft" /> +    <menu_item_call +     label="Play" +     layout="topleft" +     name="Sound Play"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="sound_play" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft" +     name="Landmark Separator" /> +    <menu_item_call +    label="Copy SLurl" +    layout="topleft" +    name="url_copy"> +       <menu_item_call.on_click +        function="Inventory.DoToSelected" +        parameter="copy_slurl" /> +   </menu_item_call>  +   <menu_item_call +     label="About Landmark" +     layout="topleft" +     name="About Landmark"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="about" /> +    </menu_item_call> +   <menu_item_call +     label="Show on Map" +     layout="topleft" +     name="show_on_map"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="show_on_map" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Animation Separator" /> +    <menu_item_call +     label="Play Inworld" +     layout="topleft" +     name="Animation Play"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="playworld" /> +    </menu_item_call> +    <menu_item_call +     label="Play Locally" +     layout="topleft" +     name="Animation Audition"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="playlocal" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Send Instant Message Separator" /> +    <menu_item_call +     label="Send Instant Message" +     layout="topleft" +     name="Send Instant Message"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="begin_im" /> +    </menu_item_call> +    <menu_item_call +     label="Offer Teleport..." +     layout="topleft" +     name="Offer Teleport..."> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="lure" /> +    </menu_item_call> +    <menu_item_call +     label="Request Teleport..." +     layout="topleft" +     name="Request Teleport..."> +        <menu_item_call.on_click +        function="Inventory.DoToSelected" +        parameter="request_lure" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Gesture Separator" /> +    <menu_item_call +     label="Activate" +     layout="topleft" +     name="Activate"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="activate" /> +    </menu_item_call> +    <menu_item_call +     label="Deactivate" +     layout="topleft" +     name="Deactivate"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="deactivate" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Texture Separator" /> +    <menu_item_call +     label="Save As" +     layout="topleft" +     name="Save As"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="save_as" /> +    </menu_item_call> +  <menu_item_call + label="Save Selected As" + layout="topleft" + name="Save Selected As"> +    <menu_item_call.on_click +     function="Inventory.DoToSelected" +     parameter="save_selected_as" /> +  </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Wearable And Object Separator"/> +    <menu_item_call +     label="Wear" +     layout="topleft" +     name="Wearable And Object Wear"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="wear" /> +    </menu_item_call> +    <menu +     label="Attach To" +     layout="topleft" +     name="Attach To" /> +    <menu +     label="Attach To HUD" +     layout="topleft" +     name="Attach To HUD" /> +    <menu_item_call +     label="Touch" +     layout="topleft" +     name="Attachment Touch"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="touch" /> +    </menu_item_call> +    <menu_item_call +     label="Edit" +     layout="topleft" +     name="Wearable Edit"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="edit" /> +    </menu_item_call> +    <menu_item_call +     label="Add" +     layout="topleft" +     name="Wearable Add"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="wear_add" /> +    </menu_item_call> +    <menu_item_call +     label="Detach From Yourself" +     layout="topleft" +     name="Detach From Yourself"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="detach" /> +    </menu_item_call> +    <menu_item_call +     label="Take Off" +     layout="topleft" +     name="Take Off"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="take_off" /> +    </menu_item_call> +    <menu_item_separator +     layout="topleft"  +     name="Settings Separator" /> +    <menu_item_call +            name="Settings Apply Local" +            layout="topleft" +            label="Apply Only To Myself"> +        <menu_item_call.on_click  +                function="Inventory.DoToSelected" +                parameter="apply_settings_local" /> +    </menu_item_call> +    <menu_item_call +            name="Settings Apply Parcel" +            layout="topleft" +            label="Apply To Parcel"> +        <menu_item_call.on_click  +                function="Inventory.DoToSelected" +                parameter="apply_settings_parcel" /> +    </menu_item_call> +  <menu_item_separator +   layout="topleft" +   name="Subfolder Separator" /> +  <menu_item_call +   label="Create folder from selected" +   layout="topleft" +   name="New folder from selected"> +    <menu_item_call.on_click +     function="Inventory.DoToSelected" +     parameter="new_folder_from_selected" /> +  </menu_item_call> +  <menu_item_call +   label="Ungroup folder items" +   layout="topleft" +   name="Ungroup folder items"> +    <menu_item_call.on_click +     function="Inventory.DoToSelected" +     parameter="ungroup_folder_items" /> +  </menu_item_call> +  <menu +   label="Use as default for" +   layout="topleft" +   name="upload_def"> +    <menu_item_call +     label="Image uploads" +     layout="topleft" +     name="Image uploads"> +      <menu_item_call.on_click +       function="Inventory.FileUploadLocation" +        parameter="texture" /> +      <menu_item_call.on_visible +       function="Inventory.CanSetUploadLocation" /> +    </menu_item_call> +    <menu_item_call +     label="Sound uploads" +     layout="topleft" +     name="Sound uploads"> +      <menu_item_call.on_click +       function="Inventory.FileUploadLocation" +       parameter="sound" /> +      <menu_item_call.on_visible +       function="Inventory.CanSetUploadLocation" /> +    </menu_item_call> +    <menu_item_call +     label="Animation uploads" +     layout="topleft" +     name="Animation uploads"> +      <menu_item_call.on_click +       function="Inventory.FileUploadLocation" +       parameter="animation" /> +      <menu_item_call.on_visible +       function="Inventory.CanSetUploadLocation" /> +    </menu_item_call> +    <menu_item_call +     label="Model uploads" +     layout="topleft" +     name="Model uploads"> +      <menu_item_call.on_click +       function="Inventory.FileUploadLocation" +       parameter="model" /> +      <menu_item_call.on_visible +       function="Inventory.CanSetUploadLocation" /> +    </menu_item_call> +  </menu> +	<menu_item_separator +	 layout="topleft" +	 name="Marketplace Separator" /> +	<menu_item_call +        label="Copy to Marketplace Listings" +        layout="topleft" +        name="Marketplace Copy"> +		<menu_item_call.on_click +        function="Inventory.DoToSelected" +        parameter="copy_to_marketplace_listings" /> +	</menu_item_call> +	<menu_item_call +        label="Move to Marketplace Listings" +        layout="topleft" +        name="Marketplace Move"> +		<menu_item_call.on_click +        function="Inventory.DoToSelected" +        parameter="move_to_marketplace_listings" /> +	</menu_item_call> +	<menu_item_call +     label="--no options--" +     layout="topleft" +     name="--no options--" /> +    <menu_item_separator +     layout="topleft" /> +</context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml index 99ca910062..0ca505dd5d 100755 --- a/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml +++ b/indra/newview/skins/default/xui/en/menu_gallery_outfit_tab.xml @@ -42,35 +42,11 @@          parameter="take_off" />      </menu_item_call>      <menu_item_call -        label="Upload Photo (L$[UPLOAD_COST])" -        layout="topleft" -        name="upload_photo"> -        <on_click -        function="Outfit.UploadPhoto" /> -    </menu_item_call> -    <menu_item_call -        label="Select Photo" -        layout="topleft" -        name="select_photo"> +     label="Image..." +     layout="topleft" +     name="thumbnail">          <on_click -        function="Outfit.SelectPhoto" /> -    </menu_item_call> -    <menu_item_call -        label="Take a Snapshot" -        layout="topleft" -        name="take_snapshot"> -        <on_click -        function="Outfit.TakeSnapshot" /> -    </menu_item_call> -    <menu_item_call -        label="Remove Photo" -        layout="topleft" -        name="remove_photo"> -        <on_click -        function="Outfit.RemovePhoto" /> -        <on_visible -        function="Outfit.OnVisible" -        parameter="remove_photo" /> +         function="Outfit.Thumbnail" />      </menu_item_call>      <menu_item_separator name="sepatator1" />      <menu diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index aa3d0ae071..e650c10603 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -159,248 +159,6 @@           function="Inventory.DoCreate"           parameter="outfit" />      </menu_item_call> -    <menu_item_call -     label="New Script" -     layout="topleft" -     name="New Script"> -        <menu_item_call.on_click -         function="Inventory.DoCreate" -         parameter="lsl" /> -    </menu_item_call> -    <menu_item_call -     label="New Notecard" -     layout="topleft" -     name="New Note"> -        <menu_item_call.on_click -         function="Inventory.DoCreate" -         parameter="notecard" /> -    </menu_item_call> -    <menu_item_call -     label="New Gesture" -     layout="topleft" -     name="New Gesture"> -        <menu_item_call.on_click -         function="Inventory.DoCreate" -         parameter="gesture" /> -    </menu_item_call> -    <menu -     label="New Clothes" -     layout="topleft" -     name="New Clothes"> -        <menu_item_call -         label="New Shirt" -         layout="topleft" -         name="New Shirt"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="shirt" /> -        </menu_item_call> -        <menu_item_call -         label="New Pants" -         layout="topleft" -         name="New Pants"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="pants" /> -        </menu_item_call> -        <menu_item_call -         label="New Shoes" -         layout="topleft" -         name="New Shoes"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="shoes" /> -        </menu_item_call> -        <menu_item_call -         label="New Socks" -         layout="topleft" -         name="New Socks"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="socks" /> -        </menu_item_call> -        <menu_item_call -         label="New Jacket" -         layout="topleft" -         name="New Jacket"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="jacket" /> -        </menu_item_call> -        <menu_item_call -         label="New Skirt" -         layout="topleft" -         name="New Skirt"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="skirt" /> -        </menu_item_call> -        <menu_item_call -         label="New Gloves" -         layout="topleft" -         name="New Gloves"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="gloves" /> -        </menu_item_call> -        <menu_item_call -         label="New Undershirt" -         layout="topleft" -         name="New Undershirt"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="undershirt" /> -        </menu_item_call> -        <menu_item_call -         label="New Underpants" -         layout="topleft" -         name="New Underpants"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="underpants" /> -        </menu_item_call> -        <menu_item_call -         label="New Alpha Mask" -         layout="topleft" -         name="New Alpha Mask"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="alpha" /> -        </menu_item_call> -        <menu_item_call -         label="New Tattoo" -         layout="topleft" -         name="New Tattoo"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="tattoo" /> -        </menu_item_call> -        <menu_item_call -         label="New Universal" -         layout="topleft" -         name="New Universal"> -        <menu_item_call.on_click -         function="Inventory.DoCreate" -         parameter="universal" /> -      </menu_item_call> -        <menu_item_call -         label="New Physics" -         layout="topleft" -         name="New Physics"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="physics" /> -        </menu_item_call> -    </menu> -    <menu -     label="New Body Parts" -     layout="topleft" -     name="New Body Parts"> -        <menu_item_call -         label="New Shape" -         layout="topleft" -         name="New Shape"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="shape" /> -        </menu_item_call> -        <menu_item_call -         label="New Skin" -         layout="topleft" -         name="New Skin"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="skin" /> -        </menu_item_call> -        <menu_item_call -         label="New Hair" -         layout="topleft" -         name="New Hair"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="hair" /> -        </menu_item_call> -        <menu_item_call -         label="New Eyes" -         layout="topleft" -         name="New Eyes"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="eyes" /> -        </menu_item_call> -    </menu> -    <menu -     label="New Settings" -     layout="topleft" -     name="New Settings"> -        <menu_item_call -         label="New Sky" -         layout="topleft" -         name="New Sky"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="sky"/> -            <menu_item_call.on_enable -                    function="Inventory.EnvironmentEnabled" /> -        </menu_item_call> -        <menu_item_call -         label="New Water" -         layout="topleft" -         name="New Water"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="water"/> -            <menu_item_call.on_enable -                    function="Inventory.EnvironmentEnabled" /> -        </menu_item_call> -        <menu_item_call -         label="New Day Cycle" -         layout="topleft" -         name="New Day Cycle"> -            <menu_item_call.on_click -             function="Inventory.DoCreate" -             parameter="daycycle"/> -            <menu_item_call.on_enable -                    function="Inventory.EnvironmentEnabled" /> -        </menu_item_call> -    </menu> -    <menu -     label="Use as default for" -     layout="topleft" -     name="upload_def"> -      <menu_item_call -       label="Image uploads" -       layout="topleft" -       name="Image uploads"> -        <menu_item_call.on_click -         function="Inventory.FileUploadLocation" -         parameter="texture" /> -      </menu_item_call> -      <menu_item_call -       label="Sound uploads" -       layout="topleft" -       name="Sound uploads"> -        <menu_item_call.on_click -         function="Inventory.FileUploadLocation" -         parameter="sound" /> -      </menu_item_call> -      <menu_item_call -       label="Animation uploads" -       layout="topleft" -       name="Animation uploads"> -        <menu_item_call.on_click -         function="Inventory.FileUploadLocation" -         parameter="animation" /> -      </menu_item_call> -      <menu_item_call -       label="Model uploads" -       layout="topleft" -       name="Model uploads"> -        <menu_item_call.on_click -         function="Inventory.FileUploadLocation" -         parameter="model" /> -      </menu_item_call> -    </menu>      <menu       label="Change Type"       layout="topleft" @@ -597,12 +355,12 @@           parameter="properties" />      </menu_item_call>      <menu_item_call -     label="Rename" +     label="Image..."       layout="topleft" -     name="Rename"> +     name="thumbnail">          <menu_item_call.on_click           function="Inventory.DoToSelected" -         parameter="rename" /> +         parameter="thumbnail" />      </menu_item_call>      <menu_item_call       label="Copy Asset UUID" @@ -624,6 +382,32 @@       layout="topleft"        name="Copy Separator" />      <menu_item_call +     label="Open" +     layout="topleft" +     name="open_in_current_window"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="open_in_current_window" /> +    </menu_item_call> +    <menu_item_call +     label="Open in new window" +     layout="topleft" +     name="open_in_new_window"> +        <menu_item_call.on_click +         function="Inventory.OpenNewFolderWindow"/> +    </menu_item_call> +    <menu_item_separator +     layout="topleft" +     name="Open Folder Separator" /> +    <menu_item_call +     label="Rename" +     layout="topleft" +     name="Rename"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="rename" /> +    </menu_item_call> +    <menu_item_call       label="Cut"       layout="topleft"       name="Cut"> @@ -931,6 +715,43 @@       function="Inventory.DoToSelected"       parameter="ungroup_folder_items" />    </menu_item_call> +  <menu +   label="Use as default for" +   layout="topleft" +   name="upload_def"> +    <menu_item_call +     label="Image uploads" +     layout="topleft" +     name="Image uploads"> +      <menu_item_call.on_click +       function="Inventory.FileUploadLocation" +        parameter="texture" /> +    </menu_item_call> +    <menu_item_call +     label="Sound uploads" +     layout="topleft" +     name="Sound uploads"> +    <menu_item_call.on_click +     function="Inventory.FileUploadLocation" +     parameter="sound" /> +    </menu_item_call> +    <menu_item_call +     label="Animation uploads" +     layout="topleft" +     name="Animation uploads"> +    <menu_item_call.on_click +     function="Inventory.FileUploadLocation" +     parameter="animation" /> +    </menu_item_call> +    <menu_item_call +     label="Model uploads" +     layout="topleft" +     name="Model uploads"> +    <menu_item_call.on_click +     function="Inventory.FileUploadLocation" +     parameter="model" /> +    </menu_item_call> +  </menu>  	<menu_item_separator  	 layout="topleft"  	 name="Marketplace Separator" /> diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml index 3385a29a6c..12ba121d3c 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory_add.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml @@ -290,4 +290,13 @@                      function="Inventory.EnvironmentEnabled" />          </menu_item_call>      </menu> -</menu>
\ No newline at end of file +    <menu_item_separator/> +    <menu_item_call +     label="Shop..." +     layout="topleft" +     name="Shop"> +        <menu_item_call.on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="shop" /> +    </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml index 3eacdbc781..2c630880c2 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml @@ -15,52 +15,25 @@           function="Inventory.GearDefault.Custom.Action"           parameter="new_window" />      </menu_item_call> -    <menu_item_separator -     layout="topleft" /> -    <menu_item_check -     label="Sort by Name" -     layout="topleft" -     name="sort_by_name"> -        <on_click -         function="Inventory.GearDefault.Custom.Action" -         parameter="sort_by_name" /> -        <on_check -         function="Inventory.GearDefault.Check" -         parameter="sort_by_name" /> -    </menu_item_check> -    <menu_item_check -     label="Sort by Most Recent" -     layout="topleft" -     name="sort_by_recent"> -        <on_click -         function="Inventory.GearDefault.Custom.Action" -         parameter="sort_by_recent" /> -        <on_check -         function="Inventory.GearDefault.Check" -         parameter="sort_by_recent" />          -    </menu_item_check> -    <menu_item_check -     label="Sort Folders Always by Name" +    <menu_item_call +     label="Collapse All Folders"       layout="topleft" -     name="sort_folders_by_name"> +     name="close_folders">          <on_click           function="Inventory.GearDefault.Custom.Action" -         parameter="sort_folders_by_name" /> -        <on_check -         function="Inventory.GearDefault.Check" -         parameter="sort_folders_by_name" /> -    </menu_item_check> -    <menu_item_check -     label="Sort System Folders to Top" +         parameter="close_folders" /> +        <on_visible +         function="Inventory.GearDefault.Visible" +         parameter="multi_folder_view" /> +    </menu_item_call> +    <menu_item_call +     label="Close All Windows"       layout="topleft" -     name="sort_system_folders_to_top"> +     name="close_windows">          <on_click           function="Inventory.GearDefault.Custom.Action" -         parameter="sort_system_folders_to_top" /> -        <on_check -         function="Inventory.GearDefault.Check" -         parameter="sort_system_folders_to_top" /> -    </menu_item_check> +         parameter="close_inv_windows" /> +    </menu_item_call>      <menu_item_separator       layout="topleft" />      <menu_item_call @@ -79,29 +52,6 @@           function="Inventory.GearDefault.Custom.Action"           parameter="reset_filters" />      </menu_item_call> -    <menu_item_call -     label="Close All Folders" -     layout="topleft" -     name="close_folders"> -        <on_click -         function="Inventory.GearDefault.Custom.Action" -         parameter="close_folders" /> -    </menu_item_call> -    <menu_item_separator -     layout="topleft" /> -    <menu_item_call -     label="Empty Lost and Found" -     layout="topleft" -     name="empty_lostnfound"> -        <on_click -         function="Inventory.GearDefault.Custom.Action" -         parameter="empty_lostnfound" /> -        <on_enable -         function="Inventory.GearDefault.Enable" -         parameter="empty_lostnfound" /> -    </menu_item_call> -    <menu_item_separator -     layout="topleft" />      <menu_item_call            label="Save Texture As"           layout="topleft" @@ -119,7 +69,8 @@       name="Share"       visible="true">       <on_click -         function="Inventory.Share" /> +         function="Inventory.GearDefault.Custom.Action" +         parameter="share" />       <on_enable           function="Inventory.GearDefault.Enable"           parameter="share" /> @@ -154,9 +105,25 @@               function="Inventory.GearDefault.Custom.Action"               parameter="replace_links" />          </menu_item_call> -    <menu_item_separator -     layout="topleft" /> - +    <menu_item_separator> +        <menu_item_separator.on_visible +         function="Inventory.GearDefault.Visible" +         parameter="multi_folder_view" /> +    </menu_item_separator> +    <menu_item_call +     label="Empty Lost and Found" +     layout="topleft" +     name="empty_lostnfound"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="empty_lostnfound" /> +        <on_enable +         function="Inventory.GearDefault.Enable" +         parameter="empty_lostnfound" /> +        <on_visible +         function="Inventory.GearDefault.Visible" +         parameter="multi_folder_view" /> +    </menu_item_call>      <menu_item_call       label="Empty Trash"       layout="topleft" @@ -167,5 +134,8 @@          <on_enable           function="Inventory.GearDefault.Enable"           parameter="empty_trash" /> +        <on_visible +         function="Inventory.GearDefault.Visible" +         parameter="multi_folder_view" />      </menu_item_call>  </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml b/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml index 46193f4a7a..8e34f52f3a 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory_search_visibility.xml @@ -7,6 +7,17 @@   name="menu_search_visibility"   visible="false">    <menu_item_check +   label="Search outfit folders" +   layout="topleft" +   name="search_outfits"> +    <on_click +     function="Inventory.GearDefault.Custom.Action" +     parameter="toggle_search_outfits" /> +    <on_check +     function="Inventory.GearDefault.Check" +     parameter="toggle_search_outfits" /> +  </menu_item_check> +  <menu_item_check     label="Search Trash"     layout="topleft"     name="search_trash"> diff --git a/indra/newview/skins/default/xui/en/menu_inventory_view_default.xml b/indra/newview/skins/default/xui/en/menu_inventory_view_default.xml new file mode 100644 index 0000000000..c7f9822e41 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_inventory_view_default.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + bottom="806" + layout="topleft" + left="0" + mouse_opaque="false" + name="menu_view_default" + visible="false"> +    <menu_item_check +     label="Sort by Name" +     layout="topleft" +     name="sort_by_name"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="sort_by_name" /> +        <on_check +         function="Inventory.GearDefault.Check" +         parameter="sort_by_name" /> +    </menu_item_check> +    <menu_item_check +     label="Sort by Most Recent" +     layout="topleft" +     name="sort_by_recent"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="sort_by_recent" /> +        <on_check +         function="Inventory.GearDefault.Check" +         parameter="sort_by_recent" />          +    </menu_item_check> +    <menu_item_check +     label="Sort Folders Always by Name" +     layout="topleft" +     name="sort_folders_by_name"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="sort_folders_by_name" /> +        <on_check +         function="Inventory.GearDefault.Check" +         parameter="sort_folders_by_name" /> +    </menu_item_check> +    <menu_item_check +     label="Sort System Folders to Top" +     layout="topleft" +     name="sort_system_folders_to_top"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="sort_system_folders_to_top" /> +        <on_check +         function="Inventory.GearDefault.Check" +         parameter="sort_system_folders_to_top" /> +        <on_visible +         function="Inventory.GearDefault.Visible" +         parameter="multi_folder_view" /> +    </menu_item_check> +    <menu_item_separator> +        <menu_item_separator.on_visible +         function="Inventory.GearDefault.Visible" +         parameter="single_folder_view" /> +    </menu_item_separator> +    <menu_item_check +     label="List view" +     layout="topleft" +     name="list_view"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="list_view" /> +        <on_check +         function="Inventory.GearDefault.Check" +         parameter="list_view" /> +        <on_visible +         function="Inventory.GearDefault.Visible" +         parameter="single_folder_view" /> +    </menu_item_check> +    <menu_item_check +     label="Gallery view" +     layout="topleft" +     name="gallery_view"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="gallery_view" /> +        <on_check +         function="Inventory.GearDefault.Check" +         parameter="gallery_view" /> +        <on_visible +         function="Inventory.GearDefault.Visible" +         parameter="single_folder_view" /> +    </menu_item_check> +    <menu_item_check +     label="Combination view" +     layout="topleft" +     name="combination_view"> +        <on_click +         function="Inventory.GearDefault.Custom.Action" +         parameter="combination_view" /> +        <on_check +         function="Inventory.GearDefault.Check" +         parameter="combination_view" /> +        <on_visible +         function="Inventory.GearDefault.Visible" +         parameter="single_folder_view" /> +    </menu_item_check> +    <menu_item_separator/> +    <menu_item_check +     label="Inventory settings..." +     name="inv_settings"> +        <menu_item_check.on_check +         function="Floater.Visible" +         parameter="inventory_settings" /> +        <menu_item_check.on_click +         function="Floater.Toggle" +         parameter="inventory_settings" /> +    </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml index 32d9d28434..e216962d12 100644 --- a/indra/newview/skins/default/xui/en/menu_outfit_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_outfit_gear.xml @@ -40,32 +40,11 @@           parameter="take_off" />      </menu_item_call>      <menu_item_call -     label="Upload Photo (L$[UPLOAD_COST])" +     label="Image..."       layout="topleft" -     name="upload_photo"> +     name="thumbnail">          <on_click -         function="Gear.UploadPhoto" /> -    </menu_item_call> -    <menu_item_call -     label="Select Photo" -     layout="topleft" -     name="select_photo"> -        <on_click -         function="Gear.SelectPhoto" /> -    </menu_item_call> -    <menu_item_call -     label="Take a Snapshot" -     layout="topleft" -     name="take_snapshot"> -        <on_click -         function="Gear.TakeSnapshot" /> -    </menu_item_call> -    <menu_item_call -     label="Remove Photo" -     layout="topleft" -     name="remove_photo"> -        <on_click -         function="Gear.RemovePhoto" /> +         function="Gear.Thumbnail" />      </menu_item_call>    <menu_item_separator name="sepatator1" />              <!-- copied (with minor modifications) from menu_inventory_add.xml --> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index a216ef7c0d..204fead7e0 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -3284,6 +3284,30 @@ See https://wiki.secondlife.com/wiki/Adding_Spelling_Dictionaries    <notification     icon="alertmodal.tga" +   label="Rename Selected Item" +   name="RenameItem" +   type="alertmodal"> +    Choose a new name for: +[NAME] +   <tag>confirm</tag> +    <form name="form"> +    <input name="new_name" type="text" width="300"> +      [NAME] +    </input> +    <button +     default="true" +     index="0" +     name="OK" +     text="OK"/> +    <button +     index="1" +     name="Cancel" +     text="Cancel"/> +   </form> +  </notification> + +  <notification +   icon="alertmodal.tga"     name="RemoveFromFriends"     type="alertmodal">      <tag>friendship</tag> @@ -6129,7 +6153,54 @@ Are you sure you want to delete them?       notext="Cancel"       yestext="OK"/>    </notification> -   + +  <notification +   icon="alertmodal.tga" +   name="DeleteThumbnail" +   type="alertmodal"> +    <unique/> +    Delete the image for this item? There is no undo. +    <tag>confirm</tag> +    <usetemplate +     ignoretext="Don't show me this again" +     name="okcancelignore" +     notext="Cancel" +     yestext="Delete"/> +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="ThumbnailDimentionsLimit" +   type="alertmodal"> +    <unique/> +    Only square images from 64 to 256 pixels per side are allowed. +    <usetemplate +     name="okbutton" +     yestext="OK"/> +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="ThumbnailInsufficientPermissions" +   type="alertmodal"> +    <unique/> +    Only copy and transfer free images can be assigned as thumbnails. +    <usetemplate +     name="okbutton" +     yestext="OK"/> +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="ThumbnailOutfitPhoto" +   type="alertmodal"> +    <unique/> +    To add an image to an outfit, use the Outfit Gallery window, or right-click on the outfit folder and select "Image..." +    <usetemplate +     name="okbutton" +     yestext="OK"/> +  </notification> +    <notification       icon="alertmodal.tga"       name="ConfirmUnlink" @@ -6379,6 +6450,22 @@ Your inventory is experiencing issues. Please, contact support.    <notification     icon="alertmodal.tga" +   name="InventoryLimitReachedAISAlert" +   type="alertmodal"> +Your inventory is experiencing issues. Please, contact support. +  <tag>fail</tag> +  </notification> + +  <notification +   icon="notifytip.tga" +   name="InventoryLimitReachedAIS" +   type="notifytip"> +Your inventory is experiencing issues. Please, contact support. +  <tag>fail</tag> +  </notification> + +  <notification +   icon="alertmodal.tga"     name="ConfirmClearBrowserCache"     type="alertmodal">  Are you sure you want to delete your travel, web, and search history? @@ -11901,16 +11988,41 @@ Packing: [PACK_TIME]s [PSIZE]KB  Unpacking: [UNPACK_TIME]s [USIZE]KB      <tag>fail</tag>    </notification> -   + +    <notification +     icon="alertmodal.tga" +     label="Prompt for MFA Token" +     name="PromptMFAToken" +     type="alertmodal"> +        [MESSAGE] +        <tag>confirm</tag> +        <form name="form"> +            <input name="token" type="text" width="400" /> +            <button +             default="true" +             index="0" +             name="continue" +             text="Continue"/> +            <button +             index="1" +             name="cancel" +             text="Cancel"/> +        </form> +    </notification> +    <notification     icon="alertmodal.tga"     label="Prompt for MFA Token" -   name="PromptMFAToken" +   name="PromptMFATokenWithSave"     type="alertmodal">      [MESSAGE]      <tag>confirm</tag>      <form name="form">        <input name="token" type="text" width="400" /> +      <ignore +       name="ignore" +       checkbox_only="true" +       text="Remember this computer for 30 days."/>        <button         default="true"         index="0" diff --git a/indra/newview/skins/default/xui/en/panel_inventory_gallery.xml b/indra/newview/skins/default/xui/en/panel_inventory_gallery.xml new file mode 100644 index 0000000000..ed04e12193 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_inventory_gallery.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel +   background_visible="true" +   bg_alpha_color="InventoryBackgroundColor" +   border="false" +   follows="all" +   height="390" +   name="Inventory Gallery" +   layout="topleft"> +  <text +    type="string" +    clip_partial="false" +    follows="all" +    layout="topleft" +    name="empty_txt" +    height="390" +    halign="center" +    valign="center" +    parse_urls="true" +    wrap="true"> +    Folder is empty. +  </text>  +  <scroll_container +   follows="all" +   height="390" +   layout="topleft" +   left="0" +   top="0" +   tab_stop="true" +   name="gallery_scroll_panel" +   opaque="false"> +  </scroll_container> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_inventory_gallery_item.xml b/indra/newview/skins/default/xui/en/panel_inventory_gallery_item.xml new file mode 100644 index 0000000000..574872a870 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_inventory_gallery_item.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel +   background_visible="false" +   background_opaque="false" +   bg_alpha_color="FrogGreen" +   bg_opaque_color="FrogGreen"    +   border="false" +   bevel_style="none" +   follows="left|top" +   height="149" +   width="130" +   name="gallery_item_panel" +   layout="topleft" +   left="0" +   top="0"> +  <thumbnail +   name="preview_thumbnail" +   fallback_image="Thumbnail_Fallback" +   layout="topleft" +   follows="left|top" +   interactable="false" +   height="128" +   width="128" +   top="0" +   left="1"/> +  <icon +   left="5" +   top_pad="-21" +   layout="topleft" +   name="item_type" +   height="16" +   width="16" +   follows="left|top" +   visible="true" +   image_name="Inv_Eye"/> +  <icon +    left="5" +    top_pad="-8" +    layout="topleft" +    name="link_overlay" +    height="8" +    width="6" +    follows="left|top" +    visible="false" +    image_name="Inv_Link"/> +  <panel +   background_visible="false" +   background_opaque="true" +   bg_opaque_color="MenuItemHighlightBgColor" +   border="false" +   bevel_style="none" +   follows="left|top" +   left="0" +   top="129" +   height="25" +   width="130" +   name="text_bg_panel"> +    <text +      read_only="true" +      length="1" +      follows="left|top" +      left="1" +      height="23" +      layout="topleft" +      name="item_name" +      parse_urls="false" +      text_readonly_color="White" +      word_wrap="true" +      top="2" +      width="127" +      use_ellipses="true"> +      Item name, folder name. +    </text> +  </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_main_inventory.xml b/indra/newview/skins/default/xui/en/panel_main_inventory.xml index 2ff58035ed..f7a9c552cc 100644 --- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml @@ -6,8 +6,7 @@   layout="topleft"   min_height="300"   min_width="240" - name="main inventory panel" - width="330"> + name="main inventory panel">    <panel.string     name="Itemcount">    </panel.string> @@ -23,211 +22,325 @@     name="ItemcountUnknown">      Fetched [ITEM_COUNT] Items and [CATEGORY_COUNT] Folders [FILTER]    </panel.string> +  <panel.string name="inventory_title">INVENTORY</panel.string> +  <panel.string name="default_mode_btn">Multi_Folder_Mode</panel.string> +  <panel.string name="single_folder_mode_btn">Single_Folder_Mode</panel.string>    <text -		     type="string" -		     length="1" -		     follows="left|top|right" -		     height="13" -		     layout="topleft" -    		 left="12" -		     name="ItemcountText" -		     font="SansSerifMedium" -		     text_color="InventoryItemLinkColor" -		     use_ellipses="true" -		     top_pad="0" -		     width="300"> +     type="string" +     length="1" +     follows="left|top|right" +     height="13" +     layout="topleft" +  	 left="12" +  	 right="-12" +     name="ItemcountText" +     font="SansSerifMedium" +     text_color="InventoryItemLinkColor" +     use_ellipses="true" +     top_pad="0">      Items:    </text> -  <combo_box -   height="23" -   layout="topleft" -   left="10" -   top="18" -   name="search_type" -   follows="top|left" -   width="88"> -    <item -     label="Name" -     name="Name" -     value="search_by_name"/> -    <item -     label="Creator" -     name="Creator" -     value="search_by_creator"/> -    <item -     label="Description" -     name="Description" -     value="search_by_description"/> -    <item -     label="UUID" -     name="UUID" -     value="search_by_UUID"/> -  </combo_box> -  <menu_button -   follows="top|left" -   tool_tip="Show search visibility options" -   height="23" -   image_overlay="Inv_Toolbar_SearchVisibility" -   layout="topleft" -   left_pad="3" -   name="options_visibility_btn" -   width="31" /> -  <filter_editor -   text_pad_left="10" +  <layout_stack     follows="left|top|right" -   height="23" -   label="Enter search text" -   layout="topleft" -   left_pad="3" -   max_length_chars="300" -   highlight_text_field="true" -   name="inventory search editor" -   width="177" /> -  <tab_container -     follows="all" -     halign="center" -     height="339" -     layout="topleft" -     left="7" -     name="inventory filter tabs" -     tab_height="30" -     tab_position="top" -     tab_min_width="100" -     top_pad="10" -     width="312"> -    <inventory_panel -     bg_opaque_color="DkGray2" -     bg_alpha_color="DkGray2" -     background_visible="true" -     border="false" -     bevel_style="none" -     follows="all" -     height="338" -     label="MY INVENTORY" -     help_topic="my_inventory_tab" -     layout="topleft" -     left="0" -     name="All Items" -     sort_order_setting="InventorySortOrder" -     show_item_link_overlays="true" -     top="16" -     width="288" /> -    <recent_inventory_panel -     bg_opaque_color="DkGray2" -     bg_alpha_color="DkGray2" -     background_visible="true" +   height="25" +   animate="false" +   top_pad="10" +   left="2" +   right="-4" +   orientation="horizontal"> +    <layout_panel       border="false" -     bevel_style="none" -     follows="all" -     height="338" -     label="RECENT" -     help_topic="recent_inventory_tab" -     layout="topleft" -     left_delta="0" -     name="Recent Items" -     show_item_link_overlays="true" -     width="290" /> -       <inventory_panel -      name="Worn Items" -      label="WORN" -      show_empty_message="false" -      follows="all" +     bevel_style="in" +     user_resize="false" +     auto_resize="false" +     height="25" +     width="65" +     name="nav_buttons" +     visible="false"> +     <button +      follows="top|left" +      height="23" +      image_selected="Single_Folder_Back" +      image_pressed="Single_Folder_Back" +      image_unselected="Single_Folder_Back" +      scale_image="false" +      layout="topleft" +      left="3" +      top="2" +      name="back_btn" +      tool_tip="Back" +      width="20" /> +     <button +      follows="top|left" +      height="23" +      image_selected="Single_Folder_Forward" +      image_pressed="Single_Folder_Forward" +      image_unselected="Single_Folder_Forward" +      scale_image="false" +      layout="topleft" +      left_pad="1" +      name="forward_btn" +      tool_tip="Forward" +      width="20" /> +     <button +      follows="top|left" +      height="23" +      image_selected="Single_Folder_Up" +      image_pressed="Single_Folder_Up" +      image_unselected="Single_Folder_Up" +      scale_image="false"        layout="topleft" -      width="290" -      bg_opaque_color="DkGray2" -      bg_alpha_color="DkGray2" -      background_visible="true" +      left_pad="1" +      name="up_btn" +      tool_tip="Go up one level" +      width="20" /> +    </layout_panel> +    <layout_panel        border="false" -      bevel_style="none" -      scroll.reserve_scroll_corner="false"> -  </inventory_panel> -  </tab_container> -  <layout_stack -   animate="false" -   border_size="0" -   follows="left|right|bottom" -   height="25" -   layout="topleft" -   orientation="horizontal" -   top_pad="0" -   left="10" -   name="bottom_panel" -   width="307"> -      <layout_panel -       auto_resize="false" -       height="25" +      bevel_style="in" +      user_resize="false" +      height="25" +      width="381" +      visible="true"> +      <combo_box +       height="23" +       layout="topleft" +       left="2" +       top="0" +       name="search_type" +       tool_tip="Search by" +       follows="top|left" +       width="67"> +       <item +        label="Name" +        name="Name" +        value="search_by_name"/> +       <item +        label="Creator" +        name="Creator" +        value="search_by_creator"/> +       <item +        label="Description" +        name="Description" +        value="search_by_description"/> +       <item +        label="UUID" +        name="UUID" +        value="search_by_UUID"/> +      </combo_box> +      <menu_button +       follows="top|left" +       tool_tip="Search visibility options" +       height="23" +       image_overlay="Inv_Toolbar_SearchVisibility"         layout="topleft" -       name="options_gear_btn_panel" -       width="32"> -          <menu_button -           follows="bottom|left" -           tool_tip="Show additional options" -           height="25" -           image_hover_unselected="Toolbar_Left_Over" -           image_overlay="OptionsMenu_Off" -           image_selected="Toolbar_Left_Selected" -           image_unselected="Toolbar_Left_Off" -           layout="topleft" -           left="0" -           name="options_gear_btn" -           top="0" -           width="31" /> -      </layout_panel> -      <layout_panel -       auto_resize="false" -       height="25" +       left_pad="1" +       name="options_visibility_btn" +       width="31" /> +      <filter_editor +       text_pad_left="10" +       follows="left|top|right" +       height="23" +       label="Enter search text"         layout="topleft" -       name="add_btn_panel" -       width="32"> -          <button -           follows="bottom|left" -           height="25" -           image_hover_unselected="Toolbar_Middle_Over" -           image_overlay="AddItem_Off" -           image_selected="Toolbar_Middle_Selected" -           image_unselected="Toolbar_Middle_Off" -           layout="topleft" -           left="0" -           name="add_btn" -           tool_tip="Add new item" -           top="0" -           width="31" /> -      </layout_panel> -      <layout_panel -       auto_resize="true" -       height="25" +       left_pad="1" +       max_length_chars="300" +       highlight_text_field="true" +       name="inventory search editor" +       width="150" /> +      <menu_button +       follows="top|right" +       tool_tip="Actions" +       height="23" +       image_hover_unselected="Toolbar_Middle_Over" +       image_overlay="OptionsMenu_Off" +       image_selected="Toolbar_Middle_Selected" +       image_unselected="Toolbar_Middle_Off"         layout="topleft" -       name="dummy_panel" -       width="212"> -          <icon -           follows="bottom|left|right" -           height="25" -           image_name="Toolbar_Middle_Off" -           layout="topleft" -           left="0" -           top="0" -           name="dummy_icon" -           width="211" /> -      </layout_panel> -      <layout_panel -       auto_resize="false" -       height="25" +       left_pad="1" +       name="options_gear_btn" +       width="31" /> +      <menu_button +       follows="top|right" +       tool_tip="View & sort options" +       height="23" +       image_hover_unselected="Toolbar_Middle_Over" +       image_overlay="Conv_toolbar_sort" +       image_selected="Toolbar_Middle_Selected" +       image_unselected="Toolbar_Middle_Off" +       layout="topleft" +       left_pad="1" +       name="view_btn" +       width="31" /> +      <button +       follows="top|right" +       height="23" +       image_hover_unselected="Toolbar_Middle_Over" +       image_overlay="AddItem_Off" +       image_selected="Toolbar_Middle_Selected" +       image_unselected="Toolbar_Middle_Off" +       layout="topleft" +       left_pad="1" +       name="add_btn" +       tool_tip="Create new item" +       width="31" /> +      <button +       follows="top|right" +       tool_tip="Switch between views" +       height="23" +       image_hover_unselected="Toolbar_Middle_Over" +       image_overlay="Single_Folder_Mode" +       image_selected="Toolbar_Middle_Selected" +       image_unselected="Toolbar_Middle_Off" +       layout="topleft" +       left_pad="1" +       name="view_mode_btn" +       width="31" /> +    </layout_panel> +    </layout_stack> +    <panel +     follows="all" +     halign="center" +     height="372" +     layout="topleft" +     left="3" +     right="-3" +     name="default_inventory_panel" +     top_pad="5"> +      <tab_container +       follows="all" +       halign="center" +       height="372"         layout="topleft" -       name="trash_btn_panel" -       width="31"> -          <dnd_button -           follows="bottom|left" -           height="25" -           image_hover_unselected="Toolbar_Right_Over" -           image_overlay="TrashItem_Off" -           image_selected="Toolbar_Right_Selected" -           image_unselected="Toolbar_Right_Off" -           left="0" -           layout="topleft" -           name="trash_btn" -           tool_tip="Remove selected item" -           top="0" -           width="31"/> -      </layout_panel> -  </layout_stack> +       left="0" +       name="inventory filter tabs" +       tab_height="30" +       tab_position="top" +       tab_min_width="100" +       top="0"> +        <inventory_panel +         bg_opaque_color="DkGray2" +         bg_alpha_color="DkGray2" +         background_visible="true" +         border="false" +         bevel_style="none" +         follows="all" +         label="MY INVENTORY" +         help_topic="my_inventory_tab" +         layout="topleft" +         name="All Items" +         sort_order_setting="InventorySortOrder" +         show_item_link_overlays="true" +         preinitialize_views="false" +         scroll.reserve_scroll_corner="false"> +            <folder double_click_override="true"/> +        </inventory_panel> +        <recent_inventory_panel +         bg_opaque_color="DkGray2" +         bg_alpha_color="DkGray2" +         background_visible="true" +         border="false" +         bevel_style="none" +         follows="all" +         label="RECENT" +         help_topic="recent_inventory_tab" +         layout="topleft" +         name="Recent Items" +         show_item_link_overlays="true" +         preinitialize_views="false" +         scroll.reserve_scroll_corner="false"> +            <folder double_click_override="true"/> +        </recent_inventory_panel> +        <inventory_panel +         name="Worn Items" +         label="WORN" +         show_empty_message="false" +         follows="all" +         layout="topleft" +         bg_opaque_color="DkGray2" +         bg_alpha_color="DkGray2" +         background_visible="true" +         preinitialize_views="false" +         border="false" +         bevel_style="none" +         scroll.reserve_scroll_corner="false"> +            <folder double_click_override="true"/> +        </inventory_panel> +      </tab_container> +    </panel> +    <panel +     follows="all" +     halign="center" +     height="375" +     layout="topleft" +     left="7" +     name="combination_view_inventory" +     top_delta="0" +     visible="false"> +     <layout_stack +      follows="all" +      layout="topleft" +      height="375" +      name="combination_view_stack" +      animate="false" +      drag_handle_thickness="6" +      drag_handle_first_indent="18" +      drag_handle_second_indent="18" +      drag_handle_shift="5" +      show_drag_handle="true" +      top="0" +      left="0" +      orientation="vertical"> +       <layout_panel +        border="false" +        bevel_style="in" +        user_resize="true" +        auto_resize="true" +        height="248" +        min_width="150" +        name="comb_gallery_layout"> +        <panel +         class="inventory_gallery" +         filename="panel_inventory_gallery.xml" +         left="0" +         top="1" +         height="248" +         name="comb_gallery_view_inv" +         background_visible="true" +         follows="all" +         layout="topleft"> +        </panel> +       </layout_panel> +       <layout_panel +        border="false" +        bevel_style="in" +        user_resize="true" +        auto_resize="true" +        height="127" +        min_height="100" +        name="comb_inventory_layout"> +        <single_folder_inventory_panel +         name="comb_single_folder_inv" +         follows="all" +         left="0" +         top="1" +         height="127" +         layout="topleft" +         show_item_link_overlays="true" +         bg_opaque_color="DkGray2" +         bg_alpha_color="DkGray2" +         background_visible="true" +         border="false" +         bevel_style="none" +         scroll.reserve_scroll_corner="false"> +         <item +          single_folder_mode="true" +          folder_indentation="-8"/> +         <folder +          single_folder_mode="true" +          folder_indentation="-8"/> +        </single_folder_inventory_panel> +       </layout_panel> +     </layout_stack> +    </panel>  </panel> diff --git a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml index e3790ae09b..e951d25391 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml @@ -40,50 +40,10 @@     layout="topleft"     left="4"     top="0" +   tab_stop="true"     name="gallery_scroll_panel"     opaque="false"     top_pad="0"> -   <!--outfit_gallery_item -    layout="topleft" -    left="10" -    name="preview_outfit1" -    height="175" -    width="150" -    follows="left|top"/--> -     <!--layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="0" name="top_gallery_stack" orientation="horizontal"> -      <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> -        <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> -      </layout_panel> -      <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> -        <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> -      </layout_panel> -      <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> -        <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> -      </layout_panel> -    </layout_stack> -    <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="190" name="top_gallery_stack" orientation="horizontal"> -      <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> -        <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> -      </layout_panel> -      <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> -        <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> -      </layout_panel> -      <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> -        <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> -      </layout_panel> -    </layout_stack> -     <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="380" name="top_gallery_stack" orientation="horizontal"> -       <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> -         <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> -       </layout_panel> -       <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> -         <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> -       </layout_panel> -       <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> -         <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> -       </layout_panel> -     </layout_stack--> -    <!--</panel>-->      </scroll_container>     <panel       background_visible="true" diff --git a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml index b2dc975c6e..ceaff0ea69 100644 --- a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml +++ b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml @@ -18,7 +18,7 @@       follows="all"       height="400"       layout="topleft" -     left="0" +     left="3"       single_expansion="true"       top="0"       name="wearables_accordion" diff --git a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml index ca1e405a62..f899f83ad4 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_firstlife.xml @@ -19,7 +19,7 @@       layout="topleft"       visible="false"      /> -    <icon +    <thumbnail       name="real_world_pic"       image_name="Generic_Person_Large"       follows="top|left" diff --git a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml index 07cdd6d71e..fea7d1bcb8 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml @@ -68,7 +68,7 @@ Account: [ACCTTYPE]       auto_resize="false"       user_resize="false"> -      <icon +      <thumbnail         name="2nd_life_pic"         image_name="Generic_Person_Large"         layout="topleft" diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml index 9995523e61..76d0ffcb8e 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml @@ -6,8 +6,7 @@  	  layout="topleft"  	  min_height="350"  	  min_width="240" -	  name="objects panel" -	  width="333"> +	  name="objects panel">      <panel  		 follows="all"  		 layout="topleft" @@ -17,25 +16,22 @@  		 label=""  		 height="570"  		 visible="true" -		 default_tab_group="1" -		 width="330"> +		 default_tab_group="1">        <layout_stack                follows="left|right|top|bottom"                layout="topleft"                left="0"                top="0" -							tab_group="1" +              tab_group="1"                orientation="vertical"                name="inventory_layout_stack" -              height="535" -              width="330"> +              height="560">               <layout_panel                   name="main_inventory_layout_panel"                   layout="topleft"                   auto_resize="true"                   user_resize="true"                   min_dim="150" -                 width="330"                   follows="bottom|left|right"                   height="300">                   <panel @@ -47,17 +43,15 @@                        name="panel_main_inventory"                        top="0"                        label="" -                      height="300" -                      width="330" /> +                      height="300" />               </layout_panel>  			    <layout_panel -                 width="330"                   layout="topleft"                   auto_resize="false"                   user_resize="true"                   follows="left|right|top"                   name="inbox_layout_panel" -                 visible="false" +                 visible="true"                   min_dim="35"                   expanded_min_dim="90"                   height="235"> @@ -69,17 +63,15 @@                        class="panel_marketplace_inbox"                        top="0"                        label="" -                      height="235" -                      width="330"> +                      height="235">                       <string name="InboxLabelWithArg">Received items ([NUM])</string>                       <string name="InboxLabelNoArg">Received items</string>                       <button                          control_name="InventoryInboxToggleState"                          label="Received items" -												font="SansSerifMedium" +                        font="SansSerifMedium"                          name="inbox_btn"                          height="35" -                        width="308"                          image_unselected="MarketplaceBtn_Off"                          image_selected="MarketplaceBtn_Selected"                          halign="left" @@ -89,7 +81,8 @@                          tab_stop="false"                          pad_left="35"                          top="0" -                        left="10" /> +                        left="5" +                        right="-5" />                       <text                          type="string"                          length="1" @@ -101,174 +94,35 @@                          name="inbox_fresh_new_count"                          font="SansSerifMedium"                          halign="right" -                        top_pad="0" -                        width="300"> +                        top_pad="0">                          [NUM] new                       </text>                       <panel                          name="inbox_inventory_placeholder_panel"                          follows="all" -                        left="10" -                        bottom="235" -                        width="308" +                        left="5" +                        right="-5"                          top="35" +                        height="200"                          bg_opaque_color="InventoryBackgroundColor"                          background_visible="true"                          background_opaque="true"                          tool_tip="Drag and drop items to your inventory to use them"                          >                          <text name="inbox_inventory_placeholder" -															type="string" -															follows="all" -															layout="topleft" -															top="0" -															left="0" -															width="308" -															height="200" -															wrap="true" -															halign="center"> -Purchases from the marketplace will be delivered here. -												</text> +                            type="string" +                            follows="all" +                            layout="topleft" +                            top="0" +                            height="200" +                            wrap="true" +                            halign="center" +                            valign="center"> +                          Purchases from the marketplace will be delivered here. +                        </text>                      </panel>                   </panel>               </layout_panel>           </layout_stack> -			<panel follows="bottom|left|right" -					 height="30" -					 layout="topleft" -					 name="button_panel" -					 left="9" -					 top_pad="7" -					 width="308"> -				<layout_stack follows="bottom|left|right" -											height="23" -											layout="topleft" -											mouse_opaque="false" -											name="button_panel_ls" -											left="0" -											orientation="horizontal" -											top="0" -											width="308">	 -			    <layout_panel follows="bottom|left|right" -												height="23" -												layout="bottomleft" -												left="0"			 -												mouse_opaque="false" -												name="info_btn_lp" -												auto_resize="true" -												width="101"> -						<button enabled="true" -										follows="bottom|left|right" -										height="23" -										label="Profile" -										layout="topleft" -										left="1" -										name="info_btn" -										tool_tip="Show object profile" -										top="0" -										width="100" /> -			    </layout_panel> -			    <layout_panel -                     follows="bottom|left|right" -                     height="23" -                     layout="bottomleft" -                     left_pad="1"			 -                     mouse_opaque="false" -                     name="share_btn_lp" -                     auto_resize="true" -                     width="100"> -                    <button -                         enabled="true" -                         follows="bottom|left|right" -                         height="23" -                         label="Share" -                         layout="topleft" -                         left="1" -                         name="share_btn" -                         tool_tip="Share an inventory item" -                         top="0" -                         width="99" /> -			    </layout_panel> -			    <layout_panel -                     follows="bottom|left|right" -                     height="23" -                     layout="bottomleft" -                     left_pad="1"			 -                     mouse_opaque="false" -                     name="shop_btn_lp" -                     auto_resize="true" -                     width="100"> -                    <button -                         enabled="true" -                         follows="bottom|left|right" -                         height="23" -                         label="Shop" -                         layout="topleft" -                         left="1" -                         name="shop_btn" -                         tool_tip="Open Marketplace webpage" -                         top="0" -                         width="99" /> -                    <button -                         enabled="false" -                         follows="bottom|left|right" -                         height="23" -                         label="Wear" -                         layout="topleft" -                         left="1" -                         name="wear_btn" -                         tool_tip="Wear seleceted outfit" -                         top="0" -                         width="99" /> -                    <button -                         enabled="false" -                         follows="bottom|left|right" -                         height="23" -                         label="Play" -                         layout="topleft" -                         name="play_btn" -                         left="1" -                         top="0" -                         width="99" /> -                    <button -                         enabled="false" -                         follows="bottom|left|right" -                         height="23" -                         label="Teleport" -                         layout="topleft" -                         left="1" -                         name="teleport_btn" -                         tool_tip="Teleport to the selected area" -                         top="0" -                         width="99" /> -			    </layout_panel> -			</layout_stack> -		</panel> -	</panel> -	<panel -		 follows="all" -		 layout="topleft" -		 left="0" -		 class="sidepanel_item_info" -		 filename="sidepanel_item_info.xml" -		 name="sidepanel__item_panel" -		 top="0" -		 label="" -		 height="570" -		 visible="false" -		 width="330"> -	</panel> -	<panel -		 follows="all" -		 layout="topleft" -		 left="0" -		 class="sidepanel_task_info" -		 filename="sidepanel_task_info.xml" -		 name="sidepanel__task_panel" -		 top="0" -		 label="" -		 height="570" -		 visible="false" -		 width="330">  	</panel>  </panel> diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml index 35d14251c7..ad521cb1af 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -43,214 +43,126 @@      name="origin_inworld">      (Inworld)    </panel.string> -  <icon -    follows="top|right" -    height="18" -    image_name="Lock" -    layout="topleft" -    right="-15" -    mouse_opaque="true" -    name="IconLocked" -    top="8" -    width="18" /> -  <button -    follows="top|left" -    height="24" -    image_hover_unselected="BackButton_Over" -    image_pressed="BackButton_Press" -    image_unselected="BackButton_Off" -    layout="topleft" -    left="12" -    name="back_btn" -    tab_stop="false" -    top="2" -    width="30" -    use_draw_context_alpha="false" /> -  <text -    follows="top|left|right" -    font="SansSerifHugeBold" -    height="26" -    layout="topleft" -    left_pad="3" -    name="title" -    text_color="LtGray" -    top="2" -    use_ellipses="true" -    value="Item Profile" -    width="275" /> -  <text -    follows="top|left|right" -    height="13" -    layout="topleft" -    left="45" -    name="origin" -    text_color="LtGray_50" -    use_ellipses="true" -    value="(Inventory)" -    width="275" /> -  <scroll_container -    color="DkGray2" -    follows="all" -    layout="topleft" -    left="9" -    name="item_profile_scroll" -    opaque="true" -    height="493" -    width="313" -    top="45"> -    <panel -      follows="left|top|right" -      height="390" -      help_topic="" -      label="" + +<layout_stack +  animate="false" +  name="main_stack" +  layout="topleft" +  follows="all" +  orientation="vertical" +  left="0" +  top="0" +  right="-1" +  bottom="-1"> +    <layout_panel +      auto_resize="false" +      name="layout_item_name"        layout="topleft" -      left="0" -      name="item_profile" -      top="0" -      width="295"> -      <text -        type="string" -        length="1" -        follows="left|top" -        height="10" +      follows="all" +      height="25"> +      <icon +        follows="top|left" +        height="16" +        image_name="Inv_Object"          layout="topleft"          left="5" -        name="LabelItemNameTitle" -        top="10" -        width="78"> -        Name: -      </text> +        mouse_opaque="true" +        name="item_type_icon" +        top="3" +        width="16" />        <line_editor          border_style="line"          border_thickness="1"          follows="left|top|right" -        height="20"          layout="topleft" -        left_delta="78" +        left_pad="5" +        top="1" +        right="-5" +        height="20"          max_length_bytes="63"          name="LabelItemName" -        top_delta="0" -        width="210"          tool_tip="The name is limited to 63 characters. Longer prim names are cut short. Names can only consist of printable characters found in the ASCII-7 (non-extended) character set, with the exception of the vertical bar/pipe '|'." /> -      <text -        type="string" -        length="1" -        follows="left|top" -        height="10" +    </layout_panel> +     +    <layout_panel +      auto_resize="false" +      name="layout_item_details" +      layout="topleft" +      follows="all" +      height="133"> +       +      <thumbnail +        name="item_thumbnail" +        fallback_image="Thumbnail_Fallback" +        follows="top|left"          layout="topleft"          left="5" -        name="LabelItemDescTitle" -        top_pad="10" -        width="78"> -        Description: -      </text> -      <line_editor -        border_style="line" -        border_thickness="1" -        follows="left|top|right" -        height="23" -        layout="topleft" -        left_delta="78" -        max_length_bytes="127" -        name="LabelItemDesc" -        top_delta="-5" -        width="210" -        tool_tip="When people have 'Hover Tips on All Objects' selected in the viewer's settings, they'll see the object description pop-up for any object under their mouse pointer. The prim description is limited to 127 bytes any string longer then that will be truncated." /> +        top="2" +        height="128" +        width="128" +        /> +                <text          type="string"          length="1"          follows="left|top" -        height="23" +        height="16"          layout="topleft" -        left="5" -        name="LabelCreatorTitle" -        top_pad="10" +        left_pad="5" +        name="LabelOwnerTitle" +        top="0"          width="78"> -        Creator: +        Owner:        </text> -      <avatar_icon -        follows="top|left" -        height="20" -        default_icon_name="Generic_Person" -        layout="topleft" -        left_pad="0" -        top_delta="-6" -        mouse_opaque="true" -        width="20" />        <text          type="string" -        follows="left|right|top"          font="SansSerifSmall" -        height="15" +        follows="left|right|top"          layout="topleft" -        left_pad="5" -        name="LabelCreatorName" -        top_delta="6" +        height="15" +        width="187" +        left_delta="0" +        top_pad="0" +        name="LabelOwnerName"          use_ellipses="true" -        width="165"> +        translate="false"> +TestString PleaseIgnore        </text> -      <button -        follows="top|right" -        height="16" -        image_selected="Inspector_I" -        image_unselected="Inspector_I" -        layout="topleft" -        right="-5" -        name="BtnCreator" -        top_delta="-6" -        width="16" />        <text          type="string"          length="1"          follows="left|top" -        height="23" +        height="16"          layout="topleft" -        left="5" -        name="LabelOwnerTitle" -        top_pad="10" +        left_delta="0" +        name="LabelCreatorTitle" +        top_pad="7"          width="78"> -        Owner: +        Creator:        </text> -      <avatar_icon -        follows="top|left" -        height="20" -        default_icon_name="Generic_Person" -        layout="topleft" -        left_pad="0" -        top_delta="-6" -        mouse_opaque="true" -        width="20" />        <text          type="string" -        follows="left|right|top"          font="SansSerifSmall" -        height="15" +        follows="left|right|top"          layout="topleft" -        left_pad="5" -        name="LabelOwnerName" -        top_delta="6" +        left_delta="0" +        top_pad="0" +        width="187" +        height="15" +        name="LabelCreatorName"          use_ellipses="true" -        width="165"> +        translate="false"> +TestString PleaseIgnore        </text> -      <button -        follows="top|right" -        height="16" -        image_selected="Inspector_I" -        image_unselected="Inspector_I" -        layout="topleft" -        right="-5" -        name="BtnOwner" -        top_delta="-3" -        width="16" />        <text          type="string"          length="1"          follows="left|top" -        height="23" +        height="16"          layout="topleft" -        left="5" +        left_delta="0"          name="LabelAcquiredTitle" -        top_pad="10" +        top_pad="7"          width="78">          Acquired:        </text> @@ -258,171 +170,247 @@          type="string"          length="1"          follows="left|top|right" -        height="23" +        height="18"          layout="topleft" -        left_delta="78" +        left_delta="0"          name="LabelAcquiredDate" -        top_delta="0" -        width="210"> +        top_pad="0" +        width="187"> +        00/00/00        </text> -      <text -        type="string" -        length="1" +      <button          follows="left|top" -        height="10" +        height="21" +        label="Image..."          layout="topleft" -        left="5" -        name="LabelItemExperienceTitle" +        left_delta="0" +        name="change_thumbnail_btn"          top_pad="0" -        width="78" -        visible="true"> -        Experience: -      </text> +        width="120" /> +    </layout_panel> +     +    <layout_panel +      auto_resize="false" +      name="layout_item_description" +      layout="topleft" +      follows="all" +      height="84">        <text          type="string"          length="1" -        follows="left|top|right" +        follows="left|top"          height="10"          layout="topleft" -        left_delta="78" -        name="LabelItemExperience" -        top_delta="0" -        width="210" -        visible="true" -        /> -      <panel -        border="false" -        follows="left|top|right" +        left="5" +        name="LabelItemDescTitle" +        top="0" +        width="78"> +        Description: +      </text> +      <text_editor +        text_type="ascii_printable_no_pipe" +        commit_on_focus_lost="true" +        border_style="line" +        border_thickness="1" +        word_wrap="true" +        use_ellipses="false" +        follows="all"          layout="topleft" -        mouse_opaque="false" -        name="perms_inv" -        left="0" -        top_pad="25" -        height="155" -        width="313"> -        <text -          type="string" -          length="1" -          left="10" -          top_pad="13" -          text_color="EmphasisColor" -          height="15" -          follows="left|top|right" -          layout="topleft" -          name="perm_modify" -          width="200"> -          You can: -        </text> -        <check_box -          height="18" -          label="Modify" -          layout="topleft" -          left="20" -          name="CheckOwnerModify" -          top_pad="0" -          width="90" /> -        <check_box -          height="18" -          label="Copy" -          layout="topleft" -          left_pad="0" -          name="CheckOwnerCopy" -          width="90" /> -        <check_box -          height="18" -          label="Transfer" -          layout="topleft" -          left_pad="0" -          name="CheckOwnerTransfer" -          width="106" /> -        <text -          type="string" -          length="1" -          follows="left|top" -          height="16" -          layout="topleft" -          left="10" -          name="AnyoneLabel" -          top_pad="8" -          width="100"> -          Anyone: -        </text> -        <check_box -          height="18" -          label="Copy" -          layout="topleft" -          left_pad="0" -          name="CheckEveryoneCopy" -          tool_tip="Anyone can take a copy of the object . Object and all of its contents must be copy and transfer permissive." -          top_delta="-2" -          width="150" /> +        left="5" +        top_pad="5" +        right="-5" +        height="46" +        max_length="127" +        name="LabelItemDesc" +        tool_tip="When people have 'Hover Tips on All Objects' selected in the viewer's settings, they'll see the object description pop-up for any object under their mouse pointer. The prim description is limited to 127 bytes any string longer then that will be truncated." /> +          <text            type="string"            length="1"            follows="left|top" -          height="16" +          height="10"            layout="topleft" -          left="10" -          name="GroupLabel" -          top_pad="8" -          width="100"> -          Group: +          left="5" +          name="LabelItemExperienceTitle" +          top_pad="7" +          width="78" +          visible="true"> +          Experience:          </text> -        <check_box -          height="18" -          label="Share" -          layout="topleft" -          left_pad="0" -          top_delta="-2" -          name="CheckShareWithGroup" -          tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions." -          width="150" />          <text            type="string"            length="1" -          follows="left|top" -          height="16" -          layout="topleft" -          left="10" -          name="NextOwnerLabel" -          top_pad="8" -          width="200" -          word_wrap="true"> -          Next owner: -        </text> -        <check_box -          height="18" -          label="Modify" -          layout="topleft" -          left="20" -          top_pad="0" -          name="CheckNextOwnerModify" -          tool_tip="Next owner can edit properties like item name or scale of this object." -          width="90" /> -        <check_box -          height="18" -          label="Copy" -          layout="topleft" -          left_pad="0" -          name="CheckNextOwnerCopy" -          tool_tip="Next owner can make unlimited copies of this object. Copies maintain creator information, and can never be more permissive than the item being copied." -          width="90" /> -        <check_box -          height="18" -          label="Transfer" +          follows="left|top|right" +          height="10"            layout="topleft" -          left_pad="0" -          name="CheckNextOwnerTransfer" -          tool_tip="Next owner can give away or resell this object." -          width="106" /> -      </panel> +          left_delta="78" +          name="LabelItemExperience" +          top_delta="0" +          width="210" +          visible="true" +          /> +    </layout_panel> +     +    <layout_panel +      auto_resize="false" +      name="layout_item_permissions_sale" +      layout="topleft" +      follows="all" +      height="235"> +       +      <view_border +        bevel_style="none" +        height="0" +        layout="topleft" +        left="5" +        right="-5" +        name="cost_text_border" +        top="1"/> +         +      <text +        type="string" +        length="1" +        left="10" +        top_pad="7" +        height="15" +        follows="left|top" +        layout="topleft" +        name="perm_modify" +        width="200"> +        Permissions +      </text> + +      <text +        type="string" +        length="1" +        left="10" +        top_pad="5" +        height="15" +        follows="left|top" +        layout="topleft" +        name="perm_modify" +        width="200"> +        You can: +      </text> +      <check_box +        height="18" +        label="Modify" +        layout="topleft" +        left="20" +        name="CheckOwnerModify" +        top_pad="0" +        width="90" /> +      <check_box +        height="18" +        label="Copy" +        layout="topleft" +        left_pad="0" +        name="CheckOwnerCopy" +        width="90" /> +      <check_box +        height="18" +        label="Transfer" +        layout="topleft" +        left_pad="0" +        name="CheckOwnerTransfer" +        width="106" /> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="16" +        layout="topleft" +        left="10" +        name="AnyoneLabel" +        top_pad="8" +        width="100"> +        Anyone: +      </text> +      <check_box +        height="18" +        label="Copy" +        layout="topleft" +        left_pad="0" +        name="CheckEveryoneCopy" +        tool_tip="Anyone can take a copy of the object . Object and all of its contents must be copy and transfer permissive." +        top_delta="-2" +        width="150" /> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="16" +        layout="topleft" +        left="10" +        name="GroupLabel" +        top_pad="8" +        width="100"> +        Group: +      </text> +      <check_box +        height="18" +        label="Share" +        layout="topleft" +        left_pad="0" +        top_delta="-2" +        name="CheckShareWithGroup" +        tool_tip="Allow all members of the set group to share your modify permissions for this object. You must Deed to enable role restrictions." +        width="150" /> +      <text +        type="string" +        length="1" +        follows="left|top" +        height="16" +        layout="topleft" +        left="10" +        name="NextOwnerLabel" +        top_pad="8" +        width="200" +        word_wrap="true"> +        Next owner: +      </text> +      <check_box +        height="18" +        label="Modify" +        layout="topleft" +        left="20" +        top_pad="0" +        name="CheckNextOwnerModify" +        tool_tip="Next owner can edit properties like item name or scale of this object." +        width="90" /> +      <check_box +        height="18" +        label="Copy" +        layout="topleft" +        left_pad="0" +        name="CheckNextOwnerCopy" +        tool_tip="Next owner can make unlimited copies of this object. Copies maintain creator information, and can never be more permissive than the item being copied." +        width="90" /> +      <check_box +        height="18" +        label="Transfer" +        layout="topleft" +        left_pad="0" +        name="CheckNextOwnerTransfer" +        tool_tip="Next owner can give away or resell this object." +        width="106" /> +         +    <view_border +        bevel_style="none" +        height="0" +        layout="topleft" +        left="5" +        right="-5" +        name="cost_text_border" +        top_pad="9"/> +                <check_box          height="18"          label="For Sale"          layout="topleft"          left="20"          name="CheckPurchase" -        top_pad="20" +        top_pad="15"          width="100"          tool_tip="Lets people buy this object, its content or it copy inworld for specified price." />        <combo_box @@ -450,6 +438,7 @@          follows="left|top"          decimal_digits="0"          increment="1" +        control_name="Edit Cost"          name="Edit Cost"          label="Price: L$"          label_width="75" @@ -460,88 +449,80 @@          max_val="999999999"          top_pad="10"          tool_tip="Object cost." /> -      <text -        type="string" -        length="1" -        follows="left|top" -        height="10" -        layout="topleft" -        left="10" -        name="BaseMaskDebug" -        text_color="White" -        top_pad="30" -        width="130"> -        B: -      </text> -      <text -        type="string" -        length="1" -        follows="left|top" -        height="10" -        layout="topleft" -        left_delta="60" -        name="OwnerMaskDebug" -        text_color="White" -        top_delta="0" -        width="270"> -        O: -      </text> -      <text -        type="string" -        length="1" -        follows="left|top" -        height="10" -        layout="topleft" -        left_delta="60" -        name="GroupMaskDebug" -        text_color="White" -        top_delta="0" -        width="210"> -        G: -      </text> -      <text -        type="string" -        length="1" -        follows="left|top" -        height="10" -        layout="topleft" -        left_delta="60" -        name="EveryoneMaskDebug" -        text_color="White" -        top_delta="0" -        width="150"> -        E: -      </text> -      <text -       type="string" -       length="1" -       follows="left|top" -       height="10" -       layout="topleft" -       left_delta="60" -       name="NextMaskDebug" -       text_color="White" -       top_delta="0" -       width="90"> -        N: -      </text> -    </panel>    -  </scroll_container> -  <panel -    height="30" -    layout="topleft" -    name="button_panel" -    left="5" -    top_pad="0" -    width="313" -    follows="top|right|left"> -    <button -      follows="top|right" -      height="23" -      label="Cancel" -      layout="topleft" -      name="cancel_btn" -      right="-1" -      width="100" /> -  </panel> +       +    </layout_panel> + +    <layout_panel +     auto_resize="false" +     name="layout_debug_permissions" +     layout="topleft" +     follows="all" +     height="30"> +        <text +          type="string" +          length="1" +          follows="left|top" +          height="10" +          layout="topleft" +          left="10" +          name="BaseMaskDebug" +          text_color="White" +          top="2" +          width="130"> +          B: +        </text> +        <text +          type="string" +          length="1" +          follows="left|top" +          height="10" +          layout="topleft" +          left_delta="60" +          name="OwnerMaskDebug" +          text_color="White" +          top_delta="0" +          width="270"> +          O: +        </text> +        <text +          type="string" +          length="1" +          follows="left|top" +          height="10" +          layout="topleft" +          left_delta="60" +          name="GroupMaskDebug" +          text_color="White" +          top_delta="0" +          width="210"> +          G: +        </text> +        <text +          type="string" +          length="1" +          follows="left|top" +          height="10" +          layout="topleft" +          left_delta="60" +          name="EveryoneMaskDebug" +          text_color="White" +          top_delta="0" +          width="150"> +          E: +        </text> +        <text +         type="string" +         length="1" +         follows="left|top" +         height="10" +         layout="topleft" +         left_delta="60" +         name="NextMaskDebug" +         text_color="White" +         top_delta="0" +         width="90"> +          N: +        </text> +    </layout_panel> +  </layout_stack>  </panel> diff --git a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml index 5f0241512a..faff6185ab 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml @@ -62,25 +62,12 @@               name="Sale Mixed">                  Mixed Sale              </panel.string> -    <button -     follows="top|left" -     height="24" -     image_hover_unselected="BackButton_Over" -     image_pressed="BackButton_Press" -     image_unselected="BackButton_Off" -     layout="topleft" -     left="8" -     name="back_btn" -     tab_stop="false" -     top="0" -     width="30" -     use_draw_context_alpha="false" /> -        <text +    <text       follows="top|left|right"       font="SansSerifHuge"       height="26"       layout="topleft" -     left_pad="10" +     left="48"       name="title"       text_color="LtGray"       top="0" @@ -181,7 +168,6 @@  		     translate="false"           use_ellipses="true"   		     width="225"> -	        TestString PleaseIgnore  	     </text>  	    <text  			 type="string" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 09ccc7b8d2..d0e09492b3 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -2306,6 +2306,8 @@ For AI Character: Get the closest navigable point to the point provided.  	<string name="FavoritesNoMatchingItems">To add a place to your favorites, click the star to the right of the location name, then save the landmark to "Favorites bar".</string>  	<string name="MarketplaceNoListing">You have no listings yet.</string>  	<string name="MarketplaceNoMatchingItems">No items found. Check the spelling of your search string and try again.</string> +	<string name="InventorySingleFolderEmpty">Folder is empty.</string> +	<string name="InventorySingleFolderNoMatches">No matches.</string>  	<string name="InventoryNoTexture">You do not have a copy of this texture in your inventory</string>  	<string name="InventoryInboxNoItems">Your Marketplace purchases will appear here. You may then drag them into your inventory to use them.</string>  	<string name="MarketplaceURL">https://marketplace.[MARKETPLACE_DOMAIN_NAME]/</string> @@ -2353,6 +2355,7 @@ If you continue to receive this message, please contact Second Life support for  	<string name="InventoryMarketplaceListingsNoItems">          Drag folders to this area to list them for sale on the [[MARKETPLACE_DASHBOARD_URL] Marketplace].  	</string> +    <string name="InventoryFolderDebug">( F:[FOLDER_COUNT] I:[ITEMS_COUNT] V:[VERSION] DC:[VIEWER_DESCENDANT_COUNT]/[SERVER_DESCENDANT_COUNT] )</string>  	<string name="InventoryItemsCount">( [ITEMS_COUNT] Items )</string>  	<string name="Marketplace Validation Log"></string>  	<string name="Marketplace Validation Warning Stock">stock folder must be contained by a version folder</string> @@ -2391,12 +2394,16 @@ If you continue to receive this message, please contact Second Life support for    <string name="Unconstrained">Unconstrained</string>  	<!-- use value="" because they have preceding spaces --> +    <string name="active" value=" (active)"/>  	<string name="no_transfer" value=" (no transfer)" />  	<string name="no_modify"   value=" (no modify)" />  	<string name="no_copy"     value=" (no copy)" />  	<string name="worn"        value=" (worn)" /> -	<string name="link"        value=" (link)" /> -	<string name="broken_link" value=" (broken_link)" /> +	<string name="link"        value="  link" /> +	<string name="broken_link" value="  broken_link" /> +    <string name="no_transfer_lbl" value=" no transfer" /> +    <string name="no_modify_lbl"   value=" no modify" /> +    <string name="no_copy_lbl"     value=" no copy" />  	<string name="LoadingContents">Loading contents...</string>  	<string name="NoContents">No contents</string>  	<string name="WornOnAttachmentPoint" value=" (worn on [ATTACHMENT_POINT])" /> diff --git a/indra/newview/skins/default/xui/es/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/es/floater_inventory_item_properties.xml deleted file mode 100644 index a8a3ad08f8..0000000000 --- a/indra/newview/skins/default/xui/es/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="PROPIEDADES DEL ÍTEM DEL INVENTARIO"> -	<floater.string name="unknown">(desconocido)</floater.string> -	<floater.string name="public">(público)</floater.string> -	<floater.string name="you_can">Usted puede:</floater.string> -	<floater.string name="owner_can">El propietario puede:</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local][day,datetime,local] [mth,datetime,local] [year,datetime,local][hour,datetime,local]:[min,datetime,local]:[second,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">Nombre:</text> -	<text name="LabelItemDescTitle">Descripción:</text> -	<text name="LabelCreatorTitle">Creador:</text> -	<button label="Perfil..." label_selected="" name="BtnCreator"/> -	<text name="LabelOwnerTitle">Propietario:</text> -	<button label="Perfil..." label_selected="" name="BtnOwner"/> -	<text name="LabelAcquiredTitle">Adquirido:</text> -	<text name="LabelAcquiredDate">May Mié 24 12:50:46 2006</text> -	<text name="OwnerLabel">Tú:</text> -	<check_box label="Editar" name="CheckOwnerModify"/> -	<check_box label="Copiarlo" left_delta="88" name="CheckOwnerCopy"/> -	<check_box label="Revender" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel">Cualquiera:</text> -	<check_box label="Copiar" name="CheckEveryoneCopy"/> -	<text name="GroupLabel">Grupo:</text> -	<check_box label="Compartir" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel" width="230">Próximo propietario:</text> -	<check_box label="Editar" name="CheckNextOwnerModify"/> -	<check_box label="Copiarlo" left_delta="88" name="CheckNextOwnerCopy"/> -	<check_box label="Revender" name="CheckNextOwnerTransfer"/> -	<check_box label="En venta" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Copiar" name="Copy"/> -		<combo_box.item label="Contenidos" name="Contents"/> -		<combo_box.item label="Original" name="Original"/> -	</combo_box> -	<spinner label="Precio:" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/es/panel_main_inventory.xml b/indra/newview/skins/default/xui/es/panel_main_inventory.xml index 1252c7ce0d..bf1205046b 100644 --- a/indra/newview/skins/default/xui/es/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/es/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		[ITEM_COUNT] Objetos y [CATEGORY_COUNT] Carpetas Obtenidos [FILTER]  	</panel.string> +    <panel.string name="inventory_title">INVENTARIO</panel.string>  	<text name="ItemcountText">  		Ítems:  	</text> diff --git a/indra/newview/skins/default/xui/fr/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/fr/floater_inventory_item_properties.xml deleted file mode 100644 index 1d4e7c818f..0000000000 --- a/indra/newview/skins/default/xui/fr/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="PROPRIÉTÉS DES ARTICLES DE L'INVENTAIRE"> -	<floater.string name="unknown">(inconnu)</floater.string> -	<floater.string name="public">(public)</floater.string> -	<floater.string name="you_can">Vous pouvez :</floater.string> -	<floater.string name="owner_can">Le propriétaire peut :</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">Nom :</text> -	<text name="LabelItemDescTitle">Description :</text> -	<text name="LabelCreatorTitle">Créateur :</text> -	<button label="Profil..." label_selected="" name="BtnCreator"/> -	<text name="LabelOwnerTitle">Propriétaire :</text> -	<button label="Profil..." label_selected="" name="BtnOwner"/> -	<text name="LabelAcquiredTitle">Acquis :</text> -	<text name="LabelAcquiredDate">Wed May 24 12:50:46 2006</text> -	<text name="OwnerLabel">Vous :</text> -	<check_box label="Modifier" name="CheckOwnerModify"/> -	<check_box label="Copier" name="CheckOwnerCopy"/> -	<check_box label="Revendre" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel" width="80">N'importe qui :</text> -	<check_box label="Copier" name="CheckEveryoneCopy"/> -	<text name="GroupLabel" width="80">Groupe :</text> -	<check_box label="Partager" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel" width="192">Le prochain propriétaire :</text> -	<check_box label="Modifier" name="CheckNextOwnerModify"/> -	<check_box label="Copier" name="CheckNextOwnerCopy"/> -	<check_box label="Revendre" name="CheckNextOwnerTransfer"/> -	<check_box label="À vendre" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Copier" name="Copy"/> -		<combo_box.item label="Contenu" name="Contents"/> -		<combo_box.item label="Original" name="Original"/> -	</combo_box> -	<spinner label="Prix :" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/fr/panel_main_inventory.xml b/indra/newview/skins/default/xui/fr/panel_main_inventory.xml index 5bcee89752..5bf4d6c15d 100644 --- a/indra/newview/skins/default/xui/fr/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/fr/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		Recherche effectuée [ITEM_COUNT] d'articles et [CATEGORY_COUNT] de dossiers [FILTER]  	</panel.string> +    <panel.string name="inventory_title">INVENTAIRE</panel.string>  	<text name="ItemcountText">  		Articles :  	</text> diff --git a/indra/newview/skins/default/xui/it/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/it/floater_inventory_item_properties.xml deleted file mode 100644 index 8cf680b3f0..0000000000 --- a/indra/newview/skins/default/xui/it/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="CARATTERISTICHE DELL'ARTICOLO IN INVENTARIO"> -	<floater.string name="unknown">(sconosciuto)</floater.string> -	<floater.string name="public">(pubblico)</floater.string> -	<floater.string name="you_can">Tu puoi:</floater.string> -	<floater.string name="owner_can">Il proprietario può:</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">Nome:</text> -	<text name="LabelItemDescTitle">Descrizione:</text> -	<text name="LabelCreatorTitle">Creatore:</text> -	<button label="Profilo..." label_selected="" name="BtnCreator"/> -	<text name="LabelOwnerTitle">proprietario:</text> -	<button label="Profilo..." label_selected="" name="BtnOwner"/> -	<text name="LabelAcquiredTitle">Acquisito:</text> -	<text name="LabelAcquiredDate">Wed May 24 12:50:46 2006</text> -	<text name="OwnerLabel">Tu:</text> -	<check_box label="Modifica" name="CheckOwnerModify"/> -	<check_box label="Copiare" left_delta="88" name="CheckOwnerCopy"/> -	<check_box label="Rivendi" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel">Chiunque:</text> -	<check_box label="Copia" name="CheckEveryoneCopy"/> -	<text name="GroupLabel">Gruppo:</text> -	<check_box label="Condividi" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel" width="230">Proprietario successivo:</text> -	<check_box label="Modifica" name="CheckNextOwnerModify"/> -	<check_box label="Copiare" left_delta="88" name="CheckNextOwnerCopy"/> -	<check_box label="Rivendi" name="CheckNextOwnerTransfer"/> -	<check_box label="In vendita" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Copia" name="Copy"/> -		<combo_box.item label="Contenuti" name="Contents"/> -		<combo_box.item label="Originale" name="Original"/> -	</combo_box> -	<spinner label="Prezzo:" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/it/panel_main_inventory.xml b/indra/newview/skins/default/xui/it/panel_main_inventory.xml index 5d11967cee..d6890229e7 100644 --- a/indra/newview/skins/default/xui/it/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/it/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		Recuperati [ITEM_COUNT] oggetti e [CATEGORY_COUNT] cartelle [FILTER]  	</panel.string> +    <panel.string name="inventory_title">INVENTARIO</panel.string>  	<text name="ItemcountText">  		Oggetti:  	</text> diff --git a/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml deleted file mode 100644 index 2a8c9f83b2..0000000000 --- a/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="インベントリアイテムのプロパティ"> -	<floater.string name="unknown"> -		(不明) -	</floater.string> -	<floater.string name="public"> -		(公共) -	</floater.string> -	<floater.string name="you_can"> -		できること: -	</floater.string> -	<floater.string name="owner_can"> -		オーナーは次のことができます: -	</floater.string> -	<floater.string name="acquiredDate"> -		[year,datetime,local]年[mth,datetime,local]月[day,datetime,local]日[wkday,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] -	</floater.string> -	<text name="LabelItemNameTitle"> -		名前: -	</text> -	<text name="LabelItemDescTitle"> -		説明: -	</text> -	<text name="LabelCreatorTitle"> -		制作者: -	</text> -	<button label="プロフィール…" label_selected="" name="BtnCreator"/> -	<text name="LabelOwnerTitle"> -		所有者: -	</text> -	<button label="プロフィール…" label_selected="" name="BtnOwner"/> -	<text name="LabelAcquiredTitle"> -		入手日時: -	</text> -	<text name="LabelAcquiredDate"> -		2006年5月24日水曜日 12:50:46 -	</text> -	<text name="OwnerLabel"> -		あなた: -	</text> -	<check_box label="編集" name="CheckOwnerModify"/> -	<check_box label="コピー" name="CheckOwnerCopy"/> -	<check_box label="再販・譲渡" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel"> -		全員: -	</text> -	<check_box label="コピー" name="CheckEveryoneCopy"/> -	<text name="GroupLabel"> -		グループ: -	</text> -	<check_box label="共有" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel"> -		次の所有者: -	</text> -	<check_box label="編集" name="CheckNextOwnerModify"/> -	<check_box label="コピー" name="CheckNextOwnerCopy"/> -	<check_box label="再販・譲渡" name="CheckNextOwnerTransfer"/> -	<check_box label="売り出し中" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="コピー" name="Copy"/> -		<combo_box.item label="コンテンツ" name="Contents"/> -		<combo_box.item label="オリジナル" name="Original"/> -	</combo_box> -	<spinner label="価格:" name="Edit Cost"/> -	<text name="CurrencySymbol"> -		L$ -	</text> -</floater> diff --git a/indra/newview/skins/default/xui/ja/panel_main_inventory.xml b/indra/newview/skins/default/xui/ja/panel_main_inventory.xml index 9d78beb9da..a38492c6d1 100644 --- a/indra/newview/skins/default/xui/ja/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/ja/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		[ITEM_COUNT]個のアイテムと[CATEGORY_COUNT]個のフォルダーを取得しました。[FILTER]  	</panel.string> +    <panel.string name="inventory_title">インベントリ</panel.string>  	<text name="ItemcountText">  		アイテム:  	</text> diff --git a/indra/newview/skins/default/xui/pl/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/pl/floater_inventory_item_properties.xml deleted file mode 100644 index d2844e117f..0000000000 --- a/indra/newview/skins/default/xui/pl/floater_inventory_item_properties.xml +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<floater name="item properties" title="WŁAŚCIWOŚCI OBIEKTÓW W SZAFIE"> -	<floater.string name="unknown"> -		(nieznany) -	</floater.string> -	<floater.string name="public"> -		(publiczny) -	</floater.string> -	<floater.string name="you_can"> -		Ty możesz: -	</floater.string> -	<floater.string name="owner_can"> -		Właściciel może: -	</floater.string> -	<text name="LabelItemNameTitle"> -		Nazwa: -	</text> -	<text name="LabelItemDescTitle"> -		Opis: -	</text> -	<text name="LabelCreatorTitle"> -		Twórca: -	</text> -	<button label="Profil..." name="BtnCreator" /> -	<text name="LabelOwnerTitle"> -		Właściciel: -	</text> -	<button label="Profil..." name="BtnOwner" /> -	<text name="LabelAcquiredTitle"> -		Nabyte: -	</text> -	<text name="OwnerLabel"> -		Ty: -	</text> -	<check_box label="Modyfikacja" name="CheckOwnerModify" /> -	<check_box label="Kopiowanie" name="CheckOwnerCopy" /> -	<check_box label="Transferowanie" name="CheckOwnerTransfer" /> -	<text name="AnyoneLabel"> -		Każdy: -	</text> -	<check_box label="Kopiowanie" name="CheckEveryoneCopy" /> -	<text name="GroupLabel"> -		Grupa: -	</text> -	<check_box label="Udostępnij" name="CheckShareWithGroup" /> -	<text name="NextOwnerLabel"> -		Nast. właściciel: -	</text> -	<check_box label="Modyfikacja" name="CheckNextOwnerModify" /> -	<check_box label="Kopiowanie" name="CheckNextOwnerCopy" /> -	<check_box label="Transferowanie" name="CheckNextOwnerTransfer" /> -	<check_box label="Sprzedaż" name="CheckPurchase" /> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Kopia" name="Copy" /> -		<combo_box.item label="Zawartość" name="Contents" /> -		<combo_box.item label="Oryginał" name="Original" /> -	</combo_box> -	<spinner name="Edit Cost" label="Cena:" /> -</floater> diff --git a/indra/newview/skins/default/xui/pl/panel_main_inventory.xml b/indra/newview/skins/default/xui/pl/panel_main_inventory.xml index dc254e246f..1011c38378 100644 --- a/indra/newview/skins/default/xui/pl/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/pl/panel_main_inventory.xml @@ -6,6 +6,7 @@  	<panel.string name="ItemcountCompleted">  		[ITEM_COUNT] obiekty [FILTER]  	</panel.string> +    <panel.string name="inventory_title">MOJA SZAFA</panel.string>  	<text name="ItemcountText">  		Obiekty:  	</text> diff --git a/indra/newview/skins/default/xui/pt/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/pt/floater_inventory_item_properties.xml deleted file mode 100644 index 5f04c08531..0000000000 --- a/indra/newview/skins/default/xui/pt/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="PROPRIEDADES DE ITEM NO INVENTÁRIO"> -	<floater.string name="unknown">(desconhecido)</floater.string> -	<floater.string name="public">(público)</floater.string> -	<floater.string name="you_can">Você pode:</floater.string> -	<floater.string name="owner_can">Proprietário pode :</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">Nome:</text> -	<text name="LabelItemDescTitle">Descrição:</text> -	<text name="LabelCreatorTitle">Criador:</text> -	<button label="Perfil..." label_selected="" name="BtnCreator"/> -	<text name="LabelOwnerTitle">Dono:</text> -	<button label="Perfil..." label_selected="" name="BtnOwner"/> -	<text name="LabelAcquiredTitle">Adquirido:</text> -	<text name="LabelAcquiredDate">Qua Mai 24 12:50:46 2006</text> -	<text name="OwnerLabel">Você:</text> -	<check_box label="Editar" name="CheckOwnerModify"/> -	<check_box label="Copiar" name="CheckOwnerCopy"/> -	<check_box label="Revender" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel">Todos:</text> -	<check_box label="Cortar" name="CheckEveryoneCopy"/> -	<text name="GroupLabel">Grupo:</text> -	<check_box label="Compartilhar" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel" width="230">Próximo proprietário:</text> -	<check_box label="Editar" name="CheckNextOwnerModify"/> -	<check_box label="Copiar" name="CheckNextOwnerCopy"/> -	<check_box label="Revender" name="CheckNextOwnerTransfer"/> -	<check_box label="À venda" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Cortar" name="Copy"/> -		<combo_box.item label="Conteúdo" name="Contents"/> -		<combo_box.item label="Original" name="Original"/> -	</combo_box> -	<spinner label="Preço:" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/pt/panel_main_inventory.xml b/indra/newview/skins/default/xui/pt/panel_main_inventory.xml index 009b5b3193..e0cf528468 100644 --- a/indra/newview/skins/default/xui/pt/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/pt/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		Itens [ITEM_COUNT] e Pastas [CATEGORY_COUNT] Reunidos [FILTER]  	</panel.string> +    <panel.string name="inventory_title">INVENTÁRIO</panel.string>  	<text name="ItemcountText">  		Itens:  	</text> diff --git a/indra/newview/skins/default/xui/ru/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/ru/floater_inventory_item_properties.xml deleted file mode 100644 index c988825756..0000000000 --- a/indra/newview/skins/default/xui/ru/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="СВОЙСТВА ПРЕДМЕТА"> -	<floater.string name="unknown">(неизвестно)</floater.string> -	<floater.string name="public">(публичное)</floater.string> -	<floater.string name="you_can">Вы можете:</floater.string> -	<floater.string name="owner_can">Владелец может:</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local], [day,datetime,local] [mth,datetime,local] [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">Название:</text> -	<text name="LabelItemDescTitle">Описание:</text> -	<text name="LabelCreatorTitle">Создатель:</text> -	<button label="Профиль…" name="BtnCreator"/> -	<text name="LabelOwnerTitle">Владелец:</text> -	<button label="Профиль…" name="BtnOwner"/> -	<text name="LabelAcquiredTitle">Приобретено:</text> -	<text name="LabelAcquiredDate">Ср 24 Май 12:50:46 2006</text> -	<text name="OwnerLabel">Вы:</text> -	<check_box label="Изменить" name="CheckOwnerModify"/> -	<check_box label="Копировать" name="CheckOwnerCopy"/> -	<check_box label="Перепродать" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel">Все:</text> -	<check_box label="Копировать" name="CheckEveryoneCopy"/> -	<text name="GroupLabel">Группа:</text> -	<check_box label="Поделиться" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel">Следующий владелец:</text> -	<check_box label="Изменить" name="CheckNextOwnerModify"/> -	<check_box label="Копировать" name="CheckNextOwnerCopy"/> -	<check_box label="Перепродать" name="CheckNextOwnerTransfer"/> -	<check_box label="Для продажи" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Копировать" name="Copy"/> -		<combo_box.item label="Содержимое" name="Contents"/> -		<combo_box.item label="Оригинал" name="Original"/> -	</combo_box> -	<spinner label="Цена:" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/ru/panel_main_inventory.xml b/indra/newview/skins/default/xui/ru/panel_main_inventory.xml index f2502bf6d3..b473fb8f98 100644 --- a/indra/newview/skins/default/xui/ru/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/ru/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		Выборка [ITEM_COUNT] предметов и [CATEGORY_COUNT] папок [FILTER]  	</panel.string> +    <panel.string name="inventory_title">ИНВЕНТАРЬ</panel.string>  	<text name="ItemcountText">  		Вещи:  	</text> diff --git a/indra/newview/skins/default/xui/tr/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/tr/floater_inventory_item_properties.xml deleted file mode 100644 index c6a5515c6e..0000000000 --- a/indra/newview/skins/default/xui/tr/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="ENVANTER ÖGESİ ÖZELLİKLERİ"> -	<floater.string name="unknown">(bilinmiyor)</floater.string> -	<floater.string name="public">(kamuya açık)</floater.string> -	<floater.string name="you_can">Şunu yapabilirsiniz:</floater.string> -	<floater.string name="owner_can">Sahip şunu yapabilir:</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">Ad:</text> -	<text name="LabelItemDescTitle">Açıklama:</text> -	<text name="LabelCreatorTitle">Oluşturan:</text> -	<button label="Profil..." name="BtnCreator"/> -	<text name="LabelOwnerTitle">Sahip:</text> -	<button label="Profil..." name="BtnOwner"/> -	<text name="LabelAcquiredTitle">Alınan:</text> -	<text name="LabelAcquiredDate">24 Mayıs Çarş 12:50:46 2006</text> -	<text name="OwnerLabel">Siz:</text> -	<check_box label="Düzenle" name="CheckOwnerModify"/> -	<check_box label="Kopyala" name="CheckOwnerCopy"/> -	<check_box label="Tekrar Sat" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel">Herkes:</text> -	<check_box label="Kopyala" name="CheckEveryoneCopy"/> -	<text name="GroupLabel">Grup:</text> -	<check_box label="Paylaş" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel">Sonraki sahip:</text> -	<check_box label="Düzenle" name="CheckNextOwnerModify"/> -	<check_box label="Kopyala" name="CheckNextOwnerCopy"/> -	<check_box label="Tekrar Sat" name="CheckNextOwnerTransfer"/> -	<check_box label="Satılık" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="Kopyala" name="Copy"/> -		<combo_box.item label="İçerik" name="Contents"/> -		<combo_box.item label="Orijinal" name="Original"/> -	</combo_box> -	<spinner label="Fiyat:" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/tr/panel_main_inventory.xml b/indra/newview/skins/default/xui/tr/panel_main_inventory.xml index a11fd98b9a..7e98078635 100644 --- a/indra/newview/skins/default/xui/tr/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/tr/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		[ITEM_COUNT] Öğe ve [CATEGORY_COUNT] Klasör Alındı [FILTER]  	</panel.string> +    <panel.string name="inventory_title">ENVANTER</panel.string>  	<text name="ItemcountText">  		Ögeler:  	</text> diff --git a/indra/newview/skins/default/xui/zh/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/zh/floater_inventory_item_properties.xml deleted file mode 100644 index 4f17b96579..0000000000 --- a/indra/newview/skins/default/xui/zh/floater_inventory_item_properties.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="item properties" title="收納區物品屬性"> -	<floater.string name="unknown">(未知)</floater.string> -	<floater.string name="public">(公開)</floater.string> -	<floater.string name="you_can">你可以:</floater.string> -	<floater.string name="owner_can">所有人可以:</floater.string> -	<floater.string name="acquiredDate">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</floater.string> -	<text name="LabelItemNameTitle">名稱:</text> -	<text name="LabelItemDescTitle">描述:</text> -	<text name="LabelCreatorTitle">創造者:</text> -	<button label="檔案..." name="BtnCreator"/> -	<text name="LabelOwnerTitle">所有人:</text> -	<button label="檔案..." name="BtnOwner"/> -	<text name="LabelAcquiredTitle">取得於:</text> -	<text name="LabelAcquiredDate">Wed May 24 12:50:46 2006</text> -	<text name="OwnerLabel">你:</text> -	<check_box label="編輯" name="CheckOwnerModify"/> -	<check_box label="恚庨" name="CheckOwnerCopy"/> -	<check_box label="轉售" name="CheckOwnerTransfer"/> -	<text name="AnyoneLabel">任何人:</text> -	<check_box label="恚庨" name="CheckEveryoneCopy"/> -	<text name="GroupLabel">群組:</text> -	<check_box label="分享" name="CheckShareWithGroup"/> -	<text name="NextOwnerLabel">下一個所有人:</text> -	<check_box label="編輯" name="CheckNextOwnerModify"/> -	<check_box label="恚庨" name="CheckNextOwnerCopy"/> -	<check_box label="轉售" name="CheckNextOwnerTransfer"/> -	<check_box label="出售" name="CheckPurchase"/> -	<combo_box name="ComboBoxSaleType"> -		<combo_box.item label="複製" name="Copy"/> -		<combo_box.item label="內容" name="Contents"/> -		<combo_box.item label="原件" name="Original"/> -	</combo_box> -	<spinner label="價格:" name="Edit Cost"/> -	<text name="CurrencySymbol">L$</text> -</floater> diff --git a/indra/newview/skins/default/xui/zh/panel_main_inventory.xml b/indra/newview/skins/default/xui/zh/panel_main_inventory.xml index 1a28f4c3b5..9ffa9323cc 100644 --- a/indra/newview/skins/default/xui/zh/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/zh/panel_main_inventory.xml @@ -9,6 +9,7 @@  	<panel.string name="ItemcountUnknown">  		擷取了[ITEM_COUNT]個物項及[CATEGORY_COUNT]個資料夾[FILTER]  	</panel.string> +    <panel.string name="inventory_title">收納區</panel.string>  	<text name="ItemcountText">  		物品:  	</text>  | 
