diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake
index db0b44eb8f0104a236aa823bf3f8f07758258043..bfaf3f4f26fc4d88958f0652c96f075c01623d5e 100644
--- a/indra/cmake/Variables.cmake
+++ b/indra/cmake/Variables.cmake
@@ -75,11 +75,33 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
 
 if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
   set(DARWIN 1)
-  # set this dynamically from the build system now -
-  # NOTE: wont have a distributable build unless you add this on the configure line with:
+
+  # NOTE: If specifying a different SDK with CMAKE_OSX_SYSROOT at configure
+  # time you should also specify CMAKE_OSX_DEPLOYMENT_TARGET explicitly,
+  # otherwise CMAKE_OSX_SYSROOT will be overridden here. We can't just check
+  # for it being unset, as it gets set to the system default :(
+
+  # Default to building against the 10.4 SDK if no deployment target is
+  # specified.
+  if (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
+    # NOTE: setting -isysroot is NOT adequate: http://lists.apple.com/archives/Xcode-users/2007/Oct/msg00696.html
+    # see http://public.kitware.com/Bug/view.php?id=9959 + poppy
+    set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.4u.sdk)
+    set(CMAKE_OSX_DEPLOYMENT_TARGET 10.4)
+  endif (NOT CMAKE_OSX_DEPLOYMENT_TARGET)
+
+  # GCC 4.2 is incompatible with the MacOSX 10.4 SDK
+  if (${CMAKE_OSX_SYSROOT} MATCHES "10.4u")
+    set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "4.0")
+  endif (${CMAKE_OSX_SYSROOT} MATCHES "10.4u")
+
+  # NOTE: To attempt an i386/PPC Universal build, add this on the configure line:
   # -DCMAKE_OSX_ARCHITECTURES:STRING='i386;ppc'
-  #set(CMAKE_OSX_ARCHITECTURES i386;ppc)
-  set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.4u.sdk)
+  # Build only for i386 by default, system default on MacOSX 10.6 is x86_64
+  if (NOT CMAKE_OSX_ARCHITECTURES)
+    set(CMAKE_OSX_ARCHITECTURES i386)
+  endif (NOT CMAKE_OSX_ARCHITECTURES)
+
   if (CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc")
     set(ARCH universal)
   else (CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc")
@@ -89,6 +111,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
       set(ARCH i386)
     endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "ppc")
   endif (CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_ARCHITECTURES MATCHES "ppc")
+
   set(LL_ARCH ${ARCH}_darwin)
   set(LL_ARCH_DIR universal-darwin)
   set(WORD_SIZE 32)
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 65ef53443b3f629ed15acad5926444505ceeef03..7b8f51ae3c8131f6a8182d919880e28d0735b832 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -48,122 +48,38 @@
 
 const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
 
-// local channel for notification history
-class LLNotificationHistoryChannel : public LLNotificationChannel
+// Local channel for persistent notifications
+// Stores only persistent notifications.
+// Class users can use connectChanged() to process persistent notifications
+// (see LLNotificationStorage for example).
+class LLPersistentNotificationChannel : public LLNotificationChannel
 {
-	LOG_CLASS(LLNotificationHistoryChannel);
+	LOG_CLASS(LLPersistentNotificationChannel);
 public:
-	LLNotificationHistoryChannel(const std::string& filename) : 
-		LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()),
-		mFileName(filename)
+	LLPersistentNotificationChannel() :
+		LLNotificationChannel("Persistent", "Visible", &notificationFilter, LLNotificationComparators::orderByUUID())
 	{
-		connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1));
-		loadPersistentNotifications();
 	}
 
 private:
-	bool historyHandler(const LLSD& payload)
-	{
-		// we ignore "load" messages, but rewrite the persistence file on any other
-		std::string sigtype = payload["sigtype"];
-		if (sigtype != "load")
-		{
-			savePersistentNotifications();
-		}
-		return false;
-	}
-
-	// The history channel gets all notifications except those that have been cancelled
-	static bool historyFilter(LLNotificationPtr pNotification)
-	{
-		return !pNotification->isCancelled();
-	}
 
-	void savePersistentNotifications()
+	// The channel gets all persistent notifications except those that have been canceled
+	static bool notificationFilter(LLNotificationPtr pNotification)
 	{
-		/* NOTE: As of 2009-11-09 the reload of notifications on startup does not
-		work, and has not worked for months.  Skip saving notifications until the
-		read can be fixed, because this hits the disk once per notification and
-		causes log spam.  James
-
-		llinfos << "Saving open notifications to " << mFileName << llendl;
+		bool handle_notification = false;
 
-		llofstream notify_file(mFileName.c_str());
-		if (!notify_file.is_open()) 
-		{
-			llwarns << "Failed to open " << mFileName << llendl;
-			return;
-		}
-
-		LLSD output;
-		output["version"] = NOTIFICATION_PERSIST_VERSION;
-		LLSD& data = output["data"];
-
-		for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
-		{
-			if (!LLNotifications::instance().templateExists((*it)->getName())) continue;
+		handle_notification = pNotification->isPersistent()
+			&& !pNotification->isCancelled();
 
-			// only store notifications flagged as persisting
-			LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName());
-			if (!templatep->mPersist) continue;
-
-			data.append((*it)->asLLSD());
-		}
-
-		LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
-		formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
-		*/
+		return handle_notification;
 	}
 
-	void loadPersistentNotifications()
-	{
-		llinfos << "Loading open notifications from " << mFileName << llendl;
-
-		llifstream notify_file(mFileName.c_str());
-		if (!notify_file.is_open()) 
-		{
-			llwarns << "Failed to open " << mFileName << llendl;
-			return;
-		}
-
-		LLSD input;
-		LLPointer<LLSDParser> parser = new LLSDXMLParser();
-		if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
-		{
-			llwarns << "Failed to parse open notifications" << llendl;
-			return;
-		}
-
-		if (input.isUndefined()) return;
-		std::string version = input["version"];
-		if (version != NOTIFICATION_PERSIST_VERSION)
-		{
-			llwarns << "Bad open notifications version: " << version << llendl;
-			return;
-		}
-		LLSD& data = input["data"];
-		if (data.isUndefined()) return;
-
-		LLNotifications& instance = LLNotifications::instance();
-		for (LLSD::array_const_iterator notification_it = data.beginArray();
-			notification_it != data.endArray();
-			++notification_it)
-		{
-			instance.add(LLNotificationPtr(new LLNotification(*notification_it)));
-		}
-	}
-
-	//virtual
 	void onDelete(LLNotificationPtr pNotification)
 	{
-		// we want to keep deleted notifications in our log
+		// we want to keep deleted notifications in our log, otherwise some 
+		// notifications will be lost on exit.
 		mItems.insert(pNotification);
-		
-		return;
 	}
