From a00af78b95a62c613f9a2ac4a28a0017086c5ffa Mon Sep 17 00:00:00 2001
From: Loren Shih <seraph@lindenlab.com>
Date: Wed, 24 Jun 2009 21:36:42 +0000
Subject: merge -r125267:125272
 svn+ssh://svn.lindenlab.com/svn/linden/branches/avatar-pipeline/inventory-links__merge__viewer2.0.0-3-r125202
 to svn+ssh://svn.lindenlab.com/svn/linden/branches/viewer/viewer-2.0.0-3

---
 indra/llcommon/llassettype.cpp                     |  88 ++-
 indra/llcommon/llassettype.h                       |  32 +-
 indra/llinventory/llinventory.cpp                  |  27 +
 indra/llinventory/llinventory.h                    |  22 +-
 indra/llinventory/llinventorytype.cpp              |   4 +-
 indra/llmessage/lltransfersourceasset.cpp          |  42 +-
 indra/llmessage/message_prehash.cpp                |   1 +
 indra/llmessage/message_prehash.h                  |   1 +
 indra/newview/llagentwearables.cpp                 |  19 +-
 indra/newview/llagentwearables.h                   |   2 +-
 indra/newview/llfloaterproperties.cpp              |  16 +-
 indra/newview/llfolderview.h                       |   1 +
 indra/newview/llinventorybridge.cpp                | 679 ++++++++++++++-------
 indra/newview/llinventorybridge.h                  |  39 +-
 indra/newview/llinventorymodel.cpp                 |  60 +-
 indra/newview/llinventorymodel.h                   |  30 +-
 indra/newview/llviewerinventory.cpp                | 135 +++-
 indra/newview/llviewerinventory.h                  |  22 +-
 indra/newview/llvoavatarself.cpp                   |  33 +-
 indra/newview/llvoavatarself.h                     |   2 +-
 indra/newview/llwearablelist.cpp                   |  17 +-
 .../skins/default/xui/en/menu_inventory.xml        |   8 +
 scripts/messages/message_template.msg              |  18 +
 23 files changed, 968 insertions(+), 330 deletions(-)

diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index 6715b6722d..e4102a622d 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -46,7 +46,8 @@ struct AssetEntry : public LLDictionaryEntry
 			   const char *type_name, // 8 character limit!
 			   const char *human_name,
 			   const char *category_name, // used by llinventorymodel when creating new categories
-			   EDragAndDropType dad_type);
+			   EDragAndDropType dad_type,
+			   bool can_link);
 
 	// limited to 8 characters
 	const char *mTypeName;
@@ -55,6 +56,7 @@ struct AssetEntry : public LLDictionaryEntry
 	const char *mHumanName;
 	const char *mCategoryName;
 	EDragAndDropType mDadType;
+	bool mCanLink;
 };
 
 class LLAssetDictionary : public LLSingleton<LLAssetDictionary>,
@@ -66,44 +68,49 @@ public:
 
 LLAssetDictionary::LLAssetDictionary()
 {
-	addEntry(LLAssetType::AT_TEXTURE, 			new AssetEntry("TEXTURE",			"texture",	"texture",			"Textures", 		DAD_TEXTURE));
-	addEntry(LLAssetType::AT_SOUND, 			new AssetEntry("SOUND",				"sound",	"sound",			"Sounds", 			DAD_SOUND));
-	addEntry(LLAssetType::AT_CALLINGCARD, 		new AssetEntry("CALLINGCARD",		"callcard",	"calling card",		"Calling Cards", 	DAD_CALLINGCARD));
-	addEntry(LLAssetType::AT_LANDMARK, 			new AssetEntry("LANDMARK",			"landmark",	"landmark",			"Landmarks", 		DAD_LANDMARK));
-	addEntry(LLAssetType::AT_SCRIPT, 			new AssetEntry("SCRIPT",			"script",	"legacy script",	"Scripts", 			DAD_NONE));
-	addEntry(LLAssetType::AT_CLOTHING, 			new AssetEntry("CLOTHING",			"clothing",	"clothing",			"Clothing", 		DAD_CLOTHING));
-	addEntry(LLAssetType::AT_OBJECT, 			new AssetEntry("OBJECT",			"object",	"object",			"Objects", 			DAD_OBJECT));
-	addEntry(LLAssetType::AT_NOTECARD, 			new AssetEntry("NOTECARD",			"notecard",	"note card",		"Notecards", 		DAD_NOTECARD));
-	addEntry(LLAssetType::AT_CATEGORY, 			new AssetEntry("CATEGORY",			"category",	"folder",			"New Folder", 		DAD_CATEGORY));
-	addEntry(LLAssetType::AT_ROOT_CATEGORY, 	new AssetEntry("ROOT_CATEGORY",		"root",		"root",				"Inventory", 		DAD_ROOT_CATEGORY));
-	addEntry(LLAssetType::AT_LSL_TEXT, 			new AssetEntry("LSL_TEXT",			"lsltext",	"lsl2 script",		"Scripts", 			DAD_SCRIPT));
-	addEntry(LLAssetType::AT_LSL_BYTECODE, 		new AssetEntry("LSL_BYTECODE",		"lslbyte",	"lsl bytecode",		"Scripts", 			DAD_NONE));
-	addEntry(LLAssetType::AT_TEXTURE_TGA, 		new AssetEntry("TEXTURE_TGA",		"txtr_tga",	"tga texture",		"Uncompressed Images", DAD_NONE));
-	addEntry(LLAssetType::AT_BODYPART, 			new AssetEntry("BODYPART",			"bodypart",	"body part",		"Body Parts", 		DAD_BODYPART));
-	addEntry(LLAssetType::AT_TRASH, 			new AssetEntry("TRASH",				"trash",	"trash",			"Trash", 			DAD_NONE));
-	addEntry(LLAssetType::AT_SNAPSHOT_CATEGORY, new AssetEntry("SNAPSHOT_CATEGORY", "snapshot",	"snapshot",			"Photo Album", 		DAD_NONE));
-	addEntry(LLAssetType::AT_LOST_AND_FOUND, 	new AssetEntry("LOST_AND_FOUND", 	"lstndfnd",	"lost and found",	"Lost And Found", 	DAD_NONE));
-	addEntry(LLAssetType::AT_SOUND_WAV, 		new AssetEntry("SOUND_WAV",			"snd_wav",	"sound",			"Uncompressed Sounds", DAD_NONE));
-	addEntry(LLAssetType::AT_IMAGE_TGA, 		new AssetEntry("IMAGE_TGA",			"img_tga",	"targa image",		"Uncompressed Images", DAD_NONE));
-	addEntry(LLAssetType::AT_IMAGE_JPEG, 		new AssetEntry("IMAGE_JPEG",		"jpeg",		"jpeg image",		"Uncompressed Images", DAD_NONE));
-	addEntry(LLAssetType::AT_ANIMATION, 		new AssetEntry("ANIMATION",			"animatn",	"animation",		"Animations", 		DAD_ANIMATION));
-	addEntry(LLAssetType::AT_GESTURE, 			new AssetEntry("GESTURE",			"gesture",	"gesture",			"Gestures", 		DAD_GESTURE));
-	addEntry(LLAssetType::AT_SIMSTATE, 			new AssetEntry("SIMSTATE",			"simstate",	"simstate",			"New Folder", 		DAD_NONE));
-	addEntry(LLAssetType::AT_LINK, 				new AssetEntry("LINK",				"link",		"symbolic  link",	"New Folder", 		DAD_NONE));
-	addEntry(LLAssetType::AT_FAVORITE, 			new AssetEntry("FAVORITE",			"favorite",	"favorite",			"favorite", 		DAD_NONE));
-	addEntry(LLAssetType::AT_NONE, 				new AssetEntry("NONE",				"-1",		NULL,		  		"New Folder", 		DAD_NONE));
+	addEntry(LLAssetType::AT_TEXTURE, 			new AssetEntry("TEXTURE",			"texture",	"texture",			"Textures", 		DAD_TEXTURE,	FALSE));
+	addEntry(LLAssetType::AT_SOUND, 			new AssetEntry("SOUND",				"sound",	"sound",			"Sounds", 			DAD_SOUND,		FALSE));
+	addEntry(LLAssetType::AT_CALLINGCARD, 		new AssetEntry("CALLINGCARD",		"callcard",	"calling card",		"Calling Cards", 	DAD_CALLINGCARD, FALSE));
+	addEntry(LLAssetType::AT_LANDMARK, 			new AssetEntry("LANDMARK",			"landmark",	"landmark",			"Landmarks", 		DAD_LANDMARK,	FALSE));
+	addEntry(LLAssetType::AT_SCRIPT, 			new AssetEntry("SCRIPT",			"script",	"legacy script",	"Scripts", 			DAD_NONE,		FALSE));
+	addEntry(LLAssetType::AT_CLOTHING, 			new AssetEntry("CLOTHING",			"clothing",	"clothing",			"Clothing", 		DAD_CLOTHING,	TRUE));
+	addEntry(LLAssetType::AT_OBJECT, 			new AssetEntry("OBJECT",			"object",	"object",			"Objects", 			DAD_OBJECT,		TRUE));
+	addEntry(LLAssetType::AT_NOTECARD, 			new AssetEntry("NOTECARD",			"notecard",	"note card",		"Notecards", 		DAD_NOTECARD,	FALSE));
+	addEntry(LLAssetType::AT_CATEGORY, 			new AssetEntry("CATEGORY",			"category",	"folder",			"New Folder", 		DAD_CATEGORY,	TRUE));
+	addEntry(LLAssetType::AT_ROOT_CATEGORY, 	new AssetEntry("ROOT_CATEGORY",		"root",		"root",				"Inventory", 		DAD_ROOT_CATEGORY, TRUE));
+	addEntry(LLAssetType::AT_LSL_TEXT, 			new AssetEntry("LSL_TEXT",			"lsltext",	"lsl2 script",		"Scripts", 			DAD_SCRIPT,		FALSE));
+	addEntry(LLAssetType::AT_LSL_BYTECODE, 		new AssetEntry("LSL_BYTECODE",		"lslbyte",	"lsl bytecode",		"Scripts", 			DAD_NONE,		FALSE));
+	addEntry(LLAssetType::AT_TEXTURE_TGA, 		new AssetEntry("TEXTURE_TGA",		"txtr_tga",	"tga texture",		"Uncompressed Images", DAD_NONE,	FALSE));
+	addEntry(LLAssetType::AT_BODYPART, 			new AssetEntry("BODYPART",			"bodypart",	"body part",		"Body Parts", 		DAD_BODYPART,	TRUE));
+	addEntry(LLAssetType::AT_TRASH, 			new AssetEntry("TRASH",				"trash",	"trash",			"Trash", 			DAD_NONE,		FALSE));
+	addEntry(LLAssetType::AT_SNAPSHOT_CATEGORY, new AssetEntry("SNAPSHOT_CATEGORY", "snapshot",	"snapshot",			"Photo Album", 		DAD_NONE,		FALSE));
+	addEntry(LLAssetType::AT_LOST_AND_FOUND, 	new AssetEntry("LOST_AND_FOUND", 	"lstndfnd",	"lost and found",	"Lost And Found", 	DAD_NONE,		FALSE));
+	addEntry(LLAssetType::AT_SOUND_WAV, 		new AssetEntry("SOUND_WAV",			"snd_wav",	"sound",			"Uncompressed SoundS", DAD_NONE,	FALSE));
+	addEntry(LLAssetType::AT_IMAGE_TGA, 		new AssetEntry("IMAGE_TGA",			"img_tga",	"targa image",		"Uncompressed Images", DAD_NONE,	FALSE));
+	addEntry(LLAssetType::AT_IMAGE_JPEG, 		new AssetEntry("IMAGE_JPEG",		"jpeg",		"jpeg image",		"Uncompressed Images", DAD_NONE,	FALSE));
+	addEntry(LLAssetType::AT_ANIMATION, 		new AssetEntry("ANIMATION",			"animatn",	"animation",		"Animations", 		DAD_ANIMATION,	FALSE));
+	addEntry(LLAssetType::AT_GESTURE, 			new AssetEntry("GESTURE",			"gesture",	"gesture",			"Gestures", 		DAD_GESTURE,	FALSE));
+	addEntry(LLAssetType::AT_SIMSTATE, 			new AssetEntry("SIMSTATE",			"simstate",	"simstate",			"New Folder", 		DAD_NONE,		FALSE));
+	addEntry(LLAssetType::AT_FAVORITE, 			new AssetEntry("FAVORITE",			"favorite",	"favorite",			"favorite", 		DAD_NONE,		FALSE));
+
+	addEntry(LLAssetType::AT_LINK, 				new AssetEntry("LINK",				"link",		"symbolic link",	"New Folder", 		DAD_NONE,		FALSE));
+	addEntry(LLAssetType::AT_LINK_FOLDER, 		new AssetEntry("FOLDER_LINK",		"link_f", "symbolic folder link", "New Folder", DAD_NONE,		FALSE));
+
+	addEntry(LLAssetType::AT_NONE, 				new AssetEntry("NONE",				"-1",		NULL,		  		"New Folder", 		DAD_NONE,		FALSE));
 };
 
 AssetEntry::AssetEntry(const char *desc_name,
 					   const char *type_name,
 					   const char *human_name,
 					   const char *category_name,
-					   EDragAndDropType dad_type) :
+					   EDragAndDropType dad_type,
+					   bool can_link) :
 	LLDictionaryEntry(desc_name),
 	mTypeName(type_name),
 	mHumanName(human_name),
 	mCategoryName(category_name),
