diff options
| -rwxr-xr-x | doc/contributions.txt | 1 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 22 | ||||
| -rw-r--r-- | indra/newview/llfloaterlinkreplace.cpp | 396 | ||||
| -rw-r--r-- | indra/newview/llfloaterlinkreplace.h | 127 | ||||
| -rw-r--r-- | indra/newview/llinventorybridge.cpp | 25 | ||||
| -rw-r--r-- | indra/newview/llinventorybridge.h | 3 | ||||
| -rw-r--r-- | indra/newview/llinventoryfunctions.cpp | 20 | ||||
| -rw-r--r-- | indra/newview/llpanelmaininventory.cpp | 20 | ||||
| -rw-r--r-- | indra/newview/llviewerfloaterreg.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/floater_linkreplace.xml | 106 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/menu_inventory.xml | 8 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/menu_inventory_gear_default.xml | 8 | 
13 files changed, 740 insertions, 0 deletions
| diff --git a/doc/contributions.txt b/doc/contributions.txt index 0fb6110adb..2272ec7922 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -214,6 +214,7 @@ Ansariel Hiller  	MAINT-6953  	MAINT-7028  	MAINT-7059 +	MAINT-6519  Aralara Rajal  Arare Chantilly  	CHUIBUG-191 diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4241ede365..b4e930d062 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -254,6 +254,7 @@ set(viewer_SOURCE_FILES      llfloaterlagmeter.cpp      llfloaterland.cpp      llfloaterlandholdings.cpp +    llfloaterlinkreplace.cpp      llfloaterloadprefpreset.cpp      llfloatermarketplacelistings.cpp      llfloatermap.cpp @@ -876,6 +877,7 @@ set(viewer_HEADER_FILES      llfloaterlagmeter.h      llfloaterland.h      llfloaterlandholdings.h +    llfloaterlinkreplace.h      llfloaterloadprefpreset.h      llfloatermap.h      llfloatermarketplacelistings.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 962537bc12..f490551406 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5450,6 +5450,28 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>LinkReplaceBatchSize</key> +    <map> +      <key>Comment</key> +      <string>The maximum size of a batch in a link replace operation</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>U32</string> +      <key>Value</key> +      <integer>25</integer> +    </map> +    <key>LinkReplaceBatchPauseTime</key> +    <map> +      <key>Comment</key> +      <string>The time in seconds between two batches in a link replace operation</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>F32</string> +      <key>Value</key> +      <real>1.0</real> +    </map>      <key>LipSyncAah</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llfloaterlinkreplace.cpp b/indra/newview/llfloaterlinkreplace.cpp new file mode 100644 index 0000000000..6576af2ea3 --- /dev/null +++ b/indra/newview/llfloaterlinkreplace.cpp @@ -0,0 +1,396 @@ +/** + * @file llfloaterlinkreplace.cpp + * @brief Allows replacing link targets in inventory links + * @author Ansariel Hiller + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, 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 "llfloaterlinkreplace.h" + +#include "llagent.h" +#include "llappearancemgr.h" +#include "lllineeditor.h" +#include "lltextbox.h" +#include "llviewercontrol.h" + +LLFloaterLinkReplace::LLFloaterLinkReplace(const LLSD& key) +	: LLFloater(key), +	LLEventTimer(gSavedSettings.getF32("LinkReplaceBatchPauseTime")), +	mRemainingItems(0), +	mSourceUUID(LLUUID::null), +	mTargetUUID(LLUUID::null), +	mInstance(NULL), +	mBatchSize(gSavedSettings.getU32("LinkReplaceBatchSize")) +{ +	mEventTimer.stop(); +	mInstance = this; +} + +LLFloaterLinkReplace::~LLFloaterLinkReplace() +{ +	mInstance = NULL; +} + +BOOL LLFloaterLinkReplace::postBuild() +{ +	mStartBtn = getChild<LLButton>("btn_start"); +	mStartBtn->setCommitCallback(boost::bind(&LLFloaterLinkReplace::onStartClicked, this)); + +	mRefreshBtn = getChild<LLButton>("btn_refresh"); +	mRefreshBtn->setCommitCallback(boost::bind(&LLFloaterLinkReplace::checkEnableStart, this)); + +	mSourceEditor = getChild<LLInventoryLinkReplaceDropTarget>("source_uuid_editor"); +	mTargetEditor = getChild<LLInventoryLinkReplaceDropTarget>("target_uuid_editor"); + +	mSourceEditor->setDADCallback(boost::bind(&LLFloaterLinkReplace::onSourceItemDrop, this, _1)); +	mTargetEditor->setDADCallback(boost::bind(&LLFloaterLinkReplace::onTargetItemDrop, this, _1)); + +	mStatusText = getChild<LLTextBox>("status_text"); + +	return TRUE; +} + +void LLFloaterLinkReplace::onOpen(const LLSD& key) +{ +	if (key.asUUID().notNull()) +	{ +		LLUUID item_id = key.asUUID(); +		LLViewerInventoryItem* item = gInventory.getItem(item_id); +		mSourceEditor->setItem(item); +		onSourceItemDrop(item->getLinkedUUID()); +	} +	else +	{ +		checkEnableStart(); +	} +} + +void LLFloaterLinkReplace::onSourceItemDrop(const LLUUID& source_item_id) +{ +	mSourceUUID = source_item_id; +	checkEnableStart(); +} + +void LLFloaterLinkReplace::onTargetItemDrop(const LLUUID& target_item_id) +{ +	mTargetUUID = target_item_id; +	checkEnableStart(); +} + +void LLFloaterLinkReplace::updateFoundLinks() +{ +	LLInventoryModel::item_array_t items; +	LLInventoryModel::cat_array_t cat_array; +	LLLinkedItemIDMatches is_linked_item_match(mSourceUUID); +	gInventory.collectDescendentsIf(gInventory.getRootFolderID(), +									cat_array, +									items, +									LLInventoryModel::INCLUDE_TRASH, +									is_linked_item_match); +	mRemainingItems = (U32)items.size(); + +	LLStringUtil::format_map_t args; +	args["NUM"] = llformat("%d", mRemainingItems); +	mStatusText->setText(getString("ItemsFound", args)); +} + +void LLFloaterLinkReplace::checkEnableStart() +{ +	if (mSourceUUID.notNull() && mTargetUUID.notNull() && mSourceUUID == mTargetUUID) +	{ +		mStatusText->setText(getString("ItemsIdentical")); +	} +	else if (mSourceUUID.notNull()) +	{ +		updateFoundLinks(); +	} + +	mStartBtn->setEnabled(mRemainingItems > 0 && mSourceUUID.notNull() && mTargetUUID.notNull() && mSourceUUID != mTargetUUID); +} + +void LLFloaterLinkReplace::onStartClicked() +{ +	LL_INFOS() << "Starting inventory link replace" << LL_ENDL; + +	if (mSourceUUID.isNull() || mTargetUUID.isNull()) +	{ +		LL_WARNS() << "Cannot replace. Either source or target UUID is null." << LL_ENDL; +		return; +	} + +	if (mSourceUUID == mTargetUUID) +	{ +		LL_WARNS() << "Cannot replace. Source and target are identical." << LL_ENDL; +		return; +	} + +	LLInventoryModel::cat_array_t cat_array; +	LLLinkedItemIDMatches is_linked_item_match(mSourceUUID); +	gInventory.collectDescendentsIf(gInventory.getRootFolderID(), +									cat_array, +									mRemainingInventoryItems, +									LLInventoryModel::INCLUDE_TRASH, +									is_linked_item_match); +	LL_INFOS() << "Found " << mRemainingInventoryItems.size() << " inventory links that need to be replaced." << LL_ENDL; + +	if (mRemainingInventoryItems.size() > 0) +	{ +		LLViewerInventoryItem* target_item = gInventory.getItem(mTargetUUID); +		if (target_item) +		{ +			mRemainingItems = (U32)mRemainingInventoryItems.size(); + +			LLStringUtil::format_map_t args; +			args["NUM"] = llformat("%d", mRemainingItems); +			mStatusText->setText(getString("ItemsRemaining", args)); + +			mStartBtn->setEnabled(FALSE); +			mRefreshBtn->setEnabled(FALSE); + +			mEventTimer.start(); +			tick(); +		} +		else +		{ +			mStatusText->setText(getString("TargetNotFound")); +			LL_WARNS() << "Link replace target not found." << LL_ENDL; +		} +	} +} + +void LLFloaterLinkReplace::linkCreatedCallback(const LLUUID& old_item_id, +												const LLUUID& target_item_id, +												bool needs_wearable_ordering_update, +												bool needs_description_update, +												const LLUUID& outfit_folder_id) +{ +	LL_DEBUGS() << "Inventory link replace:" << LL_NEWLINE +		<< " - old_item_id = " << old_item_id.asString() << LL_NEWLINE +		<< " - target_item_id = " << target_item_id.asString() << LL_NEWLINE +		<< " - order update = " << (needs_wearable_ordering_update ? "true" : "false") << LL_NEWLINE +		<< " - description update = " << (needs_description_update ? "true" : "false") << LL_NEWLINE +		<< " - outfit_folder_id = " << outfit_folder_id.asString() << LL_ENDL; + +	// If we are replacing an object, bodypart or gesture link within an outfit folder, +	// we need to change the actual description of the link itself. LLAppearanceMgr *should* +	// have created COF links that will be used to save the outfit with an empty description. +	// Since link_inventory_array() will set the description of the linked item for the link +	// itself, this will lead to a dirty outfit state when the outfit with the replaced +	// link is worn. So we have to correct this. +	if (needs_description_update && outfit_folder_id.notNull()) +	{ +		LLInventoryModel::item_array_t items; +		LLInventoryModel::cat_array_t cats; +		LLLinkedItemIDMatches is_target_link(target_item_id); +		gInventory.collectDescendentsIf(outfit_folder_id, +										cats, +										items, +										LLInventoryModel::EXCLUDE_TRASH, +										is_target_link); + +		for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) +		{ +			LLPointer<LLViewerInventoryItem> item = *it; + +			if ((item->getType() == LLAssetType::AT_BODYPART || +				item->getType() == LLAssetType::AT_OBJECT || +				item->getType() == LLAssetType::AT_GESTURE) +				&& !item->getActualDescription().empty()) +			{ +				LL_DEBUGS() << "Updating description for " << item->getName() << LL_ENDL; + +				LLSD updates; +				updates["desc"] = ""; +				update_inventory_item(item->getUUID(), updates, LLPointer<LLInventoryCallback>(NULL)); +			} +		} +	} + +	LLUUID outfit_update_folder = LLUUID::null; +	if (needs_wearable_ordering_update && outfit_folder_id.notNull()) +	{ +		// If a wearable item was involved in the link replace operation and replaced +		// a link in an outfit folder, we need to update the clothing ordering information +		// *after* the original link has been removed. LLAppearanceMgr abuses the actual link +		// description to store the clothing ordering information it. We will have to update +		// the clothing ordering information or the outfit will be in dirty state when worn. +		outfit_update_folder = outfit_folder_id; +	} + +	LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(&LLFloaterLinkReplace::itemRemovedCallback, this, outfit_update_folder)); +	remove_inventory_object(old_item_id, cb); +} + +void LLFloaterLinkReplace::itemRemovedCallback(const LLUUID& outfit_folder_id) +{ +	if (outfit_folder_id.notNull()) +	{ +		LLAppearanceMgr::getInstance()->updateClothingOrderingInfo(outfit_folder_id); +	} + +	if (mInstance) +	{ +		decreaseOpenItemCount(); +	} +} + +void LLFloaterLinkReplace::decreaseOpenItemCount() +{ +	mRemainingItems--; + +	if (mRemainingItems == 0) +	{ +		mStatusText->setText(getString("ReplaceFinished")); +		mStartBtn->setEnabled(TRUE); +		mRefreshBtn->setEnabled(TRUE); +		mEventTimer.stop(); +		LL_INFOS() << "Inventory link replace finished." << LL_ENDL; +	} +	else +	{ +		LLStringUtil::format_map_t args; +		args["NUM"] = llformat("%d", mRemainingItems); +		mStatusText->setText(getString("ItemsRemaining", args)); +		LL_DEBUGS() << "Inventory link replace: " << mRemainingItems << " links remaining..." << LL_ENDL; +	} +} + +BOOL LLFloaterLinkReplace::tick() +{ +	LL_DEBUGS() << "Calling tick - remaining items = " << mRemainingInventoryItems.size() << LL_ENDL; + +	LLInventoryModel::item_array_t current_batch; + +	for (U32 i = 0; i < mBatchSize; ++i) +	{ +		if (!mRemainingInventoryItems.size()) +		{ +			mEventTimer.stop(); +			break; +		} + +		current_batch.push_back(mRemainingInventoryItems.back()); +		mRemainingInventoryItems.pop_back(); +	} +	processBatch(current_batch); + +	return FALSE; +} + +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); + +	for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) +	{ +		LLPointer<LLInventoryItem> source_item = *it; + +		if (source_item->getParentUUID() != cof_folder_id) +		{ +			bool is_outfit_folder = gInventory.isObjectDescendentOf(source_item->getParentUUID(), outfit_folder_id); +			// If either the new or old item in the COF is a wearable, we need to update wearable ordering after the link has been replaced +			bool needs_wearable_ordering_update = is_outfit_folder && source_item->getType() == LLAssetType::AT_CLOTHING || target_item->getType() == LLAssetType::AT_CLOTHING; +			// Other items in the COF need a description update (description of the actual link item must be empty) +			bool needs_description_update = is_outfit_folder && target_item->getType() != LLAssetType::AT_CLOTHING; + +			LL_DEBUGS() << "is_outfit_folder = " << (is_outfit_folder ? "true" : "false") << LL_NEWLINE +				<< "needs_wearable_ordering_update = " << (needs_wearable_ordering_update ? "true" : "false") << LL_NEWLINE +				<< "needs_description_update = " << (needs_description_update ? "true" : "false") << LL_ENDL; + +			LLInventoryObject::const_object_list_t obj_array; +			obj_array.push_back(LLConstPointer<LLInventoryObject>(target_item)); +			LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(&LLFloaterLinkReplace::linkCreatedCallback, +																											this, +																											source_item->getUUID(), +																											target_item->getUUID(), +																											needs_wearable_ordering_update, +																											needs_description_update, +																											(is_outfit_folder ? source_item->getParentUUID() : LLUUID::null) )); +			link_inventory_array(source_item->getParentUUID(), obj_array, cb); +		} +		else +		{ +			decreaseOpenItemCount(); +		} +	} +} + + +////////////////////////////////////////////////////////////////////////////// +// LLInventoryLinkReplaceDropTarget + +static LLDefaultChildRegistry::Register<LLInventoryLinkReplaceDropTarget> r("inventory_link_replace_drop_target"); + +BOOL LLInventoryLinkReplaceDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +														   EDragAndDropType cargo_type, +														   void* cargo_data, +														   EAcceptance* accept, +														   std::string& tooltip_msg) +{ +	LLInventoryItem* item = (LLInventoryItem*)cargo_data; + +	if (cargo_type >= DAD_TEXTURE && cargo_type <= DAD_LINK && +		item && item->getActualType() != LLAssetType::AT_LINK_FOLDER && item->getType() != LLAssetType::AT_CATEGORY && +		( +			LLAssetType::lookupCanLink(item->getType()) || +			(item->getType() == LLAssetType::AT_LINK && !gInventory.getObject(item->getLinkedUUID())) // Broken Link! +		)) +	{ +		if (drop) +		{ +			setItem(item); +			if (!mDADSignal.empty()) +			{ +				mDADSignal(mItemID); +			} +		} +		else +		{ +			*accept = ACCEPT_YES_SINGLE; +		} +	} +	else +	{ +		*accept = ACCEPT_NO; +	} + +	return TRUE; +} + +void LLInventoryLinkReplaceDropTarget::setItem(LLInventoryItem* item) +{ +	if (item) +	{ +		mItemID = item->getLinkedUUID(); +		setText(item->getName()); +	} +	else +	{ +		mItemID.setNull(); +		setText(LLStringExplicit("")); +	} +} diff --git a/indra/newview/llfloaterlinkreplace.h b/indra/newview/llfloaterlinkreplace.h new file mode 100644 index 0000000000..377dd1d450 --- /dev/null +++ b/indra/newview/llfloaterlinkreplace.h @@ -0,0 +1,127 @@ +/** + * @file llfloaterlinkreplace.h + * @brief Allows replacing link targets in inventory links + * @author Ansariel Hiller + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, 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_FLOATERLINKREPLACE_H +#define LL_FLOATERLINKREPLACE_H + +#include "llfloater.h" +#include "lleventtimer.h" +#include "lllineeditor.h" +#include "llinventoryfunctions.h" +#include "llviewerinventory.h" + +class LLButton; +class LLTextBox; + +class LLInventoryLinkReplaceDropTarget : public LLLineEditor +{ +public: +	struct Params : public LLInitParam::Block<Params, LLLineEditor::Params> +	{ +		Params() +		{} +	}; + +	LLInventoryLinkReplaceDropTarget(const Params& p) +		: LLLineEditor(p) {} +	~LLInventoryLinkReplaceDropTarget() {} + +	typedef boost::signals2::signal<void(const LLUUID& id)> item_dad_callback_t; +	boost::signals2::connection setDADCallback(const item_dad_callback_t::slot_type& cb) +	{ +		return mDADSignal.connect(cb); +	} + +	virtual BOOL postBuild() +	{ +		setEnabled(FALSE); +		return LLLineEditor::postBuild(); +	} + +	virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +								   EDragAndDropType cargo_type, +								   void* cargo_data, +								   EAcceptance* accept, +								   std::string& tooltip_msg); + +	LLUUID getItemID() const { return mItemID; } +	void setItem(LLInventoryItem* item); + +private: +	LLUUID mItemID; + +	item_dad_callback_t mDADSignal; +}; + + +class LLFloaterLinkReplace : public LLFloater, LLEventTimer +{ +	LOG_CLASS(LLFloaterLinkReplace); + +public: +	LLFloaterLinkReplace(const LLSD& key); +	virtual ~LLFloaterLinkReplace(); + +	BOOL postBuild(); +	virtual void onOpen(const LLSD& key); + +	virtual BOOL tick(); + +private: +	void checkEnableStart(); +	void onStartClicked(); +	void decreaseOpenItemCount(); +	void updateFoundLinks(); +	void processBatch(LLInventoryModel::item_array_t items); + +	void linkCreatedCallback(const LLUUID& old_item_id, +								const LLUUID& target_item_id, +								bool needs_wearable_ordering_update, +								bool needs_description_update, +								const LLUUID& outfit_folder_id); +	void itemRemovedCallback(const LLUUID& outfit_folder_id); + +	void onSourceItemDrop(const LLUUID& source_item_id); +	void onTargetItemDrop(const LLUUID& target_item_id); + +	LLInventoryLinkReplaceDropTarget*	mSourceEditor; +	LLInventoryLinkReplaceDropTarget*	mTargetEditor; +	LLButton*							mStartBtn; +	LLButton*							mRefreshBtn; +	LLTextBox*							mStatusText; + +	LLUUID	mSourceUUID; +	LLUUID	mTargetUUID; +	U32		mRemainingItems; +	U32		mBatchSize; + +	LLInventoryModel::item_array_t	mRemainingInventoryItems; + +	LLFloaterLinkReplace* mInstance; +}; + +#endif // LL_FLOATERLINKREPLACE_H diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 555c19baac..bf4a2301ae 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -851,6 +851,7 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		getClipboardEntries(true, items, disabled_items, flags);  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -1051,6 +1052,20 @@ void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags,      items.push_back(std::string("Marketplace Listings Separator"));  } +void LLInvFVBridge::addLinkReplaceMenuOption(menuentry_vec_t& items, menuentry_vec_t& disabled_items) +{ +	const LLInventoryObject* obj = getInventoryObject(); + +	if (isAgentInventory() && obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getType() != LLAssetType::AT_LINK_FOLDER) +	{ +		items.push_back(std::string("Replace Links")); + +		if (mRoot->getSelectedCount() != 1) +		{ +			disabled_items.push_back(std::string("Replace Links")); +		} +	} +}  // *TODO: remove this  BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const @@ -5179,6 +5194,7 @@ void LLTextureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  			disabled_items.push_back(std::string("Save As"));  		}  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);	  } @@ -5251,6 +5267,7 @@ void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		items.push_back(std::string("Sound Play"));  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -5339,6 +5356,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		disabled_items.push_back(std::string("About Landmark"));  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -5641,6 +5659,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  			disabled_items.push_back(std::string("Conference Chat"));  		}  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -5910,6 +5929,7 @@ void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  			items.push_back(std::string("Activate"));  		}  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -5967,6 +5987,7 @@ void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		items.push_back(std::string("Animation Audition"));  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -6283,6 +6304,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  			}  		}  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -6511,6 +6533,7 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  			}  		}  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -6682,6 +6705,7 @@ void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		items.push_back(std::string("Properties"));  		addDeleteContextMenuOptions(items, disabled_items);  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } @@ -6733,6 +6757,7 @@ void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		getClipboardEntries(true, items, disabled_items, flags);  	} +	addLinkReplaceMenuOption(items, disabled_items);  	hide_context_entries(menu, items, disabled_items);  } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index b7d8c9d034..e6fcb6be96 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -148,6 +148,9 @@ protected:  	virtual void addMarketplaceContextMenuOptions(U32 flags,  											 menuentry_vec_t &items,  											 menuentry_vec_t &disabled_items); +	virtual void addLinkReplaceMenuOption(menuentry_vec_t& items, +										  menuentry_vec_t& disabled_items); +  protected:  	LLInvFVBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index f04d6cc753..bccc654fbf 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -2306,6 +2306,26 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  		// Clear the clipboard before we start adding things on it  		LLClipboard::instance().reset();  	} +	if ("replace_links" == action) +	{ +		LLSD params; +		if (root->getSelectedCount() == 1) +		{ +			LLFolderViewItem* folder_item = root->getSelectedItems().front(); +			LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); + +			if (bridge) +			{ +				LLInventoryObject* obj = bridge->getInventoryObject(); +				if (obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getActualType() != LLAssetType::AT_LINK_FOLDER) +				{ +					params = LLSD(obj->getUUID()); +				} +			} +		} +		LLFloaterReg::showInstance("linkreplace", params); +		return; +	}  	static const std::string change_folder_string = "change_folder_type_";  	if (action.length() > change_folder_string.length() &&  diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index adbcf377c0..ff54f83016 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -1159,6 +1159,26 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata)  		mFilterEditor->setText(item_name);  		mFilterEditor->setFocus(TRUE);  	} + +	if (command_name == "replace_links") +	{ +		LLSD params; +		LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); +		if (current_item) +		{ +			LLInvFVBridge* bridge = (LLInvFVBridge*)current_item->getViewModelItem(); + +			if (bridge) +			{ +				LLInventoryObject* obj = bridge->getInventoryObject(); +				if (obj && obj->getType() != LLAssetType::AT_CATEGORY && obj->getActualType() != LLAssetType::AT_LINK_FOLDER) +				{ +					params = LLSD(obj->getUUID()); +				} +			} +		} +		LLFloaterReg::showInstance("linkreplace", params); +	}  }  void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility ) diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 4c146679db..0ebacddd9b 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -83,6 +83,7 @@  #include "llfloaterlagmeter.h"  #include "llfloaterland.h"  #include "llfloaterlandholdings.h" +#include "llfloaterlinkreplace.h"  #include "llfloaterloadprefpreset.h"  #include "llfloatermap.h"  #include "llfloatermarketplacelistings.h" @@ -258,6 +259,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("lagmeter", "floater_lagmeter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLagMeter>);  	LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>); +	LLFloaterReg::add("linkreplace", "floater_linkreplace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLinkReplace>);  	LLFloaterReg::add("load_pref_preset", "floater_load_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLoadPrefPreset>);  	LLFloaterReg::add("mem_leaking", "floater_mem_leaking.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMemLeak>); diff --git a/indra/newview/skins/default/xui/en/floater_linkreplace.xml b/indra/newview/skins/default/xui/en/floater_linkreplace.xml new file mode 100644 index 0000000000..ece75e2576 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_linkreplace.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + name="linkreplace" + help_topic="linkreplace" + positioning="centered" + title="REPLACE INVENTORY LINKS" + width="333" + height="130" + save_rect="true" + can_minimize="true" + can_close="true"> +	<string name="Ready"> +		Ready... +	</string> +	<string name="TargetNotFound"> +		Target item not found. +	</string> +	<string name="ItemsIdentical"> +		Source and target are identical. +	</string> +	<string name="ItemsFound"> +		Found [NUM] inventory links. +	</string> +	<string name="ItemsRemaining"> +		Links remaining: [NUM] +	</string> +	<string name="ReplaceFinished"> +		Finished replacing inventory links. +	</string> +	<text +	 type="string" +	 follows="left|top" +	 font="SansSerif" +	 height="23" +	 layout="topleft" +	 left="10" +	 width="35" +	 name="source_label" +	 top="10"> +		Old: +	</text> +	<inventory_link_replace_drop_target +	 name="source_uuid_editor" +	 follows="left|top|right" +	 height="23" +	 layout="topleft" +	 left_pad="10" +	 max_length_bytes="255" +	 top_delta="-3" +	 right="-10" +	 tool_tip="Drag and drop the current inventory item here that should be replaced."/> +	<text +	 type="string" +	 follows="left|top" +	 font="SansSerif" +	 height="23" +	 layout="topleft" +	 left="10" +	 width="35" +	 name="target_label" +	 top_pad="10"> +		New: +	</text> +	<inventory_link_replace_drop_target +	 name="target_uuid_editor" +	 follows="left|top|right" +	 height="23" +	 layout="topleft" +	 left_pad="10" +	 max_length_bytes="255" +	 top_delta="-3" +	 right="-10" +	 tool_tip="Drag and drop new inventory item here."/> +	<text +	 type="string" +	 follows="left|top|right" +	 font="SansSerif" +	 height="20" +	 layout="topleft" +	 left="10" +	 right="-10" +	 name="status_text" +	 top_pad="10"> +		Ready... +	</text> +	<button +	 top_pad="5" +	 left="10" +	 height="22" +	 width="90" +	 follows="left|top" +	 mouse_opaque="true" +	 halign="center" +	 name="btn_refresh" +	 label="Refresh"/> +	<button +	 top_delta="0" +	 right="-10" +	 height="22" +	 width="90" +	 follows="right|top" +	 mouse_opaque="true" +	 halign="center" +	 name="btn_start" +	 label="Start"/> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 7b3a9a2e3e..8472185457 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -603,6 +603,14 @@           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" /> 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 d95541df80..3eacdbc781 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 @@ -146,6 +146,14 @@  			 function="Inventory.GearDefault.Enable"  			 parameter="find_links" />          </menu_item_call> +    <menu_item_call  +         label="Replace Links" +         layout="topleft" +         name="Replace Links"> +            <on_click +             function="Inventory.GearDefault.Custom.Action" +             parameter="replace_links" /> +        </menu_item_call>      <menu_item_separator       layout="topleft" /> | 