-	
-private:
-	std::string mFileName;
 };
 
 bool filterIgnoredNotifications(LLNotificationPtr notification)
@@ -417,6 +333,10 @@ LLNotification::LLNotification(const LLNotification::Params& p) :
 
 		mTemporaryResponder = true;
 	}
+	else if(p.functor.responder.isChosen())
+	{
+		mResponder = p.functor.responder;
+	}
 
 	if(p.responder.isProvided())
 	{
@@ -462,6 +382,12 @@ LLSD LLNotification::asLLSD()
 	output["priority"] = (S32)mPriority;
 	output["responseFunctor"] = mResponseFunctorName;
 	output["reusable"] = mIsReusable;
+
+	if(mResponder)
+	{
+		output["responder"] = mResponder->asLLSD();
+	}
+
 	return output;
 }
 
@@ -571,12 +497,20 @@ void LLNotification::respond(const LLSD& response)
 	// *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
 	mRespondedTo = true;
 	mResponse = response;
-	// look up the functor
-	LLNotificationFunctorRegistry::ResponseFunctor functor = 
-		LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
-	// and then call it
-	functor(asLLSD(), response);
-	
+
+	if(mResponder)
+	{
+		mResponder->handleRespond(asLLSD(), response);
+	}
+	else
+	{
+		// look up the functor
+		LLNotificationFunctorRegistry::ResponseFunctor functor =
+			LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
+		// and then call it
+		functor(asLLSD(), response);
+	}
+
 	if (mTemporaryResponder && !isReusable())
 	{
 		LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
@@ -621,6 +555,11 @@ void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::Res
 	LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
 }
 
+void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
+{
+	mResponder = responder;
+}
+
 bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
 {
 	for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin(); 
@@ -1116,12 +1055,9 @@ void LLNotifications::createDefaultChannels()
 	LLNotificationChannel::buildChannel("Visible", "Ignore",
 		&LLNotificationFilters::includeEverything);
 
-	// create special history channel
-	//std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
-	// use ^^^ when done debugging notifications serialization
-	std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" );
+	// create special persistent notification channel
 	// this isn't a leak, don't worry about the empty "new"
-	new LLNotificationHistoryChannel(notifications_log_file);
+	new LLPersistentNotificationChannel();
 
 	// connect action methods to these channels
 	LLNotifications::instance().getChannel("Expiration")->
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 1799ca65b7ac4eb9f62d6103b60e996ab122402f..c942a3251203ebd728e0ca7dfa4d6929cd29a611 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -116,8 +116,23 @@ typedef enum e_notification_priority
 	NOTIFICATION_PRIORITY_CRITICAL
 } ENotificationPriority;
 
+class LLNotificationResponderInterface
+{
+public:
+	LLNotificationResponderInterface(){};
+	virtual ~LLNotificationResponderInterface(){};
+
+	virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0;
+
+	virtual LLSD asLLSD() = 0;
+
+	virtual void fromLLSD(const LLSD& params) = 0;
+};
+
 typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
 
+typedef boost::shared_ptr<LLNotificationResponderInterface> LLNotificationResponderPtr;
+
 typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
 typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
 
@@ -303,10 +318,12 @@ friend class LLNotifications;
 		{
 			Alternative<std::string>										name;
 			Alternative<LLNotificationFunctorRegistry::ResponseFunctor>	function;
+			Alternative<LLNotificationResponderPtr>						responder;
 
 			Functor()
 			:	name("functor_name"),
-				function("functor")
+				function("functor"),
+				responder("responder")
 			{}
 		};
 		Optional<Functor>						functor;
@@ -349,12 +366,13 @@ friend class LLNotifications;
 	bool mIgnored;
 	ENotificationPriority mPriority;
 	LLNotificationFormPtr mForm;
-	void* mResponderObj;
+	void* mResponderObj; // TODO - refactor/remove this field
 	bool mIsReusable;
-	
+	LLNotificationResponderPtr mResponder;
+
 	// a reference to the template
 	LLNotificationTemplatePtr mTemplatep;