-	mDadType(dad_type)
+	mDadType(dad_type),
+	mCanLink(can_link)
 {
 	llassert(strlen(mTypeName) <= 8);
 }
@@ -231,6 +238,29 @@ EDragAndDropType LLAssetType::lookupDragAndDropType(EType asset_type)
 		return DAD_NONE;
 }
 
+// static
+bool LLAssetType::lookupCanLink(EType asset_type)
+{
+	const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+	const AssetEntry *entry = dict->lookup(asset_type);
+	if (entry)
+	{
+		return entry->mCanLink;
+	}
+	return false;
+}
+
+// static
+// Not adding this to dictionary since we probably will only have these two types
+bool LLAssetType::lookupIsLinkType(EType asset_type)
+{
+	if (asset_type == AT_LINK || asset_type == AT_LINK_FOLDER)
+	{
+		return true;
+	}
+	return false;
+}
+
 // static. Generate a good default description
 void LLAssetType::generateDescriptionFor(LLAssetType::EType asset_type,
 										 std::string& description)
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index 2f54031688..353bd57bb9 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -125,23 +125,25 @@ public:
 
 		AT_SIMSTATE = 22,
 			// Simstate file.
-			
-		AT_LINK = 23,
-			// Inventory symbolic link
-			
-		AT_FAVORITE = 24,
+	
+		AT_FAVORITE = 23,
 			// favorite items
 
-		// +*********************************************+
-		// |  TO ADD AN ELEMENT TO THIS ENUM:            |
-			// +************************************************+
-		// | 1. INSERT BEFORE AT_COUNT                   |
-		// | 2. INCREMENT AT_COUNT BY 1                  |
-		// | 3. ADD TO LLAssetType::mAssetTypeNames      |
-		// | 4. ADD TO LLAssetType::mAssetTypeHumanNames |
-		// +*********************************************+
+		AT_LINK = 24,
+			// Inventory symbolic link
+
+		AT_LINK_FOLDER = 25,
+			// Inventory folder link
 
-		AT_COUNT = 25,
+		AT_COUNT = 26,
+
+			// +************************************************+
+			// |  TO ADD AN ELEMENT TO THIS ENUM:               |
+			// +************************************************+
+			// | 1. INSERT BEFORE AT_COUNT                      |
+			// | 2. INCREMENT AT_COUNT BY 1                     |
+			// | 3. ADD TO LLAssetDictionary in llassettype.cpp |
+			// +************************************************+
 
 		AT_NONE = -1
 	};
@@ -166,6 +168,8 @@ public:
 	static EType 				getType(const std::string& desc_name);
 	static const std::string&	getDesc(EType asset_type);
 	static EDragAndDropType   	lookupDragAndDropType(EType asset_type);
+	static bool 				lookupCanLink(EType asset_type);
+	static bool 				lookupIsLinkType(EType asset_type);
 
 	/* TODO: Change return types from "const char *" to "const std::string &".
 	This is fairly straightforward, but requires changing some calls to use .c_str().
diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp
index adc80b2ed3..597e19e7ea 100644
--- a/indra/llinventory/llinventory.cpp
+++ b/indra/llinventory/llinventory.cpp
@@ -126,6 +126,20 @@ const std::string& LLInventoryObject::getName() const
 	return mName;
 }
 
+// To bypass linked items, since llviewerinventory's getType
+// will return the linked-to item's type instead of this object's type.
+LLAssetType::EType LLInventoryObject::getActualType() const
+{
+	return mType;
+}
+
+// See LLInventoryItem override.
+// virtual
+const LLUUID& LLInventoryObject::getLinkedUUID() const
+{
+	return mUUID;
+}
+
 LLAssetType::EType LLInventoryObject::getType() const
 {
 	return mType;
@@ -333,6 +347,19 @@ void LLInventoryItem::copyItem(const LLInventoryItem* other)
 	mCreationDate = other->mCreationDate;
 }
 
+// If this is a linked item, then the UUID of the base object is
+// this item's assetID.
+// virtual
+const LLUUID& LLInventoryItem::getLinkedUUID() const
+{
+	if (LLAssetType::lookupIsLinkType(getActualType()))
+	{
+		return mAssetUUID;
+	}
+
+	return LLInventoryObject::getLinkedUUID();
+}
+
 const LLPermissions& LLInventoryItem::getPermissions() const
 {
 	return mPermissions;
diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h
index 3f79cedc23..ce64317f48 100644
--- a/indra/llinventory/llinventory.h
+++ b/indra/llinventory/llinventory.h
@@ -92,9 +92,12 @@ public:
 	// accessors
 	virtual const LLUUID& getUUID() const;
 	const LLUUID& getParentUUID() const;
+	virtual const LLUUID& getLinkedUUID() const; // get the inventoryID that this item points to, else this item's inventoryID
+
 	virtual const std::string& getName() const;
 	virtual LLAssetType::EType getType() const;
-	LLAssetType::EType getActualType() const { return mType; }
+	LLAssetType::EType getActualType() const; // bypasses indirection for linked items
+
 	// mutators - will not call updateServer();
 	void setUUID(const LLUUID& new_uuid);
 	void rename(const std::string& new_name);
@@ -240,15 +243,16 @@ public:
 	void generateUUID() { mUUID.generate(); }
 	
 	// accessors
-	const LLPermissions& getPermissions() const;
-	const LLUUID& getCreatorUUID() const;
+	virtual const LLUUID& getLinkedUUID() const;
+	virtual const LLPermissions& getPermissions() const;
+	virtual const LLUUID& getCreatorUUID() const;
 	virtual const LLUUID& getAssetUUID() const;
-	const std::string& getDescription() const;
-	const LLSaleInfo& getSaleInfo() const;
-	LLInventoryType::EType getInventoryType() const;
-	U32 getFlags() const;
-	time_t getCreationDate() const;
-	U32 getCRC32() const; // really more of a checksum.
+	virtual const std::string& getDescription() const;
+	virtual const LLSaleInfo& getSaleInfo() const;
+	virtual LLInventoryType::EType getInventoryType() const;
+	virtual U32 getFlags() const;
+	virtual time_t getCreationDate() const;
+	virtual U32 getCRC32() const; // really more of a checksum.
 	
 	// mutators - will not call updateServer(), and will never fail
 	// (though it may correct to sane values)
diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp
index ff9c698943..2dc229226f 100644
--- a/indra/llinventory/llinventorytype.cpp
+++ b/indra/llinventory/llinventorytype.cpp
@@ -113,8 +113,10 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] =
 	LLInventoryType::IT_NONE,			// AT_IMAGE_JPEG
 	LLInventoryType::IT_ANIMATION,		// AT_ANIMATION
 	LLInventoryType::IT_GESTURE,		// AT_GESTURE
-	LLInventoryType::IT_NONE,			// AT_LINK
+	LLInventoryType::IT_NONE,			// AT_SIMSTATE
 	LLInventoryType::IT_FAVORITE,		// AT_FAVORITE
+	LLInventoryType::IT_NONE,			// AT_LINK
+	LLInventoryType::IT_NONE,			// AT_LINK_FOLDER
 };
 
 InventoryEntry::InventoryEntry(const std::string &name,
diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp
index 059c1bdfa1..c715e16e34 100644
--- a/indra/llmessage/lltransfersourceasset.cpp
+++ b/indra/llmessage/lltransfersourceasset.cpp
@@ -264,17 +264,17 @@ bool is_asset_fetch_by_id_allowed(LLAssetType::EType type)
 	bool rv = false;
 	switch(type)
 	{
-	case LLAssetType::AT_SOUND:
-	case LLAssetType::AT_LANDMARK:
-	case LLAssetType::AT_CLOTHING:
-	case LLAssetType::AT_BODYPART:
-	case LLAssetType::AT_ANIMATION:
-	case LLAssetType::AT_GESTURE:
-	case LLAssetType::AT_FAVORITE:
-		rv = true;
-		break;
-	default:
-		break;
+		case LLAssetType::AT_SOUND:
+		case LLAssetType::AT_LANDMARK:
+		case LLAssetType::AT_CLOTHING:
+		case LLAssetType::AT_BODYPART:
+		case LLAssetType::AT_ANIMATION:
+		case LLAssetType::AT_GESTURE:
+		case LLAssetType::AT_FAVORITE:
+			rv = true;
+			break;
+		default:
+			break;
 	}
 	return rv;
 }
@@ -285,15 +285,17 @@ bool is_asset_id_knowable(LLAssetType::EType type)
 	bool rv = false;
 	switch(type)
 	{
-	case LLAssetType::AT_TEXTURE:
-	case LLAssetType::AT_SOUND:
-	case LLAssetType::AT_LANDMARK:
-	case LLAssetType::AT_CLOTHING:
-	case LLAssetType::AT_NOTECARD:
-	case LLAssetType::AT_BODYPART:
-	case LLAssetType::AT_ANIMATION:
-	case LLAssetType::AT_GESTURE:
-	case LLAssetType::AT_FAVORITE:
+		case LLAssetType::AT_TEXTURE:
+		case LLAssetType::AT_SOUND:
+		case LLAssetType::AT_LANDMARK:
+		case LLAssetType::AT_CLOTHING:
+		case LLAssetType::AT_NOTECARD:
+		case LLAssetType::AT_BODYPART:
+		case LLAssetType::AT_ANIMATION:
+		case LLAssetType::AT_GESTURE:
+		case LLAssetType::AT_FAVORITE:
+		case LLAssetType::AT_LINK:
+		case LLAssetType::AT_LINK_FOLDER:
 		rv = true;
 		break;
 	default:
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index 4e657067cd..9e3986f257 100644
--- a/indra/llmessage/message_prehash.cpp
+++ b/indra/llmessage/message_prehash.cpp
@@ -174,6 +174,7 @@ char* _PREHASH_UpdateInventoryItem = LLMessageStringTable::getInstance()->getStr
 char* _PREHASH_UpdateCreateInventoryItem = LLMessageStringTable::getInstance()->getString("UpdateCreateInventoryItem");
 char* _PREHASH_MoveInventoryItem = LLMessageStringTable::getInstance()->getString("MoveInventoryItem");
 char* _PREHASH_CopyInventoryItem = LLMessageStringTable::getInstance()->getString("CopyInventoryItem");
+char* _PREHASH_LinkInventoryItem = LLMessageStringTable::getInstance()->getString("LinkInventoryItem");
 char* _PREHASH_RemoveInventoryItem = LLMessageStringTable::getInstance()->getString("RemoveInventoryItem");
 char* _PREHASH_CreateInventoryItem = LLMessageStringTable::getInstance()->getString("CreateInventoryItem");
 char* _PREHASH_PathTwistBegin = LLMessageStringTable::getInstance()->getString("PathTwistBegin");
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
index f8ef610408..e73ec3e5e1 100644
--- a/indra/llmessage/message_prehash.h
+++ b/indra/llmessage/message_prehash.h
@@ -174,6 +174,7 @@ extern char * _PREHASH_UpdateInventoryItem;
 extern char * _PREHASH_UpdateCreateInventoryItem;
 extern char * _PREHASH_MoveInventoryItem;
 extern char * _PREHASH_CopyInventoryItem;
+extern char * _PREHASH_LinkInventoryItem;
 extern char * _PREHASH_RemoveInventoryItem;
 extern char * _PREHASH_CreateInventoryItem;
 extern char * _PREHASH_PathTwistBegin;
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 1c756b1441..1da54ad08c 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -626,9 +626,24 @@ const LLUUID& LLAgentWearables::getWearableItem(EWearableType type, U32 index) c
 }
 
 
-BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const
+// Warning: include_linked_items = TRUE makes this operation expensive.
+BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id, BOOL include_linked_items) const
 {
-	return (getWearableFromWearableItem(item_id) != NULL);
+	if (getWearableFromWearableItem(item_id) != NULL) return TRUE;
+	if (include_linked_items)
+	{
+		LLInventoryModel::item_array_t item_array;
+		gInventory.collectLinkedItems(item_id, item_array);
+		for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
+			 iter != item_array.end();
+			 iter++)
+		{
+			LLViewerInventoryItem *linked_item = (*iter);
+			const LLUUID &item_id = linked_item->getUUID();
+			if (getWearableFromWearableItem(item_id) != NULL) return TRUE;
+		}
+	}
+	return FALSE;
 }
 
 // MULTI-WEARABLE: update for multiple
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 98f49203d3..31d6e30069 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -63,7 +63,7 @@ protected:
 	// Queries
 	//--------------------------------------------------------------------
 public:
-	BOOL			isWearingItem(const LLUUID& item_id) const;
+	BOOL			isWearingItem(const LLUUID& item_id, const BOOL include_linked_items = FALSE) const;
 	BOOL			isWearableModifiable(EWearableType type, U32 index /*= 0*/) const;
 	BOOL			isWearableCopyable(EWearableType type, U32 index /*= 0*/) const;
 	BOOL			areWearablesLoaded() const { return mWearablesLoaded; } 
diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp
index 5a8afc2277..dc72b66949 100644
--- a/indra/newview/llfloaterproperties.cpp
+++ b/indra/newview/llfloaterproperties.cpp
@@ -305,10 +305,11 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item)
 	BOOL is_complete = i->isComplete();
 
 	const LLPermissions& perm = item->getPermissions();
-	BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm, 
-												GP_OBJECT_MANIPULATE);
-	BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm, 
-												GP_OBJECT_SET_SALE);
+	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);
+	const BOOL is_link = LLAssetType::lookupIsLinkType(i->getActualType());
 
 	// You need permission to modify the object to modify an inventory
 	// item in it.
@@ -491,7 +492,12 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item)
 	/////////////
 
 	// Check for ability to change values.
-	if (is_obj_modify && can_agent_manipulate)
+	if (is_link)
+	{
+		childSetEnabled("CheckShareWithGroup",FALSE);
+		childSetEnabled("CheckEveryoneCopy",FALSE);
+	}
+	else if (is_obj_modify && can_agent_manipulate)
 	{
 		childSetEnabled("CheckShareWithGroup",TRUE);
 		childSetEnabled("CheckEveryoneCopy",(owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER));
diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h
index 9d91f0d64e..3386a7fb0e 100644
--- a/indra/newview/llfolderview.h
+++ b/indra/newview/llfolderview.h
@@ -103,6 +103,7 @@ public:
 	virtual void cutToClipboard() = 0;
 	virtual BOOL isClipboardPasteable() const = 0;
 	virtual void pasteFromClipboard() = 0;
+	virtual void pasteLinkFromClipboard() = 0;
 	virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0;
 	virtual BOOL isUpToDate() const = 0;
 	virtual BOOL hasChildren() const = 0;
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 540cefbc46..af653238d3 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -202,6 +202,38 @@ BOOL LLInvFVBridge::isItemRemovable()
 	return FALSE;
 }
 