-	
+
 	/*
 	 We want to be able to store and reload notifications so that they can survive
 	 a shutdown/restart of the client. So we can't simply pass in callbacks;
@@ -393,6 +411,8 @@ friend class LLNotifications;
 
 	void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb);
 
+	void setResponseFunctor(const LLNotificationResponderPtr& responder);
+
 	typedef enum e_response_template_type
 	{
 		WITHOUT_DEFAULT_BUTTON,
@@ -459,7 +479,12 @@ friend class LLNotifications;
 	{
 		return mTemplatep->mName;
 	}
-	
+
+	bool isPersistent() const
+	{
+		return mTemplatep->mPersist;
+	}
+
 	const LLUUID& id() const
 	{
 		return mId;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index bfb5798909b4843b93038f4864409ef5cedd3051..99ba356d9e012444690644d47c5bbc2854a293ed 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -296,6 +296,7 @@ set(viewer_SOURCE_FILES
     llnotificationmanager.cpp
     llnotificationofferhandler.cpp
     llnotificationscripthandler.cpp
+    llnotificationstorage.cpp
     llnotificationtiphandler.cpp
     lloutfitslist.cpp
     lloutputmonitorctrl.cpp
@@ -802,6 +803,7 @@ set(viewer_HEADER_FILES
     llnetmap.h
     llnotificationhandler.h
     llnotificationmanager.h
+    llnotificationstorage.h
     lloutfitslist.h
     lloutputmonitorctrl.h
     llpanelavatar.h
@@ -1400,7 +1402,7 @@ if (WINDOWS)
 
     # If adding a file to viewer_manifest.py in the WindowsManifest.construct() method, be sure to add the dependency
     # here.
-    # *NOTE:Mani - This is a crappy hack to have important dependecies for the viewer_manifest copy action
+    # *NOTE:Mani - This is a crappy hack to have important dependencies for the viewer_manifest copy action
     # be met. I'm looking forward to a source-code split-up project next year that will address this kind of thing.
     # In the meantime, if you have any ideas on how to easily maintain one list, either here or in viewer_manifest.py
     # and have the build deps get tracked *please* tell me about it.
@@ -1421,7 +1423,7 @@ if (WINDOWS)
     endif(USE_GOOGLE_PERFTOOLS)
   
 
-    set(COPY_INPUT_DEPENDECIES
+    set(COPY_INPUT_DEPENDENCIES
       # The following commented dependencies are determined at variably at build time. Can't do this here.
       #${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libtcmalloc_minimal.dll => None ... Skipping libtcmalloc_minimal.dll
       ${CMAKE_SOURCE_DIR}/../etc/message.xml
@@ -1517,7 +1519,7 @@ if (WINDOWS)
       DEPENDS 
         ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
         stage_third_party_libs
-        ${COPY_INPUT_DEPENDECIES}
+        ${COPY_INPUT_DEPENDENCIES}
       COMMENT "Performing viewer_manifest copy"
       )
     
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 466f2d499dd34646d095617adcbbfe47fcd036da..8a880e5acee19a09cb39485669fc4dd3a216af8c 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -2031,6 +2031,39 @@ void LLAgentWearables::animateAllWearableParams(F32 delta, BOOL upload_bake)
 	}
 }
 
+bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool closer_to_body)
+{
+	if (!item) return false;
+	if (!item->isWearableType()) return false;
+
+	wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(item->getWearableType());
+	if (wearable_iter == mWearableDatas.end()) return false;
+
+	wearableentry_vec_t& wearable_vec = wearable_iter->second;
+	if (wearable_vec.empty()) return false;
+
+	const LLUUID& asset_id = item->getAssetUUID();
+
+	//nowhere to move if the wearable is already on any boundary (closest to the body/furthest from the body)
+	if (closer_to_body && asset_id == wearable_vec.front()->getAssetID()) return false;
+	if (!closer_to_body && asset_id == wearable_vec.back()->getAssetID()) return false;
+
+	for (U32 i = 0; i < wearable_vec.size(); ++i)
+	{
+		LLWearable* wearable = wearable_vec[i];
+		if (!wearable) continue;
+		if (wearable->getAssetID() != asset_id) continue;
+		
+		//swapping wearables
+		U32 swap_i = closer_to_body ? i-1 : i+1;
+		wearable_vec[i] = wearable_vec[swap_i];
+		wearable_vec[swap_i] = wearable;
+		return true;
+	}
+
+	return false;
+}
+
 void LLAgentWearables::updateServer()
 {
 	sendAgentWearablesUpdate();
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 585fd3f8b3acb063a4095f885240ef72d94e4ff0..d3b18f68f1b0eaf278935fbf6d37fe684a2059e0 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -82,6 +82,8 @@ class LLAgentWearables
 
 	void			animateAllWearableParams(F32 delta, BOOL upload_bake);
 	
+	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
+
 	//--------------------------------------------------------------------
 	// Accessors
 	//--------------------------------------------------------------------
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 9e02886e4f44fa40470d11c27bd0051be5406331..a6a3aa28d634e65ac1a9a716dcbf96d259861034 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -122,6 +122,38 @@ class LLWearInventoryCategoryCallback : public LLInventoryCallback
 	bool mAppend;
 };
 
+
+//Inventory callback updating "dirty" state when destroyed
+class LLUpdateDirtyState: public LLInventoryCallback
+{
+public:
+	LLUpdateDirtyState() {}
+	virtual ~LLUpdateDirtyState(){ LLAppearanceMgr::getInstance()->updateIsDirty(); }
+	virtual void fire(const LLUUID&) {}
+};
+
+
+//Inventory collect functor collecting wearables of a specific wearable type
+class LLFindClothesOfType : public LLInventoryCollectFunctor
+{
+public:
+	LLFindClothesOfType(EWearableType type) : mWearableType(type) {}
+	virtual ~LLFindClothesOfType() {}
+	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+	{
+		if (!item) return false;
+		if (item->getType() != LLAssetType::AT_CLOTHING) return false;
+		
+		LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item);
+		if (!vitem || vitem->getWearableType() != mWearableType) return false;
+
+		return true;
+	}
+
+	const EWearableType mWearableType;
+};
+
+
 LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy():
 	mFireCount(0)
 {
@@ -1593,8 +1625,11 @@ bool LLAppearanceMgr::updateBaseOutfit()
 	// in a Base Outfit we do not remove items, only links
 	purgeCategory(base_outfit_id, false);
 
+
+	LLPointer<LLInventoryCallback> dirty_state_updater = new LLUpdateDirtyState();
+
 	//COF contains only links so we copy to the Base Outfit only links
-	shallowCopyCategoryContents(getCOF(), base_outfit_id, NULL);
+	shallowCopyCategoryContents(getCOF(), base_outfit_id, dirty_state_updater);
 
 	return true;
 }
@@ -1802,6 +1837,62 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
 	}
 }
 
+
+bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body)
+{
+	if (!item || !item->isWearableType()) return false;
+	if (item->getType() != LLAssetType::AT_CLOTHING) return false;
+	if (!gInventory.isObjectDescendentOf(item->getUUID(), getCOF())) return false;
+
+	LLInventoryModel::cat_array_t cats;
+	LLInventoryModel::item_array_t items;
+	gInventory.collectDescendentsIf(getCOF(), cats, items, true, LLFindClothesOfType(item->getWearableType()));
+	if (items.empty()) return false;
+
+	//*TODO all items are not guarantied to have valid descriptions (check?)
+	std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType()));
+
+	if (closer_to_body && items.front() == item) return false;
+	if (!closer_to_body && items.back() == item) return false;
+	
+	LLInventoryModel::item_array_t::iterator it = std::find(items.begin(), items.end(), item);
+	if (items.end() == it) return false;
+
+
+	//swapping descriptions
+	closer_to_body ? --it : ++it;
+	LLViewerInventoryItem* swap_item = *it;
+	if (!swap_item) return false;
+	std::string tmp = swap_item->LLInventoryItem::getDescription();
+	swap_item->setDescription(item->LLInventoryItem::getDescription());
+	item->setDescription(tmp);
+
+
+	//items need to be updated on a dataserver
+	item->setComplete(TRUE);
+	item->updateServer(FALSE);
+	gInventory.updateItem(item);
+
+	swap_item->setComplete(TRUE);
+	swap_item->updateServer(FALSE);
+	gInventory.updateItem(swap_item);
+
+	//to cause appearance of the agent to be updated
+	bool result = false;
+	if (result = gAgentWearables.moveWearable(item, closer_to_body))
+	{
+		gAgentAvatarp->wearableUpdated(item->getWearableType(), TRUE);
+	}
+
+	setOutfitDirty(true);
+
+	//*TODO do we need to notify observers here in such a way?
+	gInventory.notifyObservers();
+
+	return result;
+}
+
+
 //#define DUMP_CAT_VERBOSE
 
 void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg)
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index efb5274c5b6e9b512cc02879e099525396a3e1d0..a308a3efa947d343fb6a76a97196a3653834fdf6 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -141,6 +141,8 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 
 	LLUUID makeNewOutfitLinks(const std::string& new_folder_name);
 
+	bool moveWearable(LLViewerInventoryItem* item, bool closer_to_body);
+
 protected:
 	LLAppearanceMgr();
 	~LLAppearanceMgr();
diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp
index 769387c26cb976682180391350eaad304759c969..4f9434030f0a7588b8ab99c64ac990dcd8359173 100644
--- a/indra/newview/llchannelmanager.cpp
+++ b/indra/newview/llchannelmanager.cpp
@@ -35,6 +35,7 @@
 #include "llchannelmanager.h"
 
 #include "llappviewer.h"
+#include "llnotificationstorage.h"
 #include "llviewercontrol.h"
 #include "llviewerwindow.h"
 #include "llrootview.h"
@@ -107,31 +108,35 @@ void LLChannelManager::onLoginCompleted()
 	if(!away_notifications)
 	{
 		onStartUpToastClose();
-		return;
 	}
-	
-	// create a channel for the StartUp Toast
-	LLChannelManager::Params p;
-	p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID"));
-	p.channel_align = CA_RIGHT;
-	mStartUpChannel = createChannel(p);
-
-	if(!mStartUpChannel)
+	else
 	{
-		onStartUpToastClose();
-		return;
-	}
+		// create a channel for the StartUp Toast
+		LLChannelManager::Params p;
+		p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID"));
+		p.channel_align = CA_RIGHT;
+		mStartUpChannel = createChannel(p);
 
-	gViewerWindow->getRootView()->addChild(mStartUpChannel);
+		if(!mStartUpChannel)
+		{
+			onStartUpToastClose();
+		}
+		else
+		{
+			gViewerWindow->getRootView()->addChild(mStartUpChannel);
 
-	// init channel's position and size
-	S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); 
-	S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth");
-	mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound);
-	mStartUpChannel->setMouseDownCallback(boost::bind(&LLNotificationWellWindow::onStartUpToastClick, LLNotificationWellWindow::getInstance(), _2, _3, _4));
+			// init channel's position and size
+			S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); 
+			S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth");
+			mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound);
+			mStartUpChannel->setMouseDownCallback(boost::bind(&LLNotificationWellWindow::onStartUpToastClick, LLNotificationWellWindow::getInstance(), _2, _3, _4));
+
+			mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this));
+			mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime"));
+		}
+	}
 
-	mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this));
-	mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime"));
+	LLPersistentNotificationStorage::getInstance()->loadNotifications();
 }
 
 //--------------------------------------------------------------------------
diff --git a/indra/newview/llnotificationstorage.cpp b/indra/newview/llnotificationstorage.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..316ff4324c297a6c5a9f8d8fdcdf25cf5f3973db
--- /dev/null
+++ b/indra/newview/llnotificationstorage.cpp
@@ -0,0 +1,228 @@
+/**
+* @file llnotificationstorage.cpp
+* @brief LLPersistentNotificationStorage class implementation
+*
+* $LicenseInfo:firstyear=2010&license=viewergpl$
+*
+* Copyright (c) 2010, Linden Research, Inc.
+*
+* Second Life Viewer Source Code
+* The source code in this file ("Source Code") is provided by Linden Lab
+* to you under the terms of the GNU General Public License, version 2.0
+* ("GPL"), unless you have obtained a separate licensing agreement
+* ("Other License"), formally executed by you and Linden Lab.  Terms of
+* the GPL can be found in doc/GPL-license.txt in this distribution, or
+* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+*
+* There are special exceptions to the terms and conditions of the GPL as
+* it is applied to this Source Code. View the full text of the exception
+* in the file doc/FLOSS-exception.txt in this software distribution, or
+* online at
+* http://secondlifegrid.net/programs/open_source/licensing/flossexception
+*
+* By copying, modifying or distributing this software, you acknowledge
+* that you have read and understood your obligations described above,
+* and agree to abide by those obligations.
+*
+* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+* COMPLETENESS OR PERFORMANCE.
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h" // must be first include
+#include "llnotificationstorage.h"
+
+#include "llxmlnode.h" // for linux compilers
+
+#include "llchannelmanager.h"
+#include "llscreenchannel.h"
+#include "llscriptfloater.h"
+#include "llsdserialize.h"
+#include "llviewermessage.h"
+
+//////////////////////////////////////////////////////////////////////////
+
+class LLResponderRegistry
+{
+public:
+
+	static void registerResponders();
+
+	static LLNotificationResponderInterface* createResponder(const std::string& notification_name, const LLSD& params);
+
+private:
+
+	template<typename RESPONDER_TYPE>
+	static LLNotificationResponderInterface* create(const LLSD& params)
+	{
+		RESPONDER_TYPE* responder = new RESPONDER_TYPE();
+		responder->fromLLSD(params);
+		return responder;
+	}
+
+	typedef boost::function<LLNotificationResponderInterface* (const LLSD& params)> responder_constructor_t;
+
+	static void add(const std::string& notification_name, const responder_constructor_t& ctr);
+
+private:
+
+	typedef std::map<std::string, responder_constructor_t> build_map_t;
+
+	static build_map_t sBuildMap;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+LLPersistentNotificationStorage::LLPersistentNotificationStorage()
+{
+	mFileName = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
+}
+
+bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload)
+{
+	// we ignore "load" messages, but rewrite the persistence file on any other
+	const std::string sigtype = payload["sigtype"].asString();
+	if ("load" != sigtype)
+	{
+		saveNotifications();
+	}
+	return false;
+}
+
+void LLPersistentNotificationStorage::saveNotifications()
+{
+	// TODO - think about save optimization.
+
+	llofstream notify_file(mFileName.c_str());
+	if (!notify_file.is_open())
+	{
+		llwarns << "Failed to open " << mFileName << llendl;
+		return;
+	}
+
+	LLSD output;
+	LLSD& data = output["data"];
+
+	LLNotificationChannelPtr history_channel = LLNotifications::instance().getChannel("Persistent");
+	LLNotificationSet::iterator it = history_channel->begin();
+
+	for ( ; history_channel->end() != it; ++it)
+	{
+		LLNotificationPtr notification = *it;
+
+		// After a notification was placed in Persist channel, it can become
+		// responded, expired - in this case we are should not save it
+		if(notification->isRespondedTo()
+			|| notification->isExpired())
+		{
+			continue;
+		}
+
+		data.append(notification->asLLSD());
+	}
+
+	LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
+	formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
+}
+
+void LLPersistentNotificationStorage::loadNotifications()
+{
+	LLResponderRegistry::registerResponders();
+
+	LLNotifications::instance().getChannel("Persistent")->
+		connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1));
+
+	llifstream notify_file(mFileName.c_str());
+	if (!notify_file.is_open())
+	{
+		llwarns << "Failed to open " << mFileName << llendl;
+		return;
+	}
+
+	LLSD input;
+	LLPointer<LLSDParser> parser = new LLSDXMLParser();
+	if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
+	{
+		llwarns << "Failed to parse open notifications" << llendl;
+		return;
+	}
+
+	if (input.isUndefined())
+	{
+		return;
+	}
+
+	LLSD& data = input["data"];
+	if (data.isUndefined())
+	{
+		return;
+	}
+
+	using namespace LLNotificationsUI;
+	LLScreenChannel* notification_channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->
+		findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
+
+	LLNotifications& instance = LLNotifications::instance();
+
+	for (LLSD::array_const_iterator notification_it = data.beginArray();
+		notification_it != data.endArray();
+		++notification_it)
+	{
+		LLSD notification_params = *notification_it;
+		LLNotificationPtr notification(new LLNotification(notification_params));
+
+		LLNotificationResponderPtr responder(LLResponderRegistry::
+			createResponder(notification_params["name"], notification_params["responder"]));
+		notification->setResponseFunctor(responder);
+
+		instance.add(notification);
+
+		// hide script floaters so they don't confuse the user and don't overlap startup toast
+		LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false);
+
+		if(notification_channel)
+		{
+			// hide saved toasts so they don't confuse the user
+			notification_channel->hideToast(notification->getID());
+		}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+LLResponderRegistry::build_map_t LLResponderRegistry::sBuildMap;
+
+void LLResponderRegistry::registerResponders()
+{
+	sBuildMap.clear();
+
+	add("ObjectGiveItem", &create<LLOfferInfo>);
+	add("UserGiveItem", &create<LLOfferInfo>);
+}
+
+LLNotificationResponderInterface* LLResponderRegistry::createResponder(const std::string& notification_name, const LLSD& params)
+{
+	build_map_t::const_iterator it = sBuildMap.find(notification_name);
+	if(sBuildMap.end() == it)
+	{
+		llwarns << "Responder for notification \'" << notification_name << "\' is not registered" << llendl;
+		return NULL;
+	}
+	responder_constructor_t ctr = it->second;
+	return ctr(params);
+}
+
+void LLResponderRegistry::add(const std::string& notification_name, const responder_constructor_t& ctr)
+{
+	if(sBuildMap.find(notification_name) != sBuildMap.end())
+	{
+		llwarns << "Responder is already registered : " << notification_name << llendl;
+		llassert(!"Responder already registered");
+	}
+	sBuildMap[notification_name] = ctr;
+}
+
+// EOF
diff --git a/indra/newview/llnotificationstorage.h b/indra/newview/llnotificationstorage.h
new file mode 100644
index 0000000000000000000000000000000000000000..5050781a85f4cbe0537a7b8937b8388cf917eb02
--- /dev/null
+++ b/indra/newview/llnotificationstorage.h
@@ -0,0 +1,65 @@
+/**
+* @file llnotificationstorage.h
+* @brief LLNotificationStorage class declaration
+*
+* $LicenseInfo:firstyear=2010&license=viewergpl$
+*
+* Copyright (c) 2010, Linden Research, Inc.
+*
+* Second Life Viewer Source Code
+* The source code in this file ("Source Code") is provided by Linden Lab
+* to you under the terms of the GNU General Public License, version 2.0
+* ("GPL"), unless you have obtained a separate licensing agreement
+* ("Other License"), formally executed by you and Linden Lab.  Terms of
+* the GPL can be found in doc/GPL-license.txt in this distribution, or
+* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+*
+* There are special exceptions to the terms and conditions of the GPL as
+* it is applied to this Source Code. View the full text of the exception
+* in the file doc/FLOSS-exception.txt in this software distribution, or
+* online at
+* http://secondlifegrid.net/programs/open_source/licensing/flossexception
+*
+* By copying, modifying or distributing this software, you acknowledge
+* that you have read and understood your obligations described above,
+* and agree to abide by those obligations.
+*
+* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+* COMPLETENESS OR PERFORMANCE.
+* $/LicenseInfo$
+*/
+
+#ifndef LL_NOTIFICATIONSTORAGE_H
+#define LL_NOTIFICATIONSTORAGE_H
+
+#include "llnotifications.h"
+
+// Class that saves not responded(unread) notifications.
+// Unread notifications are saved in open_notifications.xml in SL account folder
+//
+// Notifications that should be saved(if unread) are marked with persist="true" in notifications.xml
+// Notifications using functor responders are saved automatically (see llviewermessage.cpp
+// lure_callback_reg for example).
+// Notifications using object responders(LLOfferInfo) need additional tuning. Responder object should
+// be a) serializable(implement LLNotificationResponderInterface),
+// b) registered with LLResponderRegistry (found in llnotificationstorage.cpp).
+class LLPersistentNotificationStorage : public LLSingleton<LLPersistentNotificationStorage>
+{
+	LOG_CLASS(LLPersistentNotificationStorage);
+public:
+
+	LLPersistentNotificationStorage();
+
+	void saveNotifications();
+
+	void loadNotifications();
+
+private:
+
+	bool onPersistentChannelChanged(const LLSD& payload);
+
+	std::string mFileName;
+};
+
+#endif // LL_NOTIFICATIONSTORAGE_H
diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h
index 136868a60d26e98230de5eb134f7b7011a1f08cd..359f25238397601ddaa56e410bfc4090d8b16a23 100644
--- a/indra/newview/llpanelgroup.h
+++ b/indra/newview/llpanelgroup.h
@@ -37,7 +37,7 @@
 #include "lltimer.h"
 #include "llvoiceclient.h"
 