+// Sends an update to all link items that point to the base item.
+void LLInvFVBridge::renameLinkedItems(const LLUUID &item_id, const std::string& new_name)
+{
+	LLInventoryModel* model = getInventoryModel();
+	if(!model) return;
+
+	LLInventoryItem* itemp = model->getItem(mUUID);
+	if (!itemp) return;
+
+	if (LLAssetType::lookupIsLinkType(itemp->getActualType()))
+	{
+		return;
+	}
+	
+	LLInventoryModel::item_array_t item_array;
+	model->collectLinkedItems(item_id, item_array);
+	for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
+		 iter != item_array.end();
+		 iter++)
+	{
+		LLViewerInventoryItem *linked_item = (*iter);
+		if (linked_item->getUUID() == item_id) continue;
+		
+		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(linked_item);
+		new_item->rename(new_name);
+		new_item->updateServer(FALSE);
+		model->updateItem(new_item);
+		// model->addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
+	}
+	model->notifyObservers();
+}
+
 // Can be moved to another folder
 BOOL LLInvFVBridge::isItemMovable()
 {
@@ -370,15 +402,71 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*
 
 BOOL LLInvFVBridge::isClipboardPasteable() const
 {
+	if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+	{
+		return FALSE;
+	}
 	LLInventoryModel* model = getInventoryModel();
-	if(!model) return FALSE;
-	BOOL is_agent_inventory = model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID());
+	if (!model)
+	{
+		return FALSE;
+	}
+
+	const LLUUID &agent_id = gAgent.getID();
 
-	if(LLInventoryClipboard::instance().hasContents() && is_agent_inventory)
+	LLDynamicArray<LLUUID> objects;
+	LLInventoryClipboard::instance().retrieve(objects);
+	S32 count = objects.count();
+	for(S32 i = 0; i < count; i++)
 	{
-		return TRUE;
+		const LLUUID &item_id = objects.get(i);
+
+		// Can't paste folders
+		const LLInventoryCategory *cat = model->getCategory(item_id);
+		if (cat)
+		{
+			return FALSE;
+		}
+
+		const LLInventoryItem *item = model->getItem(item_id);
+		if (item)
+		{
+			if (!item->getPermissions().allowCopyBy(agent_id))
+			{
+				return FALSE;
+			}
+		}
 	}
-	return FALSE;
+	return TRUE;
+}
+
+BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
+{
+	if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+	{
+		return FALSE;
+	}
+	LLInventoryModel* model = getInventoryModel();
+	if (!model)
+	{
+		return FALSE;
+	}
+
+	LLDynamicArray<LLUUID> objects;
+	LLInventoryClipboard::instance().retrieve(objects);
+	S32 count = objects.count();
+	for(S32 i = 0; i < count; i++)
+	{
+		LLInventoryItem *item = model->getItem(objects.get(i));
+		if (item)
+		{
+			if (!LLAssetType::lookupCanLink(item->getActualType()))
+			{
+				return FALSE;
+			}
+		}
+	}
+	return TRUE;
 }
 
 void hideContextEntries(LLMenuGL& menu, 
@@ -461,6 +549,11 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
 		disabled_items.push_back(std::string("Paste"));
 	}
 
+	items.push_back(std::string("Paste As Link"));
+	if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0)
+	{
+		disabled_items.push_back(std::string("Paste As Link"));
+	}
 	items.push_back(std::string("Paste Separator"));
 
 	items.push_back(std::string("Delete"));
@@ -544,10 +637,25 @@ BOOL LLInvFVBridge::isInTrash() const
 {
 	LLInventoryModel* model = getInventoryModel();
 	if(!model) return FALSE;
-	LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+	const LLUUID& trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
 	return model->isObjectDescendentOf(mUUID, trash_id);
 }
 
+BOOL LLInvFVBridge::isLinkedObjectInTrash() const
+{
+	if (isInTrash()) return TRUE;
+
+	LLInventoryModel* model = getInventoryModel();
+	if(!model) return FALSE;
+	LLInventoryObject *obj = model->getObject(mUUID);
+	if (obj && LLAssetType::lookupIsLinkType(obj->getActualType()))
+	{
+		const LLUUID& trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+		return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id);
+	}
+	return FALSE;
+}
+
 BOOL LLInvFVBridge::isAgentInventory() const
 {
 	LLInventoryModel* model = getInventoryModel();
@@ -627,108 +735,106 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
 	LLInvFVBridge* new_listener = NULL;
 	switch(asset_type)
 	{
-	case LLAssetType::AT_TEXTURE:
-		if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLTextureBridge(inventory, uuid, inv_type);
-		break;
+		case LLAssetType::AT_TEXTURE:
+			if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLTextureBridge(inventory, uuid, inv_type);
+			break;
 
-	case LLAssetType::AT_SOUND:
-		if(!(inv_type == LLInventoryType::IT_SOUND))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLSoundBridge(inventory, uuid);
-		break;
+		case LLAssetType::AT_SOUND:
+			if(!(inv_type == LLInventoryType::IT_SOUND))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLSoundBridge(inventory, uuid);
+			break;
 
-	case LLAssetType::AT_LANDMARK:
-		if(!(inv_type == LLInventoryType::IT_LANDMARK))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLLandmarkBridge(inventory, uuid, flags);
-		break;
+		case LLAssetType::AT_LANDMARK:
+			if(!(inv_type == LLInventoryType::IT_LANDMARK))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLLandmarkBridge(inventory, uuid, flags);
+			break;
 		
-	case LLAssetType::AT_CALLINGCARD:
-		if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLCallingCardBridge(inventory, uuid);
-		break;
-
-	case LLAssetType::AT_SCRIPT:
-		if(!(inv_type == LLInventoryType::IT_LSL))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLScriptBridge(inventory, uuid);
-		break;
-
-	case LLAssetType::AT_OBJECT:
-		if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags);
-		break;
+		case LLAssetType::AT_CALLINGCARD:
+			if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLCallingCardBridge(inventory, uuid);
+			break;
 
-	case LLAssetType::AT_NOTECARD:
-		if(!(inv_type == LLInventoryType::IT_NOTECARD))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLNotecardBridge(inventory, uuid);
-		break;
+		case LLAssetType::AT_SCRIPT:
+			if(!(inv_type == LLInventoryType::IT_LSL))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLScriptBridge(inventory, uuid);
+			break;
 
-	case LLAssetType::AT_ANIMATION:
-		if(!(inv_type == LLInventoryType::IT_ANIMATION))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLAnimationBridge(inventory, uuid);
-		break;
+		case LLAssetType::AT_OBJECT:
+			if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags);
+			break;
 
-	case LLAssetType::AT_GESTURE:
-		if(!(inv_type == LLInventoryType::IT_GESTURE))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLGestureBridge(inventory, uuid);
-		break;
+		case LLAssetType::AT_NOTECARD:
+			if(!(inv_type == LLInventoryType::IT_NOTECARD))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLNotecardBridge(inventory, uuid);
+			break;
 
-	case LLAssetType::AT_LSL_TEXT:
-		if(!(inv_type == LLInventoryType::IT_LSL))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLLSLTextBridge(inventory, uuid);
-		break;
+		case LLAssetType::AT_ANIMATION:
+			if(!(inv_type == LLInventoryType::IT_ANIMATION))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLAnimationBridge(inventory, uuid);
+			break;
 
-	case LLAssetType::AT_CLOTHING:
-	case LLAssetType::AT_BODYPART:
-		if(!(inv_type == LLInventoryType::IT_WEARABLE))
-		{
-			llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
-		}
-		new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags);
-		break;
+		case LLAssetType::AT_GESTURE:
+			if(!(inv_type == LLInventoryType::IT_GESTURE))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLGestureBridge(inventory, uuid);
+			break;
 
-	case LLAssetType::AT_CATEGORY:
-	case LLAssetType::AT_ROOT_CATEGORY:
-		new_listener = new LLFolderBridge(inventory, uuid);
-		break;
+		case LLAssetType::AT_LSL_TEXT:
+			if(!(inv_type == LLInventoryType::IT_LSL))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLLSLTextBridge(inventory, uuid);
+			break;
 
-	case LLAssetType::AT_FAVORITE:
-		new_listener = new LLFolderBridge(inventory, uuid);
-		break;
-		
-	default:
-		llinfos << "Unhandled asset type (llassetstorage.h): "
-				<< (S32)asset_type << llendl;
-		break;
+		case LLAssetType::AT_CLOTHING:
+		case LLAssetType::AT_BODYPART:
+			if(!(inv_type == LLInventoryType::IT_WEARABLE))
+			{
+				llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+			}
+			new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags);
+			break;
+		case LLAssetType::AT_CATEGORY:
+		case LLAssetType::AT_ROOT_CATEGORY:
+			new_listener = new LLFolderBridge(inventory, uuid);
+			break;
+		case LLAssetType::AT_LINK:
+			// Only should happen for broken links.
+			new_listener = new LLLinkItemBridge(inventory, uuid);
+			break;
+		default:
+			llinfos << "Unhandled asset type (llassetstorage.h): "
+					<< (S32)asset_type << llendl;
+			break;
 	}
 
 	if (new_listener)
@@ -739,6 +845,22 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
 	return new_listener;
 }
 
+void LLInvFVBridge::purgeItem(LLInventoryModel *model, const LLUUID &uuid)
+{
+	LLInventoryCategory* cat = model->getCategory(uuid);
+	if (cat)
+	{
+		model->purgeDescendentsOf(uuid);
+		model->notifyObservers();
+	}
+	LLInventoryObject* obj = model->getObject(uuid);
+	if (obj)
+	{
+		model->purgeObject(uuid);
+		model->notifyObservers();
+	}
+}
+
 // +=================================================+
 // |        LLItemBridge                             |
 // +=================================================+
@@ -748,32 +870,27 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model,
 	if ("open" == action)
 	{
 		openItem();
+		return;
 	}
 	else if ("properties" == action)
 	{
 		showProperties();
+		return;
 	}
 	else if ("purge" == action)
 	{
-		LLInventoryCategory* cat = model->getCategory(mUUID);
-		if(cat)
-		{
-			model->purgeDescendentsOf(mUUID);
-		}
-		LLInventoryObject* obj = model->getObject(mUUID);
-		if(!obj) return;
-		obj->removeFromServer();
-		LLPreview::hide(mUUID);
-		model->deleteObject(mUUID);
-		model->notifyObservers();
+		purgeItem(model, mUUID);
+		return;
 	}
 	else if ("restoreToWorld" == action)
 	{
 		restoreToWorld();
+		return;
 	}
 	else if ("restore" == action)
 	{
 		restoreItem();
+		return;
 	}
 	else if ("copy_uuid" == action)
 	{
@@ -804,6 +921,18 @@ void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model,
 		folder_view_itemp->getListener()->pasteFromClipboard();
 		return;
 	}
+	else if ("paste_link" == action)
+	{
+		// Single item only
+		LLInventoryItem* itemp = model->getItem(mUUID);
+		if (!itemp) return;
+
+		LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
+		if (!folder_view_itemp) return;
+
+		folder_view_itemp->getListener()->pasteLinkFromClipboard();
+		return;
+	}
 }
 
 void LLItemBridge::selectItem()
@@ -913,6 +1042,24 @@ void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name)
 	}
 }
 
+LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const
+{ 
+	U8 font = LLFontGL::NORMAL;
+	
+	if( gAgentWearables.isWearingItem( mUUID ) )
+	{
+		// llinfos << "BOLD" << llendl;
+		font |= LLFontGL::BOLD;
+	}
+
+	const LLViewerInventoryItem* item = getItem();
+	if (LLAssetType::lookupIsLinkType(item->getActualType()))
+	{
+		font |= LLFontGL::ITALIC;
+	}
+	return (LLFontGL::StyleFlags)font;
+}
+
 std::string LLItemBridge::getLabelSuffix() const
 {
 	// assume that this won't be called before string table is loaded
@@ -932,8 +1079,12 @@ std::string LLItemBridge::getLabelSuffix() const
 			BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
 			BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
 																gAgent.getID());
+			BOOL link = (item->getActualType() == LLAssetType::AT_LINK);
 
 			const char* EMPTY = "";
+			const char* LINK = " (link)"; // *TODO: Seraph translate
+			if (link) return LINK;
+
 			const char* scopy;
 			if(copy) scopy = EMPTY;
 			else scopy = NO_COPY;
@@ -986,6 +1137,8 @@ BOOL LLItemBridge::renameItem(const std::string& new_name)
 		buildDisplayName(new_item, mDisplayName);
 		new_item->updateServer(FALSE);
 		model->updateItem(new_item);
+		renameLinkedItems(item->getUUID(),new_name);
+
 		model->notifyObservers();
 	}
 	// return FALSE because we either notified observers (& therefore
@@ -1034,13 +1187,19 @@ BOOL LLItemBridge::isItemCopyable() const
 			return FALSE;
 		}
 
-		if( avatarp->isWearingAttachment( mUUID ) )
+		if( avatarp->isWearingAttachment( mUUID, TRUE ) )
 		{
 			return FALSE;
 		}
-			
-
-		return (item->getPermissions().allowCopyBy(gAgent.getID()));
+		
+		// All items can be copied, not all can be pasted.
+		// The only time an item can't be copied is if it's a link 
+		// return (item->getPermissions().allowCopyBy(gAgent.getID()));
+		if (item->getActualType() == LLAssetType::AT_LINK)
+		{
+			return FALSE;
+		}
+		return TRUE;
 	}
 	return FALSE;
 }
@@ -1152,7 +1311,7 @@ BOOL LLFolderBridge::isItemRemovable()
 		if( (item->getType() == LLAssetType::AT_CLOTHING) ||
 			(item->getType() == LLAssetType::AT_BODYPART) )
 		{
-			if( gAgentWearables.isWearingItem( item->getUUID() ) )
+			if( gAgentWearables.isWearingItem( item->getUUID(), TRUE ) )
 			{
 				return FALSE;
 			}
@@ -1160,7 +1319,7 @@ BOOL LLFolderBridge::isItemRemovable()
 		else
 		if( item->getType() == LLAssetType::AT_OBJECT )
 		{
-			if( avatar->isWearingAttachment( item->getUUID() ) )
+			if( avatar->isWearingAttachment( item->getUUID(), TRUE ) )
 			{
 				return FALSE;
 			}
@@ -1183,6 +1342,21 @@ BOOL LLFolderBridge::isUpToDate() const
 	return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
 }
 
+BOOL LLFolderBridge::isItemCopyable() const
+{
+	return TRUE;
+}
+
+BOOL LLFolderBridge::copyToClipboard() const
+{
+	if(isItemCopyable())
+	{
+		LLInventoryClipboard::instance().add(mUUID);
+		return TRUE;
+	}
+	return FALSE;
+}
+
 BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
 											BOOL drop)
 {
@@ -1620,22 +1794,37 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model
 	if ("open" == action)
 	{
 		openItem();
+		return;
 	}
 	else if ("paste" == action)
 	{
 		pasteFromClipboard();
+		return;
+	}
+	else if ("paste_link" == action)
+	{
+		pasteLinkFromClipboard();
+		return;
 	}
 	else if ("properties" == action)
 	{
 		showProperties();
+		return;
 	}
 	else if ("replaceoutfit" == action)
 	{
 		modifyOutfit(FALSE);
+		return;
 	}
 	else if ("addtooutfit" == action)
 	{
 		modifyOutfit(TRUE);
+		return;
+	}
+	else if ("copy" == action)
+	{
+		copyToClipboard();
+		return;
 	}
 	else if ("removefromoutfit" == action)
 	{
@@ -1645,25 +1834,17 @@ void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model
 		if(!cat) return;
 		
 		remove_inventory_category_from_avatar ( cat );
+		return;
 	}	
 	else if ("purge" == action)
 	{		
-		LLViewerInventoryCategory* cat;
-		cat = (LLViewerInventoryCategory*)getCategory();
-
-		if(cat)
-		{
-			model->purgeDescendentsOf(mUUID);
-		}
-		LLInventoryObject* obj = model->getObject(mUUID);
-		if(!obj) return;
-		obj->removeFromServer();
-		model->deleteObject(mUUID);
-		model->notifyObservers();
+		purgeItem(model, mUUID);
+		return;
 	}
 	else if ("restore" == action)
 	{
 		restoreItem();
+		return;
 	}
 }
 
@@ -1723,59 +1904,59 @@ LLUIImagePtr LLFolderBridge::getIcon() const
 	}
 	switch(preferred_type)
 	{
-	case LLAssetType::AT_TEXTURE:
-		control = "inv_folder_texture.tga";
-		break;
-	case LLAssetType::AT_SOUND:
-		control = "inv_folder_sound.tga";
-		break;
-	case LLAssetType::AT_CALLINGCARD:
-		control = "inv_folder_callingcard.tga";
-		break;
-	case LLAssetType::AT_LANDMARK:
-		control = "inv_folder_landmark.tga";
-		break;
-	case LLAssetType::AT_SCRIPT:
-	case LLAssetType::AT_LSL_TEXT:
-		control = "inv_folder_script.tga";
-		break;
-	case LLAssetType::AT_OBJECT:
-		control = "inv_folder_object.tga";
-		break;
-	case LLAssetType::AT_NOTECARD:
-		control = "inv_folder_notecard.tga";
-		break;
-	case LLAssetType::AT_CATEGORY:
-		control = "inv_folder_plain_closed.tga";
-		break;
-	case LLAssetType::AT_CLOTHING:
-		control = "inv_folder_clothing.tga";
-		break;
-	case LLAssetType::AT_BODYPART:
-		control = "inv_folder_bodypart.tga";
-		break;
-	case LLAssetType::AT_TRASH:
-		control = "inv_folder_trash.tga";
-		break;
-	case LLAssetType::AT_SNAPSHOT_CATEGORY:
-		control = "inv_folder_snapshot.tga";
-		break;
-	case LLAssetType::AT_LOST_AND_FOUND:
-		control = "inv_folder_lostandfound.tga";
-		break;
-	case LLAssetType::AT_ANIMATION:
-		control = "inv_folder_animation.tga";
-		break;
-	case LLAssetType::AT_GESTURE:
-		control = "inv_folder_gesture.tga";
-		break;
-	case LLAssetType::AT_FAVORITE:
-		//TODO - need icon
-		control = "inv_folder_plain_closed.tga";
-		break;
-	default:
-		control = "inv_folder_plain_closed.tga";
-		break;
+		case LLAssetType::AT_TEXTURE:
+			control = "inv_folder_texture.tga";
+			break;
+		case LLAssetType::AT_SOUND:
+			control = "inv_folder_sound.tga";
+			break;
+		case LLAssetType::AT_CALLINGCARD:
+			control = "inv_folder_callingcard.tga";
+			break;
+		case LLAssetType::AT_LANDMARK:
+			control = "inv_folder_landmark.tga";
+			break;
+		case LLAssetType::AT_SCRIPT:
+		case LLAssetType::AT_LSL_TEXT:
+			control = "inv_folder_script.tga";
+			break;
+		case LLAssetType::AT_OBJECT:
+			control = "inv_folder_object.tga";
+			break;
+		case LLAssetType::AT_NOTECARD:
+			control = "inv_folder_notecard.tga";
+			break;
+		case LLAssetType::AT_CATEGORY:
+			control = "inv_folder_plain_closed.tga";
+			break;
+		case LLAssetType::AT_CLOTHING:
+			control = "inv_folder_clothing.tga";
+			break;
+		case LLAssetType::AT_BODYPART:
+			control = "inv_folder_bodypart.tga";
+			break;
+		case LLAssetType::AT_TRASH:
+			control = "inv_folder_trash.tga";
+			break;
+		case LLAssetType::AT_SNAPSHOT_CATEGORY:
+			control = "inv_folder_snapshot.tga";
+			break;
+		case LLAssetType::AT_LOST_AND_FOUND:
+			control = "inv_folder_lostandfound.tga";
+			break;
+		case LLAssetType::AT_ANIMATION:
+			control = "inv_folder_animation.tga";
+			break;
+		case LLAssetType::AT_GESTURE:
+			control = "inv_folder_gesture.tga";
+			break;
+		case LLAssetType::AT_FAVORITE:
+			//TODO - need icon
+			control = "inv_folder_plain_closed.tga";
+			break;
+		default:
+			control = "inv_folder_plain_closed.tga";
+			break;
 	}
 	return LLUI::getUIImage(control);
 }