-struct LLOfferInfo;
+class LLOfferInfo;
 
 const S32 UPDATE_MEMBERS_PER_FRAME = 500;
 
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index e139cb31d65584a35e8646950332ffff38c7c1fb..ae181e2819b2ddbf6be687db639dfa388e14147f 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -73,6 +73,9 @@ const U64 WEARABLE_MASK = (1LL << LLInventoryType::IT_WEARABLE);
 const U64 ATTACHMENT_MASK = (1LL << LLInventoryType::IT_ATTACHMENT) | (1LL << LLInventoryType::IT_OBJECT);
 const U64 ALL_ITEMS_MASK = WEARABLE_MASK | ATTACHMENT_MASK;
 
+static const std::string SAVE_BTN("save_btn");
+static const std::string REVERT_BTN("revert_btn");
+
 class LLInventoryLookObserver : public LLInventoryObserver
 {
 public:
@@ -221,10 +224,9 @@ BOOL LLPanelOutfitEdit::postBuild()
 	mEditWearableBtn->setVisible(FALSE);
 	mEditWearableBtn->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this));
 
-	childSetAction("revert_btn", boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance()));
+	childSetAction(REVERT_BTN, boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance()));
 
-	childSetAction("save_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false));
-	childSetAction("save_as_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true));
+	childSetAction(SAVE_BTN, boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false));
 	childSetAction("save_flyout_btn", boost::bind(&LLPanelOutfitEdit::showSaveMenu, this));
 
 	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar save_registar;
@@ -234,10 +236,22 @@ BOOL LLPanelOutfitEdit::postBuild()
 
 	mWearableListManager = new LLFilteredWearableListManager(
 		getChild<LLInventoryItemsList>("filtered_wearables_list"), ALL_ITEMS_MASK);
+		
+	childSetAction("move_closer_btn", boost::bind(&LLPanelOutfitEdit::moveWearable, this, true));
+	childSetAction("move_further_btn", boost::bind(&LLPanelOutfitEdit::moveWearable, this, false));
 
 	return TRUE;
 }
 