@@ -1794,6 +1975,8 @@ BOOL LLFolderBridge::renameItem(const std::string& new_name)
 		new_cat->rename(new_name);
 		new_cat->updateServer(FALSE);
 		model->updateCategory(new_cat);
+		renameLinkedItems(cat->getUUID(),new_name);
+
 		model->notifyObservers();
 	}
 	// return FALSE because we either notified observers (& therefore
@@ -1842,15 +2025,6 @@ BOOL LLFolderBridge::removeItem()
 	return TRUE;
 }
 
-BOOL LLFolderBridge::isClipboardPasteable() const
-{
-	if(LLInventoryClipboard::instance().hasContents() && isAgentInventory())
-	{
-		return TRUE;
-	}
-	return FALSE;
-}
-
 void LLFolderBridge::pasteFromClipboard()
 {
 	LLInventoryModel* model = getInventoryModel();
@@ -1878,6 +2052,33 @@ void LLFolderBridge::pasteFromClipboard()
 	}
 }
 
+void LLFolderBridge::pasteLinkFromClipboard()
+{
+	LLInventoryModel* model = getInventoryModel();
+	if(model)
+	{
+		LLInventoryItem* item = NULL;
+		LLDynamicArray<LLUUID> objects;
+		LLInventoryClipboard::instance().retrieve(objects);
+		S32 count = objects.count();
+		LLUUID parent_id(mUUID);
+		for(S32 i = 0; i < count; i++)
+		{
+			item = model->getItem(objects.get(i));
+			if (item)
+			{
+				link_inventory_item(
+					gAgent.getID(),
+					item->getUUID(),
+					parent_id,
+					item->getName(),
+					LLAssetType::AT_LINK,
+					LLPointer<LLInventoryCallback>(NULL));
+			}
+		}
+	}
+}
+
 void LLFolderBridge::staticFolderOptionsMenu()
 {
 	if (!sSelf) return;
@@ -1948,6 +2149,9 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv
 // Flags unused
 void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 {
+	mItems.clear();
+	mDisabledItems.clear();
+
 	lldebugs << "LLFolderBridge::buildContextMenu()" << llendl;
 //	std::vector<std::string> disabled_items;
 	LLInventoryModel* model = getInventoryModel();
@@ -3116,7 +3320,7 @@ BOOL LLObjectBridge::isItemRemovable()
 {
 	LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
 	if(!avatar) return FALSE;
-	if(avatar->isWearingAttachment(mUUID)) return FALSE;
+	if(avatar->isWearingAttachment(mUUID, TRUE)) return FALSE;
 	return LLInvFVBridge::isItemRemovable();
 }
 
@@ -3125,6 +3329,17 @@ LLUIImagePtr LLObjectBridge::getIcon() const
 	return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt, mIsMultiObject );
 }
 
+LLInventoryObject* LLObjectBridge::getObject() const
+{
+	LLInventoryObject* object = NULL;
+	LLInventoryModel* model = getInventoryModel();
+	if(model)
+	{
+		object = (LLInventoryObject*)model->getObject(mUUID);
+	}
+	return object;
+}
+
 // virtual
 void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, std::string action)
 {
@@ -3194,15 +3409,21 @@ void LLObjectBridge::openItem()
 
 LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const
 { 
+	U8 font = LLFontGL::NORMAL;
+
 	LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
 	if( avatar && avatar->isWearingAttachment( mUUID ) )
 	{
-		return LLFontGL::BOLD;
+		font |= LLFontGL::BOLD;
 	}
-	else
+
+	LLInventoryItem* item = getItem();
+	if (item->getActualType() == LLAssetType::AT_LINK)
 	{
-		return LLFontGL::NORMAL;
+		font |= LLFontGL::ITALIC;
 	}
+	
+	return (LLFontGL::StyleFlags)font;
 }
 
 std::string LLObjectBridge::getLabelSuffix() const
@@ -3223,7 +3444,7 @@ std::string LLObjectBridge::getLabelSuffix() const
 void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
 {
 	LLSD payload;
-	payload["item_id"] = item->getUUID();
+	payload["item_id"] = item->getLinkedUUID(); // Wear the base object in case this is a link.
 
 	S32 attach_pt = 0;
 	if (gAgent.getAvatarObject() && attachment)
@@ -3315,7 +3536,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 				items.push_back(std::string("Detach From Yourself"));
 			}
 			else
-			if( !isInTrash() )
+			if( !isInTrash() && !isLinkedObjectInTrash() )
 			{
 				items.push_back(std::string("Attach Separator"));
 				items.push_back(std::string("Object Wear"));
@@ -3381,6 +3602,8 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name)
 		buildDisplayName(new_item, mDisplayName);
 		new_item->updateServer(FALSE);
 		model->updateItem(new_item);
+		renameLinkedItems(item->getUUID(),new_name);
+
 		model->notifyObservers();
 
 		LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
@@ -4141,23 +4364,10 @@ BOOL LLWearableBridge::renameItem(const std::string& new_name)
 
 BOOL LLWearableBridge::isItemRemovable()
 {
-	if(gAgentWearables.isWearingItem(mUUID)) return FALSE;
+	if (gAgentWearables.isWearingItem(mUUID, TRUE)) return FALSE;
 	return LLInvFVBridge::isItemRemovable();
 }
 
-LLFontGL::StyleFlags LLWearableBridge::getLabelStyle() const
-{ 
-	if( gAgentWearables.isWearingItem( mUUID ) )
-	{
-		// llinfos << "BOLD" << llendl;
-		return LLFontGL::BOLD;
-	}
-	else
-	{
-		return LLFontGL::NORMAL;
-	}
-}
-
 std::string LLWearableBridge::getLabelSuffix() const
 {
 	if( gAgentWearables.isWearingItem( mUUID ) )
@@ -4300,6 +4510,13 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 		{
 			disabled_items.push_back(std::string("Wearable Edit"));
 		}
+		// Don't allow items to be worn if their baseobj is in the trash.
+		if (isLinkedObjectInTrash())
+		{
+			disabled_items.push_back(std::string("Wearable Wear"));
+			disabled_items.push_back(std::string("Wearable Add"));
+			disabled_items.push_back(std::string("Wearable Edit"));
+		}
 
 		if( item && (item->getType() == LLAssetType::AT_CLOTHING) )
 		{
@@ -4534,9 +4751,8 @@ void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable,
 	delete item_id;
 }
 
-
 LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_type,
-									   const LLUUID& uuid,LLInventoryModel* model)
+													   const LLUUID& uuid,LLInventoryModel* model)
 {
 	LLInvFVBridgeAction* action = NULL;
 	switch(asset_type)
@@ -4819,3 +5035,44 @@ void	LLWearableBridgeAction::doIt()
 
 	LLInvFVBridgeAction::doIt();
 }
+
+// +=================================================+
+// |        LLLinkItemBridge                         |
+// +=================================================+
+// For broken links
+
+std::string LLLinkItemBridge::sPrefix("Link: ");
+
+
+LLUIImagePtr LLLinkItemBridge::getIcon() const
+{
+	return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE);
+}
+
+void LLLinkItemBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+	// *TODO: Translate
+	lldebugs << "LLLink::buildContextMenu()" << llendl;
+	std::vector<std::string> items;
+	std::vector<std::string> disabled_items;
+
+	if(isInTrash())
+	{
+		items.push_back(std::string("Purge Item"));
+		if (!isItemRemovable())
+		{
+			disabled_items.push_back(std::string("Purge Item"));
+		}
+
+		items.push_back(std::string("Restore Item"));
+	}
+	else
+	{	
+		items.push_back(std::string("Delete"));
+		if (!isItemRemovable())
+		{
+			disabled_items.push_back(std::string("Delete"));
+		}
+	}
+	hideContextEntries(menu, items, disabled_items);
+}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 3958f7e9c2..016eb701d6 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -167,7 +167,9 @@ public:
 	virtual BOOL copyToClipboard() const { return FALSE; }
 	virtual void cutToClipboard() {}
 	virtual BOOL isClipboardPasteable() const;
+	virtual BOOL isClipboardPasteableAsLink() const;
 	virtual void pasteFromClipboard() {}
+	virtual void pasteLinkFromClipboard() {}
 	void getClipboardEntries(bool show_asset_id, std::vector<std::string> &items, 
 		std::vector<std::string> &disabled_items, U32 flags);
 	virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
@@ -191,6 +193,8 @@ protected:
 	LLInventoryModel* getInventoryModel() const;
 	
 	BOOL isInTrash() const;
+	BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash?
+
 	// return true if the item is in agent inventory. if false, it
 	// must be lost or in the inventory library.
 	BOOL isAgentInventory() const;
@@ -204,11 +208,13 @@ protected:
 									 const LLUUID& new_parent,
 									 BOOL restamp);
 	void removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch);
-	
+	void renameLinkedItems(const LLUUID &item_id, const std::string& new_name);
+
 protected:
 	LLHandle<LLPanel> mInventoryPanel;
-	LLUUID mUUID;	// item id
+	const LLUUID mUUID;	// item id
 	LLInventoryType::EType mInvType;
+	void purgeItem(LLInventoryModel *model, const LLUUID &uuid);
 };
 
 
@@ -227,6 +233,7 @@ public:
 	virtual LLUIImagePtr getIcon() const;
 	virtual const std::string& getDisplayName() const;
 	virtual std::string getLabelSuffix() const;
+	virtual LLFontGL::StyleFlags getLabelStyle() const;
 	virtual PermissionMask getPermissionMask() const;
 	virtual time_t getCreationDate() const;
 	virtual BOOL isItemRenameable() const;