+void LLPanelOutfitEdit::moveWearable(bool closer_to_body)
+{
+	LLViewerInventoryItem* wearable_to_move = gInventory.getItem(mLookContents->getSelectionInterface()->getCurrentID());
+	LLAppearanceMgr::getInstance()->moveWearable(wearable_to_move, closer_to_body);
+
+	//*TODO why not to listen to inventory?
+	updateLookInfo();
+}
+
 void LLPanelOutfitEdit::showAddWearablesPanel()
 {
 	childSetVisible("add_wearables_panel", childGetValue("add_btn"));
@@ -267,6 +281,8 @@ void LLPanelOutfitEdit::saveOutfit(bool as_new)
 	{
 		panel_outfits_inventory->onSave();
 	}
+
+	//*TODO how to get to know when base outfit is updated or new outfit is created?
 }
 
 void LLPanelOutfitEdit::showSaveMenu()
@@ -542,10 +558,12 @@ void LLPanelOutfitEdit::lookFetched(void)
 		columns[0]["value"] = item->getName();
 		columns[1]["column"] = "look_item_sort";
 		columns[1]["type"] = "text"; // TODO: multi-wearable sort "type" should go here.
-		columns[1]["value"] = "BAR"; // TODO: Multi-wearable sort index should go here
+		columns[1]["value"] = item->LLInventoryItem::getDescription();
 		
 		mLookContents->addElement(row);
 	}
+
+	updateVerbs();
 }
 
 void LLPanelOutfitEdit::updateLookInfo()
@@ -589,4 +607,15 @@ void LLPanelOutfitEdit::displayCurrentOutfit()
 	updateLookInfo();
 }
 
+//private
+void LLPanelOutfitEdit::updateVerbs()
+{
+	bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty();
+	
+	childSetEnabled(SAVE_BTN, outfit_is_dirty);
+	childSetEnabled(REVERT_BTN, outfit_is_dirty);
+
+	mSaveMenu->setItemEnabled("save_outfit", outfit_is_dirty);
+}
+
 // EOF
diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h
index 308ee23115c4f5b7aec74e63623fb736233a8bd2..b6f121d4846874d5c8cf280ac3fe3c71b03e3955 100644
--- a/indra/newview/llpaneloutfitedit.h
+++ b/indra/newview/llpaneloutfitedit.h
@@ -87,6 +87,8 @@ class LLPanelOutfitEdit : public LLPanel
 		// Sends a request for data about the given parcel, which will
 		// only update the location if there is none already available.
 
+	void moveWearable(bool closer_to_body);
+
 	void showAddWearablesPanel();
 	void showWearablesFilter();
 	void showFilteredWearablesPanel();
@@ -110,6 +112,8 @@ class LLPanelOutfitEdit : public LLPanel
 
 private:
 
+	void updateVerbs();
+
 	//*TODO got rid of mCurrentOutfitID
 	LLUUID				mCurrentOutfitID;
 
diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp
index f35cb3516a81296fa4f5b09261625f802dea3503..11b6c0a3aeb702601520de973fc931fd7cbbd481 100644
--- a/indra/newview/llscriptfloater.cpp
+++ b/indra/newview/llscriptfloater.cpp
@@ -539,4 +539,14 @@ bool LLScriptFloaterManager::getFloaterPosition(const LLUUID& object_id, Floater
 	return false;
 }
 
+void LLScriptFloaterManager::setFloaterVisible(const LLUUID& notification_id, bool visible)
+{
+	LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>(
+		"script_floater", notification_id);
+	if(floater)
+	{
+		floater->setVisible(visible);
+	}
+}
+
 // EOF
diff --git a/indra/newview/llscriptfloater.h b/indra/newview/llscriptfloater.h
index ec3ec4b540db0f699b6218ac986173ac653813a2..dc0cfc2400dd8f560d6fabf8b4523a9374e98b5e 100644
--- a/indra/newview/llscriptfloater.h
+++ b/indra/newview/llscriptfloater.h
@@ -99,6 +99,8 @@ class LLScriptFloaterManager : public LLSingleton<LLScriptFloaterManager>
 
 	bool getFloaterPosition(const LLUUID& object_id, FloaterPositionInfo& fpi);
 
+	void setFloaterVisible(const LLUUID& notification_id, bool visible);
+
 protected:
 
 	typedef std::map<std::string, EObjectType> object_type_map;
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 2ea6e5936dd2f3c43db1a5647e9081406a7990eb..e64696b12081d07dc2063eabb0edf8ea74e35ab9 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -850,6 +850,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				mState = WAIT_HTTP_REQ;	
 
 				mFetcher->addToHTTPQueue(mID);
+				mSentRequest = QUEUED;
 				// Will call callbackHttpGet when curl request completes
 				std::vector<std::string> headers;
 				headers.push_back("Accept: image/x-j2c");
@@ -936,6 +937,11 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			{
 				mFileSize = mBufferSize;
 			}
+			else //the file size is unknown
+			{
+				mFileSize = S32_MAX ; //flag the file is not fully loaded.
+			}
+
 			U8* buffer = new U8[mBufferSize];
 			if (cur_size > 0)
 			{
diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp
index c9d2d404c0504edc4a9dedb650c89c79b7182970..089163929e5e57fa1b085e3633fb803fc1f63fe4 100644
--- a/indra/newview/lltoastnotifypanel.cpp
+++ b/indra/newview/lltoastnotifypanel.cpp
@@ -493,7 +493,7 @@ void LLToastNotifyPanel::onClickButton(void* data)
 	{
 		sButtonClickSignal(self->mNotification->getID(), button_name);
 
-		if(new_info)
+		if(new_info && !self->mNotification->isPersistent())
 		{
 			self->mNotification->setResponseFunctor(
 				boost::bind(&LLOfferInfo::inventory_offer_callback, new_info, _1, _2));
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index ec5eb658f69d055b6b2e61798a83356786301785..ba0c8d464db147811e1654da634a7bff751ccc18 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -1252,6 +1252,16 @@ void inventory_offer_mute_callback(const LLUUID& blocked_id,
 			gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id));
 }
 
+LLOfferInfo::LLOfferInfo()
+ : LLNotificationResponderInterface()
+ , mFromGroup(FALSE)
+ , mFromObject(FALSE)
+ , mIM(IM_NOTHING_SPECIAL)
+ , mType(LLAssetType::AT_NONE)
+ , mPersist(false)
+{
+}
+
 LLOfferInfo::LLOfferInfo(const LLSD& sd)
 {
 	mIM = (EInstantMessage)sd["im_type"].asInteger();
@@ -1265,6 +1275,7 @@ LLOfferInfo::LLOfferInfo(const LLSD& sd)
 	mFromName = sd["from_name"].asString();
 	mDesc = sd["description"].asString();
 	mHost = LLHost(sd["sender"].asString());
+	mPersist = sd["persist"].asBoolean();
 }
 
 LLOfferInfo::LLOfferInfo(const LLOfferInfo& info)
@@ -1280,6 +1291,7 @@ LLOfferInfo::LLOfferInfo(const LLOfferInfo& info)
 	mFromName = info.mFromName;
 	mDesc = info.mDesc;
 	mHost = info.mHost;
+	mPersist = info.mPersist;
 }
 
 LLSD LLOfferInfo::asLLSD()
@@ -1296,9 +1308,15 @@ LLSD LLOfferInfo::asLLSD()
 	sd["from_name"] = mFromName;
 	sd["description"] = mDesc;
 	sd["sender"] = mHost.getIPandPort();
+	sd["persist"] = mPersist;
 	return sd;
 }
 
+void LLOfferInfo::fromLLSD(const LLSD& params)
+{
+	*this = params;
+}
+
 void LLOfferInfo::send_auto_receive_response(void)
 {	
 	LLMessageSystem* msg = gMessageSystem;
@@ -1338,6 +1356,21 @@ void LLOfferInfo::send_auto_receive_response(void)
 	}
 }
 
+void LLOfferInfo::handleRespond(const LLSD& notification, const LLSD& response)
+{
+	initRespondFunctionMap();
+
+	const std::string name = notification["name"].asString();
+	if(mRespondFunctions.find(name) == mRespondFunctions.end())
+	{
+		llwarns << "Unexpected notification name : " << name << llendl;
+		llassert(!"Unexpected notification name");
+		return;
+	}
+
+	mRespondFunctions[name](notification, response);
+}
+
 bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response)
 {
 	LLChat chat;
@@ -1474,7 +1507,10 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
 		gInventory.addObserver(opener);
 	}
 
-	delete this;
+	if(!mPersist)
+	{
+		delete this;
+	}
 	return false;
 }
 
@@ -1640,7 +1676,10 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const
 		gInventory.addObserver(opener);
 	}
 
-	delete this;
+	if(!mPersist)
+	{
+		delete this;
+	}
 	return false;
 }
 
@@ -1656,6 +1695,15 @@ class LLPostponedOfferNotification: public LLPostponedNotification
 	}
 };
 