@@ -267,8 +274,8 @@ public:
 	virtual LLUIImagePtr getIcon() const;
 	virtual BOOL renameItem(const std::string& new_name);
 	virtual BOOL removeItem();
-	virtual BOOL isClipboardPasteable() const;
 	virtual void pasteFromClipboard();
+	virtual void pasteLinkFromClipboard();
 	virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
 	virtual BOOL hasChildren() const;
 	virtual BOOL dragOrDrop(MASK mask, BOOL drop,
@@ -278,7 +285,9 @@ public:
 	virtual BOOL isItemRemovable();
 	virtual BOOL isItemMovable();
 	virtual BOOL isUpToDate() const;
-
+	virtual BOOL isItemCopyable() const;
+	virtual BOOL copyToClipboard() const;
+	
 	static void createWearable(LLFolderBridge* bridge, EWearableType type);
 	static void createWearable(LLUUID parent_folder_id, EWearableType type);
 
@@ -489,6 +498,8 @@ public:
 	virtual BOOL			isItemRemovable();
 	virtual BOOL renameItem(const std::string& new_name);
 
+	LLInventoryObject* getObject() const;
+
 protected:
 	LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) :
 		LLItemBridge(inventory, uuid), mInvType(type)
@@ -527,7 +538,6 @@ public:
 	virtual void	performAction(LLFolderView* folder, LLInventoryModel* model, std::string action);
 	virtual void	openItem();
 	virtual void	buildContextMenu(LLMenuGL& menu, U32 flags);
-	virtual LLFontGL::StyleFlags getLabelStyle() const;
 	virtual std::string getLabelSuffix() const;
 	virtual BOOL	isItemRemovable();
 	virtual BOOL renameItem(const std::string& new_name);
@@ -562,8 +572,25 @@ protected:
 	EWearableType  mWearableType;
 };
 
+class LLLinkItemBridge : public LLItemBridge
+{
+	friend class LLInvFVBridge;
+public:
+	virtual const std::string& getPrefix() { return sPrefix; }
+
+	virtual LLUIImagePtr getIcon() const;
+	virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+protected:
+	LLLinkItemBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+		LLItemBridge(inventory, uuid) {}
+
+protected:
+	static std::string sPrefix;
+};
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLInvFVBridgeAction (& it's derived classes)
+// Class LLInvFVBridgeAction (& its derived classes)
 //
 // This is an implementation class to be able to 
 // perform action to view inventory items.
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 9177d51d5c..5a14bdd55e 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -464,6 +464,18 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
 	}
 }
 
+void LLInventoryModel::collectLinkedItems(const LLUUID& id,
+										  item_array_t& items)
+{
+	LLInventoryModel::cat_array_t cat_array;
+	LLLinkedItemIDMatches is_linked_item_match(id);
+	collectDescendentsIf(gAgent.getInventoryRootID(),
+						 cat_array,
+						 items,
+						 LLInventoryModel::INCLUDE_TRASH,
+						 is_linked_item_match);
+}
+
 // Generates a string containing the path to the item specified by
 // item_id.
 void LLInventoryModel::appendPath(const LLUUID& id, std::string& path)
@@ -747,6 +759,7 @@ void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
 // Delete a particular inventory object by ID.
 void LLInventoryModel::deleteObject(const LLUUID& id)
 {
+	purgeLinkedObjects(id);
 	lldebugs << "LLInventoryModel::deleteObject()" << llendl;
 	LLPointer<LLInventoryObject> obj = getObject(id);
 	if(obj)
@@ -786,6 +799,42 @@ void LLInventoryModel::deleteObject(const LLUUID& id)
 	}
 }
 
+// Delete a particular inventory item by ID, and remove it from the server.
+void LLInventoryModel::purgeObject(const LLUUID &id)
+{
+	lldebugs << "LLInventoryModel::purgeObject()" << llendl;
+	LLPointer<LLInventoryObject> obj = getObject(id);
+	if(obj)
+	{
+		obj->removeFromServer();
+		LLPreview::hide(id);
+		deleteObject(id);
+	}
+}
+
+void LLInventoryModel::purgeLinkedObjects(const LLUUID &id)
+{
+	LLInventoryItem* itemp = getItem(id);
+	if (!itemp) return;
+
+	if (LLAssetType::lookupIsLinkType(itemp->getActualType()))
+	{
+		return;
+	}
+
+	LLInventoryModel::item_array_t item_array;
+	collectLinkedItems(id, item_array);
+	
+	for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
+		 iter != item_array.end();
+		 iter++)
+	{
+		LLViewerInventoryItem *linked_item = (*iter);
+		if (linked_item->getUUID() == id) continue;
+		purgeObject(linked_item->getUUID());
+	}
+}
+
 // This is a method which collects the descendents of the id
 // provided. If the category is not found, no action is
 // taken. This method goes through the long winded process of
@@ -3914,11 +3963,20 @@ void LLInventoryTransactionObserver::changed(U32 mask)
 ///----------------------------------------------------------------------------
 /// LLAssetIDMatches 
 ///----------------------------------------------------------------------------
-bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+bool LLAssetIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
 {
 	return (item && item->getAssetUUID() == mAssetID);
 }
 
+///----------------------------------------------------------------------------
+/// LLLinkedItemIDMatches 
+///----------------------------------------------------------------------------
+bool LLLinkedItemIDMatches::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+	return (item && 
+			(LLAssetType::lookupIsLinkType(item->getActualType())) &&
+			(item->getLinkedUUID() == mBaseItemID)); // A linked item's assetID will be the compared-to item's itemID.
+}
 
 ///----------------------------------------------------------------------------
 /// Local function definitions
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 77e604769e..46288700d2 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -169,7 +169,7 @@ public:
 									item_array_t*& items);
 	void unlockDirectDescendentArrays(const LLUUID& cat_id);
 	
-	// Starting with the object specified, add it's descendents to the
+	// Starting with the object specified, add its descendents to the
 	// array provided, but do not add the inventory object specified
 	// by id. There is no guaranteed order. Neither array will be
 	// erased before adding objects to it. Do not store a copy of the
@@ -187,6 +187,11 @@ public:
 							  BOOL include_trash,
 							  LLInventoryCollectFunctor& add);
 
+	// Collect all items in inventory that are linked to item_id.
+	// Assumes item_id is itself not a linked item.
+	void collectLinkedItems(const LLUUID& item_id,
+							item_array_t& items);
+	
 	// This method will return false if this inventory model is in an usabel state.
 	// The inventory model usage is sensitive to the initial construction of the 
 	// model. 
@@ -227,8 +232,13 @@ public:
 	// delete a particular inventory object by ID. This will purge one
 	// object from the internal data structures maintaining a
 	// cosistent internal state. No cache accounting, observer
-	// notification, or server update is performed.
+	// notification, or server update is performed.  Purges linked items.
 	void deleteObject(const LLUUID& id);
+	
+	// delete a particular inventory object by ID, and delete it from
+	// the server.  Also purges linked items via purgeLinkedObjects.
+	void purgeObject(const LLUUID& id);
+	void purgeLinkedObjects(const LLUUID& id);
 
 	// This is a method which collects the descendents of the id
 	// provided. If the category is not found, no action is
@@ -530,6 +540,22 @@ protected:
 	LLUUID mAssetID;
 };
 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLLinkedItemIDMatches
+//
+// This functor finds inventory items linked to the specific inventory id.
+// Assumes the inventory id is itself not a linked item.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLLinkedItemIDMatches : public LLInventoryCollectFunctor
+{
+public:
+	LLLinkedItemIDMatches(const LLUUID& item_id) : mBaseItemID(item_id) {}
+	virtual ~LLLinkedItemIDMatches() {}
+	bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+	
+protected:
+	LLUUID mBaseItemID;
+};
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Class LLIsType
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 45d24ee7e8..2c79e67ebc 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -244,8 +244,7 @@ BOOL LLViewerInventoryItem::unpackMessage(LLSD item)
 }
 
 // virtual