+void LLOfferInfo::initRespondFunctionMap()
+{
+	if(mRespondFunctions.empty())
+	{
+		mRespondFunctions["ObjectGiveItem"] = boost::bind(&LLOfferInfo::inventory_task_offer_callback, this, _1, _2);
+		mRespondFunctions["UserGiveItem"] = boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2);
+	}
+}
+
 void inventory_offer_handler(LLOfferInfo* info)
 {
 	//Until throttling is implmented, busy mode should reject inventory instead of silently
@@ -1772,7 +1820,8 @@ void inventory_offer_handler(LLOfferInfo* info)
 		// Inventory Slurls don't currently work for non agent transfers, so only display the object name.
 		args["ITEM_SLURL"] = msg;
 		// Note: sets inventory_task_offer_callback as the callback
-		p.substitutions(args).payload(payload).functor.function(boost::bind(&LLOfferInfo::inventory_task_offer_callback, info, _1, _2));
+		p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info));
+		info->mPersist = true;
 		p.name = name_found ? "ObjectGiveItem" : "ObjectGiveItemUnknownUser";
 		// Pop up inv offer chiclet and let the user accept (keep), or reject (and silently delete) the inventory.
 		LLNotifications::instance().add(p);
@@ -1784,7 +1833,8 @@ void inventory_offer_handler(LLOfferInfo* info)
 		// *TODO fix memory leak
 		// inventory_offer_callback() is not invoked if user received notification and 
 		// closes viewer(without responding the notification)
-		p.substitutions(args).payload(payload).functor.function(boost::bind(&LLOfferInfo::inventory_offer_callback, info, _1, _2));
+		p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info));
+		info->mPersist = true;
 		p.name = "UserGiveItem";
 		
 		// Prefetch the item into your local inventory.
diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h
index 7c021dc05f246d834bdc6287695ab70eb0efa8da..72ad3c8926085d878fe11afdb7addc2deeb04900 100644
--- a/indra/newview/llviewermessage.h
+++ b/indra/newview/llviewermessage.h
@@ -40,6 +40,7 @@
 #include "lluuid.h"
 #include "message.h"
 #include "stdenums.h"
+#include "llnotifications.h"
 
 //
 // Forward declarations
@@ -210,11 +211,10 @@ bool highlight_offered_item(const LLUUID& item_id);
 
 void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid);
 
-struct LLOfferInfo
+class LLOfferInfo : public LLNotificationResponderInterface
 {
-        LLOfferInfo()
-	:	mFromGroup(FALSE), mFromObject(FALSE),
-		mIM(IM_NOTHING_SPECIAL), mType(LLAssetType::AT_NONE) {};
+public:
+	LLOfferInfo();
 	LLOfferInfo(const LLSD& sd);
 
 	LLOfferInfo(const LLOfferInfo& info);
@@ -232,12 +232,27 @@ struct LLOfferInfo
 	std::string mFromName;
 	std::string mDesc;
 	LLHost mHost;
+	bool mPersist;
+
+	// LLNotificationResponderInterface implementation
+	/*virtual*/ LLSD asLLSD();
+	/*virtual*/ void fromLLSD(const LLSD& params);
+	/*virtual*/ void handleRespond(const LLSD& notification, const LLSD& response);
 
-	LLSD asLLSD();
 	void send_auto_receive_response(void);
+
+	// TODO - replace all references with handleRespond()
 	bool inventory_offer_callback(const LLSD& notification, const LLSD& response);
 	bool inventory_task_offer_callback(const LLSD& notification, const LLSD& response);
 
+private:
+
+	void initRespondFunctionMap();
+
+	typedef boost::function<bool (const LLSD&, const LLSD&)> respond_function_t;
+	typedef std::map<std::string, respond_function_t> respond_function_map_t;
+
+	respond_function_map_t mRespondFunctions;
 };
 
 void process_feature_disabled_message(LLMessageSystem* msg, void**);
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 07304eb14e2672a79f97861176c9ca9c5acd71f6..5d184fea3a7e339c39518a8d7b7708f336bcd423 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -5066,6 +5066,7 @@ No valid parcel could be found.
   <notification
    icon="notify.tga"
    name="ObjectGiveItem"
+   persist="true"
    type="offer">
 An object named [OBJECTFROMNAME] owned by [NAME_SLURL] has given you this [OBJECTTYPE]:
 [ITEM_SLURL]
@@ -5110,6 +5111,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th
   <notification
    icon="notify.tga"
    name="UserGiveItem"
+   persist="true"
    type="offer">
 [NAME_SLURL] has given you this [OBJECTTYPE]:
 [ITEM_SLURL]
@@ -5162,6 +5164,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th
   <notification
    icon="notify.tga"
    name="TeleportOffered"
+   persist="true"
    type="offer">
 [NAME_SLURL] has offered to teleport you to their location:
 
@@ -5207,6 +5210,7 @@ An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you th
   <notification
    icon="notify.tga"
    name="OfferFriendship"
+   persist="true"
    type="offer">
 [NAME_SLURL] is offering friendship.
 
diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
index b1f0ff15cb071bfd3c84230a605d33df9bdd132e..314d2389ae0fe3f35bba89f0708d12773414078a 100644
--- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
@@ -221,6 +221,30 @@
                  name="new_btn"
                  top="1"
                  width="31" />
+                <button
+                 follows="bottom|left"
+                 height="25"
+                 image_hover_unselected="Toolbar_Middle_Over"
+                 image_overlay="Movement_Forward_On"
+                 image_selected="Toolbar_Middle_Selected"
+                 image_unselected="Toolbar_Middle_Off"
+                 layout="topleft"
+                 left_pad="1"
+                 name="move_closer_btn"
+                 top="1"
+                 width="31" />
+                <button
+                 follows="bottom|left"
+                 height="25"
+                 image_hover_unselected="Toolbar_Middle_Over"
+                 image_overlay="Movement_Backward_On"
+                 image_selected="Toolbar_Middle_Selected"
+                 image_unselected="Toolbar_Middle_Off"
+                 layout="topleft"
+                 left_pad="1"
+                 name="move_further_btn"
+                 top="1"
+                 width="31" />
                 <button
                  follows="bottom|right"
                  height="25"