-BOOL LLViewerInventoryItem::unpackMessage(
-	LLMessageSystem* msg, const char* block, S32 block_num)
+BOOL LLViewerInventoryItem::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num)
 {
 	BOOL rv = LLInventoryItem::unpackMessage(msg, block, block_num);
 	mIsComplete = TRUE;
@@ -742,6 +741,32 @@ void copy_inventory_item(
 	gAgent.sendReliableMessage();
 }
 
+void link_inventory_item(
+	const LLUUID& agent_id,
+	const LLUUID& item_id,
+	const LLUUID& parent_id,
+	const std::string& new_name,
+	const LLAssetType::EType asset_type,
+	LLPointer<LLInventoryCallback> cb)
+{
+	LLMessageSystem* msg = gMessageSystem;
+	msg->newMessageFast(_PREHASH_LinkInventoryItem);
+	msg->nextBlockFast(_PREHASH_AgentData);
+	{
+		msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+	}
+	msg->nextBlockFast(_PREHASH_InventoryData);
+	{
+		msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
+		msg->addUUIDFast(_PREHASH_FolderID, parent_id);
+		msg->addUUIDFast(_PREHASH_OldItemID, item_id);
+		msg->addStringFast(_PREHASH_Name, new_name);
+		msg->addU8Fast(_PREHASH_AssetType, asset_type);
+	}
+	gAgent.sendReliableMessage();
+}
+
 void move_inventory_item(
 	const LLUUID& agent_id,
 	const LLUUID& session_id,
@@ -948,26 +973,19 @@ void menu_create_inventory_item(LLFolderView* folder, LLFolderBridge *bridge, co
 
 LLAssetType::EType LLViewerInventoryItem::getType() const
 {
-	if (mType == LLAssetType::AT_LINK)
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
 	{
-		LLInventoryItem *linked_item = gInventory.getItem(mAssetUUID);
-		if (linked_item)
-		{
-			return linked_item->getType();
-		}
+		return linked_item->getType();
 	}
+	
 	return LLInventoryItem::getType();
 }
 
 const LLUUID& LLViewerInventoryItem::getAssetUUID() const
 {
-	if (mType == LLAssetType::AT_LINK)
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
 	{
-		LLInventoryItem *linked_item = gInventory.getItem(mAssetUUID);
-		if (linked_item)
-		{
-			return linked_item->getAssetUUID();
-		}
+		return linked_item->getAssetUUID();
 	}
 
 	return LLInventoryItem::getAssetUUID();
@@ -975,11 +993,96 @@ const LLUUID& LLViewerInventoryItem::getAssetUUID() const
 
 const std::string& LLViewerInventoryItem::getName() const
 {
-	if (mType == LLAssetType::AT_LINK)
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
 	{
-		return LLInventoryItem::getName(); //+" link";
+		return linked_item->getName();
 	}
 
 	return LLInventoryItem::getName();
 }
 
+const LLPermissions& LLViewerInventoryItem::getPermissions() const
+{
+	// Use the actual permissions of the symlink, not its parent.
+	return LLInventoryItem::getPermissions();	
+}
+
+const LLUUID& LLViewerInventoryItem::getCreatorUUID() const
+{
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
+	{
+		return linked_item->getCreatorUUID();
+	}
+
+	return LLInventoryItem::getCreatorUUID();
+}
+
+const std::string& LLViewerInventoryItem::getDescription() const
+{
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
+	{
+		return linked_item->getDescription();
+	}
+
+	return LLInventoryItem::getDescription();
+}
+
+const LLSaleInfo& LLViewerInventoryItem::getSaleInfo() const
+{	
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
+	{
+		return linked_item->getSaleInfo();
+	}
+
+	return LLInventoryItem::getSaleInfo();
+}
+
+LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const
+{
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
+	{
+		return linked_item->getInventoryType();
+	}
+
+	return LLInventoryItem::getInventoryType();
+}
+
+U32 LLViewerInventoryItem::getFlags() const
+{
+	if (const LLViewerInventoryItem *linked_item = getLinkedItem())
+	{
+		return linked_item->getFlags();
+	}
+
+	return LLInventoryItem::getFlags();
+}
+
+time_t LLViewerInventoryItem::getCreationDate() const
+{
+	return LLInventoryItem::getCreationDate();
+}
+
+U32 LLViewerInventoryItem::getCRC32() const
+{
+	return LLInventoryItem::getCRC32();	
+}
+
+const LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const
+{
+	if (mType == LLAssetType::AT_LINK)
+	{
+		const LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID);
+		return linked_item;
+	}
+	return NULL;
+}
+
+const LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const
+{
+	if (mType == LLAssetType::AT_LINK_FOLDER)
+	{
+		const LLViewerInventoryCategory *linked_category = gInventory.getCategory(mAssetUUID);
+		return linked_category;
+	}
+	return NULL;
+}
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index 8318931dde..7084c9f37a 100644
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -39,6 +39,7 @@
 
 class LLFolderView;
 class LLFolderBridge;
+class LLViewerInventoryCategory;
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Class LLViewerInventoryItem
@@ -59,7 +60,15 @@ public:
 	virtual LLAssetType::EType getType() const;
 	virtual const LLUUID& getAssetUUID() const;
 	virtual const std::string& getName() const;
-	
+	virtual const LLPermissions& getPermissions() const;
+	virtual const LLUUID& getCreatorUUID() const;
+	virtual const std::string& getDescription() const;
+	virtual const LLSaleInfo& getSaleInfo() const;
+	virtual LLInventoryType::EType getInventoryType() const;
+	virtual U32 getFlags() const;
+	virtual time_t getCreationDate() const;
+	virtual U32 getCRC32() const; // really more of a checksum.
+
 	// construct a complete viewer inventory item
 	LLViewerInventoryItem(const LLUUID& uuid, const LLUUID& parent_uuid,
 						  const LLPermissions& permissions,
@@ -133,6 +142,9 @@ public:
 	LLTransactionID getTransactionID() const { return mTransactionID; }
 	
 protected:
+	const LLViewerInventoryItem *getLinkedItem() const;
+	const LLViewerInventoryCategory *getLinkedCategory() const;
+
 	BOOL mIsComplete;
 	LLTransactionID mTransactionID;
 };
@@ -279,6 +291,14 @@ void copy_inventory_item(
 	const std::string& new_name,
 	LLPointer<LLInventoryCallback> cb);
 
+void link_inventory_item(
+	const LLUUID& agent_id,
+	const LLUUID& item_id,
+	const LLUUID& parent_id,
+	const std::string& new_name,
+	const LLAssetType::EType asset_type,
+	LLPointer<LLInventoryCallback> cb);
+
 void move_inventory_item(
 	const LLUUID& agent_id,
 	const LLUUID& session_id,
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index d629767bbe..e7d7d74f62 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -907,18 +907,43 @@ void LLVOAvatarSelf::wearableUpdated( EWearableType type )
 //-----------------------------------------------------------------------------
 // isWearingAttachment()
 //-----------------------------------------------------------------------------
-BOOL LLVOAvatarSelf::isWearingAttachment( const LLUUID& inv_item_id )
+// Warning: include_linked_items = TRUE makes this operation expensive.
+BOOL LLVOAvatarSelf::isWearingAttachment( const LLUUID& inv_item_id , BOOL include_linked_items ) const
 {
-	for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); 
+	for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); 
 		 iter != mAttachmentPoints.end(); )
 	{
-		attachment_map_t::iterator curiter = iter++;
-		LLViewerJointAttachment* attachment = curiter->second;
+		attachment_map_t::const_iterator curiter = iter++;
+		const LLViewerJointAttachment* attachment = curiter->second;
 		if( attachment->getItemID() == inv_item_id )
 		{
 			return TRUE;
 		}
 	}
+
+	if (include_linked_items)
+	{
+		LLInventoryModel::item_array_t item_array;
+		gInventory.collectLinkedItems(inv_item_id, item_array);
+		for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
+			 iter != item_array.end();
+			 iter++)
+		{
+			const LLViewerInventoryItem *linked_item = (*iter);
+			const LLUUID &item_id = linked_item->getUUID();
+			for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); 
+				 iter != mAttachmentPoints.end(); )
+			{
+				attachment_map_t::const_iterator curiter = iter++;
+				const LLViewerJointAttachment* attachment = curiter->second;
+				if( attachment->getItemID() == item_id )
+				{
+					return TRUE;
+				}
+			}
+		}
+	}
+
 	return FALSE;
 }
 
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index 431c814382..02a77cba90 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -267,7 +267,7 @@ public:
 	//--------------------------------------------------------------------
 public:
 	void 				updateAttachmentVisibility(U32 camera_mode);
-	BOOL 				isWearingAttachment(const LLUUID& inv_item_id);
+	BOOL 				isWearingAttachment(const LLUUID& inv_item_id, BOOL include_linked_items = FALSE) const;
 	LLViewerObject* 	getWornAttachment(const LLUUID& inv_item_id ) const;
 	const std::string   getAttachedPointName(const LLUUID& inv_item_id) const;
 	/*virtual*/ LLViewerJointAttachment *attachObject(LLViewerObject *viewer_object);
diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp
index 92de94636b..1275312676 100644
--- a/indra/newview/llwearablelist.cpp
+++ b/indra/newview/llwearablelist.cpp
@@ -77,14 +77,17 @@ LLWearableList::~LLWearableList()
 void LLWearableList::getAsset(const LLAssetID& _assetID, const std::string& wearable_name, LLAssetType::EType asset_type, void(*asset_arrived_callback)(LLWearable*, void* userdata), void* userdata)
 {
 	LLAssetID assetID = _assetID;
-	if (asset_type == LLAssetType::AT_LINK)
+
+	// A bit of a hack since wearables database doesn't contain asset types...
+	// Perform indirection in case this assetID is in fact a link.  This only works
+	// because of the assumption that all assetIDs and itemIDs are unique (i.e.
+	// no assetID is also used as an itemID elsewhere); therefore if the assetID
+	// exists as an itemID in the user's inventory, then this must be a link.
+	const LLInventoryItem *linked_item = gInventory.getItem(_assetID);
+	if (linked_item)
 	{
-		LLInventoryItem *linked_item = gInventory.getItem(_assetID);
-		if (linked_item)
-		{
-			assetID = linked_item->getAssetUUID();
-			asset_type = linked_item->getType();
-		}
+		assetID = linked_item->getAssetUUID();
+		asset_type = linked_item->getType();
 	}
 	llassert( (asset_type == LLAssetType::AT_CLOTHING) || (asset_type == LLAssetType::AT_BODYPART) );
 	LLWearable* instance = get_if_there(mList, assetID, (LLWearable*)NULL );
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index 808618ba96..6f2fd5e5e5 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -320,6 +320,14 @@
          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_separator
      layout="topleft" />
     <menu_item_call
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
index 23efd65c45..67233cbda0 100644
--- a/scripts/messages/message_template.msg
+++ b/scripts/messages/message_template.msg
@@ -8945,3 +8945,21 @@ version 2.0
 		{	CRC				U32	}
 	}
 }
+
+{
+	LinkInventoryItem	Low	426 NotTrusted	Zerocoded
+	{
+		AgentData		Single
+		{	AgentID		LLUUID	}
+		{	SessionID	LLUUID	}
+	}
+	{
+		InventoryData		Variable
+		{	CallbackID	U32			} // Async Response
+		{	FolderID		LLUUID	}
+		{	OldItemID		LLUUID	}
+		{	Name			Variable	1	}
+		{	AssetType		U8		}
+	}
+}
+
-- 
cgit v1.2.3