diff --git a/indra/llcommon/llinitparam.cpp b/indra/llcommon/llinitparam.cpp
index bb160b3c0bf162de768c1ac52f461f0d4cae37cc..451b638a3f4f03600fc748c37f2b5bc60a57fdb6 100644
--- a/indra/llcommon/llinitparam.cpp
+++ b/indra/llcommon/llinitparam.cpp
@@ -181,7 +181,8 @@ namespace LLInitParam
 
 	bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent)
 	{
-		if (!deserializeBlock(p, std::make_pair(name_stack.begin(), name_stack.end()), true))
+		Parser::name_stack_range_t range = std::make_pair(name_stack.begin(), name_stack.end());
+		if (!deserializeBlock(p, range, true))
 		{
 			if (!silent)
 			{
@@ -321,7 +322,7 @@ namespace LLInitParam
 		return true;
 	}
 
-	bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool ignored)
+	bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool ignored)
 	{
 		BlockDescriptor& block_data = mostDerivedBlockDescriptor();
 		bool names_left = name_stack_range.first != name_stack_range.second;
diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h
index 545f53ffe6876825e75df3ccc0e88e48c5b5aa45..2f767c234e7f777e7db733e0b6322774340f76ef 100644
--- a/indra/llcommon/llinitparam.h
+++ b/indra/llcommon/llinitparam.h
@@ -572,7 +572,7 @@ namespace LLInitParam
 		};
 
 		typedef bool(*merge_func_t)(Param&, const Param&, bool);
-		typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, bool);
+		typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool);
 		typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param);
 		typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
 		typedef bool(*validation_func_t)(const Param*);
@@ -633,38 +633,38 @@ namespace LLInitParam
 		class BaseBlock*				mCurrentBlockPtr;		// pointer to block currently being constructed
 	};
 
-		//TODO: implement in terms of owned_ptr
-		template<typename T>
+	//TODO: implement in terms of owned_ptr
+	template<typename T>
 	class LazyValue
-		{
+	{
 		public:
 		LazyValue()
-				: mPtr(NULL)
-			{}
+		: mPtr(NULL)
+		{}
 
 		~LazyValue()
-			{
-				delete mPtr;
-			}
+		{
+			delete mPtr;
+		}
 
 		LazyValue(const T& value)
-			{
+		{
 			mPtr = new T(value);
 		}
 
 		LazyValue(const LazyValue& other)
 		:	mPtr(NULL)
-				{
+		{
 			*this = other;
-				}
+		}
 
 		LazyValue& operator = (const LazyValue& other)
 		{
 			if (!other.mPtr)
-				{
+			{
 				delete mPtr;
-					mPtr = NULL;
-				}
+				mPtr = NULL;
+			}
 			else
 			{
 				if (!mPtr)
@@ -675,9 +675,9 @@ namespace LLInitParam
 				{
 					*mPtr = *(other.mPtr);
 				}
-				}
-				return *this;
 			}
+			return *this;
+		}
 
 		bool operator==(const LazyValue& other) const
 		{
@@ -685,13 +685,13 @@ namespace LLInitParam
 			return *mPtr == *other.mPtr;
 		}
 
-			bool empty() const
-			{
-				return mPtr == NULL;
-			}
+		bool empty() const
+		{
+			return mPtr == NULL;
+		}
 
-			void set(const T& other)
-			{
+		void set(const T& other)
+		{
 			if (!mPtr)
 			{
 				mPtr = new T(other);
@@ -702,36 +702,36 @@ namespace LLInitParam
 			}
 		}
 
-			const T& get() const
-			{
+		const T& get() const
+		{
 			return *ensureInstance();
-			}
+		}
 
-			T& get()
-			{
+		T& get()
+		{
 			return *ensureInstance();
 		}
 
 		operator const T&() const
 		{ 
 			return get(); 
-			}
+		}
 
-		private:
-			// lazily allocate an instance of T
-			T* ensureInstance() const
+	private:
+		// lazily allocate an instance of T
+		T* ensureInstance() const
+		{
+			if (mPtr == NULL)
 			{
-				if (mPtr == NULL)
-				{
-					mPtr = new T();
-				}
-				return mPtr;
-			}
+				mPtr = new T();			
+                        }
+			return mPtr;
+		}
 
-		private:
+	private:
 
-			mutable T* mPtr;
-		};
+		mutable T* mPtr;
+	};
 
 	// root class of all parameter blocks
 
@@ -838,7 +838,7 @@ namespace LLInitParam
 		// Blocks can override this to do custom tracking of changes
 		virtual void paramChanged(const Param& changed_param, bool user_provided) {}
 
-		bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
+		bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
 		void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
 		bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const;
 
@@ -913,7 +913,7 @@ namespace LLInitParam
 		}
 
 		U32 getEnclosingBlockOffset() const
-	{
+		{
 			return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow;
 		}
 
@@ -970,7 +970,7 @@ namespace LLInitParam
 
 		bool isProvided() const { return Param::anyProvided(); }
 
-		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
+		static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{ 
 			self_t& typed_param = static_cast<self_t&>(param);
 			// no further names in stack, attempt to parse value now
@@ -1128,7 +1128,7 @@ namespace LLInitParam
 			}
 		}
 
-		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
+		static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{ 
 			self_t& typed_param = static_cast<self_t&>(param);
 			// attempt to parse block...
@@ -1287,7 +1287,7 @@ namespace LLInitParam
 		}
 	};
 
-	// container of non-block parameters
+	// list of non-block parameters
 	template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
 	class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, NOT_BLOCK> 
 	:	public Param
@@ -1316,7 +1316,7 @@ namespace LLInitParam
 
 		bool isProvided() const { return Param::anyProvided(); }
 
-		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
+		static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{ 
 			Parser::name_stack_range_t new_name_stack_range(name_stack_range);
 			self_t& typed_param = static_cast<self_t&>(param);
@@ -1499,7 +1499,7 @@ namespace LLInitParam
 		}
 	};
 
-	// container of block parameters
+	// list of block parameters
 	template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
 	class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> 
 	:	public Param
@@ -1528,30 +1528,35 @@ namespace LLInitParam
 
 		bool isProvided() const { return Param::anyProvided(); }
 
-		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) 
+		static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) 
 		{ 
 			Parser::name_stack_range_t new_name_stack_range(name_stack_range);
 			self_t& typed_param = static_cast<self_t&>(param);
 			bool new_value = false;
+			bool new_array_value = false;
 
 			// pop first element if empty string
 			if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
 			{
-				new_value |= new_name_stack_range.first->second;
+				new_array_value = new_name_stack_range.first->second;
 				++new_name_stack_range.first;
 			}
-			if (new_name || typed_param.mValues.empty())
+
+			if (new_name || new_array_value || typed_param.mValues.empty())
 			{
 				new_value = true;
 				typed_param.mValues.push_back(value_t());
 			}
-
 			param_value_t& value = typed_param.mValues.back();
 
 			// attempt to parse block...
 			if(value.deserializeBlock(parser, new_name_stack_range, new_name))
 			{
 				typed_param.setProvided();
+				if (new_array_value)
+				{
+					name_stack_range.first->second = false;
+				}
 				return true;
 			}
 			else if(named_value_t::valueNamesExist())
@@ -1565,6 +1570,10 @@ namespace LLInitParam
 					{
 						typed_param.mValues.back().setValueName(name);
 						typed_param.setProvided();
+						if (new_array_value)
+						{
+							name_stack_range.first->second = false;
+						}
 						return true;
 					}
 
@@ -2039,7 +2048,7 @@ namespace LLInitParam
 				}
 			}
 			
-			static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
+			static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
 			{
 				if (name_stack_range.first == name_stack_range.second)
 				{
@@ -2062,7 +2071,7 @@ namespace LLInitParam
 			// dummy writer interfaces
 			template<typename T>
 			Deprecated& operator =(const T& val)
-		{
+			{
 				// do nothing
 				return *this;
 			}
@@ -2178,14 +2187,14 @@ namespace LLInitParam
 			return mValue.getValue();
 		}
 
-		bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name)
+		bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{
 			if (new_name)
 			{
 				resetToDefault();
 			}
 			return mValue.deserializeBlock(p, name_stack_range, new_name);
-		}
+			}
 
 		void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const
 		{
@@ -2271,12 +2280,12 @@ namespace LLInitParam
 			return mValue.getValue();
 		}
 
-		bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name)
+		bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{
 			if (new_name)
 			{
 				mCurParam = getBlockDescriptor().mAllParams.begin();
-		}
+			}
 			if (name_stack_range.first == name_stack_range.second 
 				&& mCurParam != getBlockDescriptor().mAllParams.end())
 			{
@@ -2288,7 +2297,7 @@ namespace LLInitParam
 				if (deserialize_func 
 					&& paramp 
 					&& deserialize_func(*paramp, p, name_stack_range, new_name))
-		{
+				{
 					++mCurParam;
 					return true;
 				}
@@ -2300,7 +2309,7 @@ namespace LLInitParam
 			else
 			{
 				return mValue.deserializeBlock(p, name_stack_range, new_name);
-		}
+			}
 		}
 
 		void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const
@@ -2353,7 +2362,7 @@ namespace LLInitParam
 		:	T(),
 			mValidated(false)
 		{}
-	
+
 		ParamValue(const default_value_t& value)
 		:	T(value.getValue()),
 			mValidated(false)
@@ -2401,7 +2410,7 @@ namespace LLInitParam
 			return mValue.get().getValue();
 		}
 
-		bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name)
+		bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{
 			return mValue.get().deserializeBlock(p, name_stack_range, new_name);
 		}
@@ -2425,7 +2434,7 @@ namespace LLInitParam
 		{
 			return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite);
 		}
-			
+
 		bool validateBlock(bool emit_errors = true) const
 		{
 			return mValue.empty() || mValue.get().validateBlock(emit_errors);
@@ -2510,7 +2519,7 @@ namespace LLInitParam
 		LLSD& getValue() { return mValue; }
 
 		// block param interface
-		LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
+		LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
 		LL_COMMON_API void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
 		bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
 		{
@@ -2540,10 +2549,10 @@ namespace LLInitParam
 		} EValueAge;
 
 		typedef ParamValue<T>			derived_t;
-		typedef CustomParamValue<T>				self_t;
-		typedef Block<derived_t>				block_t;
+		typedef CustomParamValue<T>		self_t;
+		typedef Block<derived_t>		block_t;
 		typedef T						default_value_t;
-		typedef T								value_t;
+		typedef T						value_t;
 		typedef void					baseblock_base_class_t;
 
 
@@ -2553,7 +2562,7 @@ namespace LLInitParam
 			mValidated(false)
 		{}
 
-		bool deserializeBlock(Parser& parser, Parser::name_stack_range_t name_stack_range, bool new_name)
+		bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
 		{
 			derived_t& typed_param = static_cast<derived_t&>(*this);
 			// try to parse direct value T
diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp
index 54c8389772ea8e3dec0de945bda424e08b0bc277..9f4460a988c20a25a808ee052f466f36b15ae3cd 100644
--- a/indra/llcommon/llsdparam.cpp
+++ b/indra/llcommon/llsdparam.cpp
@@ -303,7 +303,7 @@ namespace LLInitParam
 {
 	// LLSD specialization
 	// block param interface
-	bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name)
+	bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack, bool new_name)
 	{
 		if (name_stack.first == name_stack.second
 			&& p.readValue<LLSD>(mValue))
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index eb88a6b25103c8d742a777c72cc34d9f6eab45ff..b71f13a45003bc454b3044146f3afeca25d34f5b 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -131,6 +131,9 @@ set(viewer_SOURCE_FILES
     llcommandlineparser.cpp
     llcompilequeue.cpp
     llconfirmationmanager.cpp
+    llconversationlog.cpp
+    llconversationloglist.cpp
+    llconversationloglistitem.cpp
     llcurrencyuimanager.cpp
     llcylinder.cpp
     lldateutil.cpp
@@ -187,6 +190,8 @@ set(viewer_SOURCE_FILES
     llfloaterbuyland.cpp
     llfloatercamera.cpp
     llfloatercolorpicker.cpp
+    llfloaterconversationlog.cpp
+    llfloaterconversationpreview.cpp
     llfloaterdeleteenvpreset.cpp
     llfloaterdestinations.cpp
     llfloaterdisplayname.cpp
@@ -692,6 +697,9 @@ set(viewer_HEADER_FILES
     llcommandlineparser.h
     llcompilequeue.h
     llconfirmationmanager.h
+    llconversationlog.h
+    llconversationloglist.h
+    llconversationloglistitem.h
     llcurrencyuimanager.h
     llcylinder.h
     lldateutil.h
@@ -748,6 +756,8 @@ set(viewer_HEADER_FILES
     llfloaterbuyland.h
     llfloatercamera.h
     llfloatercolorpicker.h
+    llfloaterconversationlog.h
+    llfloaterconversationpreview.h
     llfloaterdeleteenvpreset.h
     llfloaterdestinations.h
     llfloaterdisplayname.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 325cb1f583a206cb3a6f9e8229ec233fb3fab8e6..cbf5edc42332e5b1a6491098c78ddd6583faf0b1 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10052,6 +10052,28 @@
       <key>Value</key>
       <integer>2</integer>
     </map>
+    <key>CallLogSortOrder</key>
+    <map>
+      <key>Comment</key>
+      <string>Specifies sort order for Call Log (0 = by name, 1 = by date)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>U32</string>
+      <key>Value</key>
+      <integer>2</integer>
+    </map>
+    <key>SortFriendsFirst</key>
+    <map>
+      <key>Comment</key>
+      <string>Specifies whether friends will be sorted first in Call Log</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
     <key>ShowPGSearchAll</key>    
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index efa24796e587a06f9b5263ac287610a09d0e50a9..e58bac8e96ea34ee06517738196940bfe7665eaa 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -59,6 +59,7 @@
 #include "llares.h" 
 #include "llcurl.h"
 #include "llcalc.h"
+#include "llconversationlog.h"
 #include "lltexturestats.h"
 #include "lltexturestats.h"
 #include "llviewerwindow.h"
@@ -1824,6 +1825,9 @@ bool LLAppViewer::cleanup()
 	// save mute list. gMuteList used to also be deleted here too.
 	LLMuteList::getInstance()->cache(gAgent.getID());
 
+	//save call log list
+	LLConversationLog::instance().cache();
+
 	if (mPurgeOnExit)
 	{
 		llinfos << "Purging all cache files on exit" << llendflush;
diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..df9350407de6fe3a8649f6b9757a7a4afbd11758
--- /dev/null
+++ b/indra/newview/llconversationlog.cpp
@@ -0,0 +1,336 @@
+/**
+ * @file llconversationlog.h
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llagent.h"
+#include "llconversationlog.h"
+#include "lltrans.h"
+
+struct Conversation_params
+{
+	Conversation_params(time_t time)
+	:	mTime(time),
+		mTimestamp(LLConversation::createTimestamp(time))
+	{}
+
+	time_t		mTime;
+	std::string	mTimestamp;
+	SessionType	mConversationType;
+	std::string	mConversationName;
+	std::string	mHistoryFileName;
+	LLUUID		mSessionID;
+	LLUUID		mParticipantID;
+	bool		mIsVoice;
+	bool		mHasOfflineIMs;
+};
+
+/************************************************************************/
+/*             LLConversation implementation                            */
+/************************************************************************/
+
+LLConversation::LLConversation(const Conversation_params& params)
+:	mTime(params.mTime),
+	mTimestamp(params.mTimestamp),
+	mConversationType(params.mConversationType),
+	mConversationName(params.mConversationName),
+	mHistoryFileName(params.mHistoryFileName),
+	mSessionID(params.mSessionID),
+	mParticipantID(params.mParticipantID),
+	mIsVoice(params.mIsVoice),
+	mHasOfflineIMs(params.mHasOfflineIMs)
+{
+	setListenIMFloaterOpened();
+}
+
+LLConversation::LLConversation(const LLIMModel::LLIMSession& session)
+:	mTime(time_corrected()),
+	mTimestamp(createTimestamp(mTime)),
+	mConversationType(session.mSessionType),
+	mConversationName(session.mName),
+	mHistoryFileName(session.mHistoryFileName),
+	mSessionID(session.mSessionID),
+	mParticipantID(session.mOtherParticipantID),
+	mIsVoice(session.mStartedAsIMCall),
+	mHasOfflineIMs(session.mHasOfflineMessage)
+{
+	setListenIMFloaterOpened();
+}
+
+LLConversation::LLConversation(const LLConversation& conversation)
+{
+	mTime				= conversation.getTime();
+	mTimestamp			= conversation.getTimestamp();
+	mConversationType	= conversation.getConversationType();
+	mConversationName	= conversation.getConversationName();
+	mHistoryFileName	= conversation.getHistoryFileName();
+	mSessionID			= conversation.getSessionID();
+	mParticipantID		= conversation.getParticipantID();
+	mIsVoice			= conversation.isVoice();
+	mHasOfflineIMs		= conversation.hasOfflineMessages();
+
+	setListenIMFloaterOpened();
+}
+
+LLConversation::~LLConversation()
+{
+	mIMFloaterShowedConnection.disconnect();
+}
+
+void LLConversation::onIMFloaterShown(const LLUUID& session_id)
+{
+	if (mSessionID == session_id)
+	{
+		mHasOfflineIMs = false;
+	}
+}
+
+// static
+const std::string LLConversation::createTimestamp(const time_t& utc_time)
+{
+	std::string timeStr;
+	LLSD substitution;
+	substitution["datetime"] = (S32) utc_time;
+
+	timeStr = "["+LLTrans::getString ("TimeMonth")+"]/["
+				 +LLTrans::getString ("TimeDay")+"]/["
+				 +LLTrans::getString ("TimeYear")+"] ["
+				 +LLTrans::getString ("TimeHour")+"]:["
+				 +LLTrans::getString ("TimeMin")+"]";
+
+
+	LLStringUtil::format (timeStr, substitution);
+	return timeStr;
+}
+
+void LLConversation::setListenIMFloaterOpened()
+{
+	LLIMFloater* floater = LLIMFloater::findInstance(mSessionID);
+
+	bool has_offline_ims = !mIsVoice && mHasOfflineIMs;
+	bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus();
+
+	// we don't need to listen for im floater with this conversation is opened
+	// if floater is already opened or this conversation doesn't have unread offline messages
+	if (has_offline_ims && !ims_are_read)
+	{
+		mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1));
+	}
+}
+/************************************************************************/
+/*             LLConversationLog implementation                         */
+/************************************************************************/
+
+LLConversationLog::LLConversationLog()
+{
+	loadFromFile(getFileName());
+
+	LLIMMgr::instance().addSessionObserver(this);
+	LLAvatarTracker::instance().addObserver(this);
+}
+void LLConversationLog::logConversation(const LLConversation& conversation)
+{
+	mConversations.push_back(conversation);
+	notifyObservers();
+}
+
+void LLConversationLog::removeConversation(const LLConversation& conversation)
+{
+	conversations_vec_t::iterator conv_it = mConversations.begin();
+	for(; conv_it != mConversations.end(); ++conv_it)
+	{
+		if (conv_it->getSessionID() == conversation.getSessionID() && conv_it->getTime() == conversation.getTime())
+		{
+			mConversations.erase(conv_it);
+			notifyObservers();
+			return;
+		}
+	}
+}
+
+const LLConversation* LLConversationLog::getConversation(const LLUUID& session_id)
+{
+	conversations_vec_t::const_iterator conv_it = mConversations.begin();
+	for(; conv_it != mConversations.end(); ++conv_it)
+	{
+		if (conv_it->getSessionID() == session_id)
+		{
+			return &*conv_it;
+		}
+	}
+
+	return NULL;
+}
+
+void LLConversationLog::addObserver(LLConversationLogObserver* observer)
+{
+	mObservers.insert(observer);
+}
+
+void LLConversationLog::removeObserver(LLConversationLogObserver* observer)
+{
+	mObservers.erase(observer);
+}
+
+void LLConversationLog::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
+{
+	LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
+	if (session)
+	{
+		LLConversation conversation(*session);
+		LLConversationLog::instance().logConversation(conversation);
+	}
+}
+
+// LLFriendObserver
+void LLConversationLog::changed(U32 mask)
+{
+	if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE))
+	{
+		notifyObservers();
+	}
+}
+
+void LLConversationLog::cache()
+{
+	saveToFile(getFileName());
+}
+
+std::string LLConversationLog::getFileName()
+{
+	std::string agent_id_string;
+	gAgent.getID().toString(agent_id_string);
+
+	return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_string) + ".call_log";
+}
+
+bool LLConversationLog::saveToFile(const std::string& filename)
+{
+	if(!filename.size())
+	{
+		llwarns << "Call log list filename is empty!" << llendl;
+		return false;
+	}
+
+	LLFILE* fp = LLFile::fopen(filename, "wb");
+	if (!fp)
+	{
+		llwarns << "Couldn't open call log list" << filename << llendl;
+		return false;
+	}
+
+	std::string participant_id;
+	std::string conversation_id;
+
+	conversations_vec_t::const_iterator conv_it = mConversations.begin();
+	for (; conv_it != mConversations.end(); ++conv_it)
+	{
+		conv_it->getSessionID().toString(conversation_id);
+		conv_it->getParticipantID().toString(participant_id);
+
+		// examples of two file entries
+		// [1343221177] 0 1 0 John Doe| 7e4ec5be-783f-49f5-71dz-16c58c64c145 4ec62a74-c246-0d25-2af6-846beac2aa55 john.doe|
+		// [1343222639] 2 0 0 Ad-hoc Conference| c3g67c89-c479-4c97-b21d-32869bcfe8rc 68f1c33e-4135-3e3e-a897-8c9b23115c09 Ad-hoc Conference hash597394a0-9982-766d-27b8-c75560213b9a|
+
+		fprintf(fp, "[%d] %d %d %d %s| %s %s %s|\n",
+				(S32)conv_it->getTime(),
+				(S32)conv_it->getConversationType(),
+				(S32)conv_it->isVoice(),
+				(S32)conv_it->hasOfflineMessages(),
+				     conv_it->getConversationName().c_str(),
+				participant_id.c_str(),
+				conversation_id.c_str(),
+				conv_it->getHistoryFileName().c_str());
+	}
+	fclose(fp);
+	return true;
+}
+bool LLConversationLog::loadFromFile(const std::string& filename)
+{
+	if(!filename.size())
+	{
+		llwarns << "Call log list filename is empty!" << llendl;
+		return false;
+	}
+
+	LLFILE* fp = LLFile::fopen(filename, "rb");
+	if (!fp)
+	{
+		llwarns << "Couldn't open call log list" << filename << llendl;
+		return false;
+	}
+
+	char buffer[MAX_STRING];
+	char conv_name_buffer[MAX_STRING];
+	char part_id_buffer[MAX_STRING];
+	char conv_id_buffer[MAX_STRING];
+	char history_file_name[MAX_STRING];
+	int is_voice;
+	int has_offline_ims;
+	int stype;
+	S32 time;
+
+	while (!feof(fp) && fgets(buffer, MAX_STRING, fp))
+	{
+		conv_name_buffer[0] = '\0';
+		part_id_buffer[0]	= '\0';
+		conv_id_buffer[0]	= '\0';
+
+		sscanf(buffer, "[%d] %d %d %d %[^|]| %s %s %[^|]|",
+				&time,
+				&stype,
+				&is_voice,
+				&has_offline_ims,
+				conv_name_buffer,
+				part_id_buffer,
+				conv_id_buffer,
+				history_file_name);
+
+		Conversation_params params(time);
+		params.mConversationType = (SessionType)stype;
+		params.mIsVoice = is_voice;
+		params.mHasOfflineIMs = has_offline_ims;
+		params.mConversationName = std::string(conv_name_buffer);
+		params.mParticipantID = LLUUID(part_id_buffer);
+		params.mSessionID = LLUUID(conv_id_buffer);
+		params.mHistoryFileName = std::string(history_file_name);
+
+		LLConversation conversation(params);
+		mConversations.push_back(conversation);
+	}
+	fclose(fp);
+
+	notifyObservers();
+	return true;
+}
+
+void LLConversationLog::notifyObservers()
+{
+	std::set<LLConversationLogObserver*>::const_iterator iter = mObservers.begin();
+	for (; iter != mObservers.end(); ++iter)
+	{
+		(*iter)->changed();
+	}
+}
diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h
new file mode 100644
index 0000000000000000000000000000000000000000..18865bb80e851bfd437044501880b185bc75c2df
--- /dev/null
+++ b/indra/newview/llconversationlog.h
@@ -0,0 +1,164 @@
+/**
+ * @file llconversationlog.h
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLCONVERSATIONLOG_H_
+#define LLCONVERSATIONLOG_H_
+
+#include "llcallingcard.h"
+#include "llimfloater.h"
+#include "llimview.h"
+
+class LLConversationLogObserver;
+struct Conversation_params;
+
+typedef LLIMModel::LLIMSession::SType SessionType;
+
+/*
+ * This class represents a particular session(conversation) of any type(im/voice/p2p/group/...) by storing some of session's data.
+ * Each LLConversation object has a corresponding visual representation in a form of LLConversationLogListItem.
+ */
+class LLConversation
+{
+public:
+
+	LLConversation(const Conversation_params& params);
+	LLConversation(const LLIMModel::LLIMSession& session);
+	LLConversation(const LLConversation& conversation);
+
+	~LLConversation();
+
+	const SessionType&	getConversationType()	const	{ return mConversationType; }
+	const std::string&	getConversationName()	const	{ return mConversationName; }
+	const std::string&	getHistoryFileName()	const	{ return mHistoryFileName; }
+	const LLUUID&		getSessionID()			const	{ return mSessionID; }
+	const LLUUID&		getParticipantID()		const	{ return mParticipantID; }
+	const std::string&	getTimestamp()			const	{ return mTimestamp; }
+	const time_t&		getTime()				const	{ return mTime; }
+	bool				isVoice()				const	{ return mIsVoice; }
+	bool				hasOfflineMessages()	const	{ return mHasOfflineIMs; }
+
+	/*
+	 * Resets flag of unread offline message to false when im floater with this conversation is opened.
+	 */
+	void onIMFloaterShown(const LLUUID& session_id);
+
+	/*
+	 * returns string representation(in form of: mm/dd/yyyy hh:mm) of time when conversation was started
+	 */
+	static const std::string createTimestamp(const time_t& utc_time);
+
+private:
+
+	/*
+	 * If conversation has unread offline messages sets callback for opening LLIMFloater
+	 * with this conversation.
+	 */
+	void setListenIMFloaterOpened();
+
+	boost::signals2::connection mIMFloaterShowedConnection;
+
+	time_t			mTime; // start time of conversation
+	SessionType		mConversationType;
+	std::string		mConversationName;
+	std::string		mHistoryFileName;
+	LLUUID			mSessionID;
+	LLUUID			mParticipantID;
+	bool			mIsVoice;
+	bool			mHasOfflineIMs;
+	std::string		mTimestamp; // conversation start time in form of: mm/dd/yyyy hh:mm
+};
+
+/**
+ * LLConversationLog stores all agent's conversations.
+ * This class is responsible for creating and storing LLConversation objects when im or voice session starts.
+ * Also this class saves/retrieves conversations to/from file.
+ *
+ * Also please note that it may be several conversations with the same sessionID stored in the conversation log.
+ * To distinguish two conversations with the same sessionID it's also needed to compare their creation date.
+ */
+
+class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObserver, LLFriendObserver
+{
+	friend class LLSingleton<LLConversationLog>;
+public:
+
+	/**
+	 * adds conversation to the conversation list and notifies observers
+	 */
+	void logConversation(const LLConversation& conversation);
+	void removeConversation(const LLConversation& conversation);
+
+	/**
+	 * Returns first conversation with matched session_id
+	 */
+	const LLConversation* getConversation(const LLUUID& session_id);
+
+	void addObserver(LLConversationLogObserver* observer);
+	void removeObserver(LLConversationLogObserver* observer);
+
+	const std::vector<LLConversation>& getConversations() { return mConversations; }
+
+	// LLIMSessionObserver triggers
+	virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id);
+	virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){}							// Stub
+	virtual void sessionRemoved(const LLUUID& session_id){}										// Stub
+	virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){}	// Stub
+
+	// LLFriendObserver trigger
+	virtual void changed(U32 mask);
+
+	/**
+	 * public method which is called on viewer exit to save conversation log
+	 */
+	void cache();
+
+private:
+
+	LLConversationLog();
+	void notifyObservers();
+
+	/**
+	 * constructs file name in which conversations log will be saved
+	 * file name template: agentID.call_log.
+	 * For example: a086icaa-782d-88d0-ae29-987a55c99sss.call_log
+	 */
+	std::string getFileName();
+
+	bool saveToFile(const std::string& filename);
+	bool loadFromFile(const std::string& filename);
+
+	typedef std::vector<LLConversation> conversations_vec_t;
+	std::vector<LLConversation>				mConversations;
+	std::set<LLConversationLogObserver*>	mObservers;
+};
+
+class LLConversationLogObserver
+{
+public:
+	virtual ~LLConversationLogObserver(){}
+	virtual void changed() = 0;
+};
+
+#endif /* LLCONVERSATIONLOG_H_ */
diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0433719a891a098981927c4d7e9e9135dccae535
--- /dev/null
+++ b/indra/newview/llconversationloglist.cpp
@@ -0,0 +1,422 @@
+/**
+ * @file llconversationloglist.cpp
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llavataractions.h"
+#include "llagent.h"
+#include "llfloaterreg.h"
+#include "llfloaterconversationpreview.h"
+#include "llgroupactions.h"
+#include "llconversationloglist.h"
+#include "llconversationloglistitem.h"
+#include "llviewermenu.h"
+
+static LLDefaultChildRegistry::Register<LLConversationLogList> r("conversation_log_list");
+
+static LLConversationLogListNameComparator NAME_COMPARATOR;
+static LLConversationLogListDateComparator DATE_COMPARATOR;
+
+LLConversationLogList::LLConversationLogList(const Params& p)
+:	LLFlatListViewEx(p),
+	mIsDirty(true)
+{
+	LLConversationLog::instance().addObserver(this);
+
+	// Set up context menu.
+	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar check_registrar;
+	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+
+	registrar.add		("Calllog.Action",	boost::bind(&LLConversationLogList::onCustomAction,	this, _2));
+	check_registrar.add ("Calllog.Check",	boost::bind(&LLConversationLogList::isActionChecked,this, _2));
+	enable_registrar.add("Calllog.Enable",	boost::bind(&LLConversationLogList::isActionEnabled,this, _2));
+
+	LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(
+									"menu_conversation_log_gear.xml",
+									gMenuHolder,
+									LLViewerMenuHolderGL::child_registry_t::instance());
+	if(context_menu)
+	{
+		mContextMenu = context_menu->getHandle();
+	}
+
+	mIsFriendsOnTop = gSavedSettings.getBOOL("SortFriendsFirst");
+}
+
+LLConversationLogList::~LLConversationLogList()
+{
+	if (mContextMenu.get())
+	{
+		mContextMenu.get()->die();
+	}
+
+	LLConversationLog::instance().removeObserver(this);
+}
+
+void LLConversationLogList::draw()
+{
+	if (mIsDirty)
+	{
+		refresh();
+	}
+	LLFlatListViewEx::draw();
+}
+
+BOOL LLConversationLogList::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+	BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask);
+
+	LLToggleableMenu* context_menu = mContextMenu.get();
+	{
+		context_menu->buildDrawLabels();
+	if (context_menu && size())
+		context_menu->updateParent(LLMenuGL::sMenuContainer);
+		LLMenuGL::showPopup(this, context_menu, x, y);
+	}
+
+	return handled;
+}
+
+void LLConversationLogList::setNameFilter(const std::string& filter)
+{
+	std::string filter_upper = filter;
+	LLStringUtil::toUpper(filter_upper);
+	if (mNameFilter != filter_upper)
+	{
+		mNameFilter = filter_upper;
+		setDirty();
+	}
+}
+
+bool LLConversationLogList::findInsensitive(std::string haystack, const std::string& needle_upper)
+{
+    LLStringUtil::toUpper(haystack);
+    return haystack.find(needle_upper) != std::string::npos;
+}
+
+void LLConversationLogList::sortByName()
+{
+	setComparator(&NAME_COMPARATOR);
+	sort();
+}
+
+void LLConversationLogList::sortByDate()
+{
+	setComparator(&DATE_COMPARATOR);
+	sort();
+}
+
+void LLConversationLogList::toggleSortFriendsOnTop()
+{
+	mIsFriendsOnTop = !mIsFriendsOnTop;
+	gSavedSettings.setBOOL("SortFriendsFirst", mIsFriendsOnTop);
+	sort();
+}
+
+void LLConversationLogList::changed()
+{
+	refresh();
+}
+
+void LLConversationLogList::addNewItem(const LLConversation* conversation)
+{
+	LLConversationLogListItem* item = new LLConversationLogListItem(&*conversation);
+	if (!mNameFilter.empty())
+	{
+		item->highlightNameDate(mNameFilter);
+	}
+	addItem(item, conversation->getSessionID(), ADD_TOP);
+}
+
+void LLConversationLogList::refresh()
+{
+	rebuildList();
+	sort();
+
+	mIsDirty = false;
+}
+
+void LLConversationLogList::rebuildList()
+{
+	clear();
+
+	bool have_filter = !mNameFilter.empty();
+
+	const std::vector<LLConversation>& conversations = LLConversationLog::instance().getConversations();
+	std::vector<LLConversation>::const_iterator iter = conversations.begin();
+
+	for (; iter != conversations.end(); ++iter)
+	{
+		bool not_found = have_filter && !findInsensitive(iter->getConversationName(), mNameFilter) && !findInsensitive(iter->getTimestamp(), mNameFilter);
+		if (not_found)
+			continue;
+
+		addNewItem(&*iter);
+	}
+}
+
+void LLConversationLogList::onCustomAction(const LLSD& userdata)
+{
+	const std::string command_name = userdata.asString();
+	const LLUUID& selected_id = getSelectedConversation()->getParticipantID();
+	LLIMModel::LLIMSession::SType stype = getSelectedSessionType();
+
+	if ("im" == command_name)
+	{
+		switch (stype)
+		{
+		case LLIMModel::LLIMSession::P2P_SESSION:
+			LLAvatarActions::startIM(selected_id);
+			break;
+
+		case LLIMModel::LLIMSession::GROUP_SESSION:
+			LLGroupActions::startIM(selected_id);
+			break;
+
+		default:
+			break;
+		}
+	}
+	else if ("call" == command_name)
+	{
+		switch (stype)
+		{
+		case LLIMModel::LLIMSession::P2P_SESSION:
+			LLAvatarActions::startCall(selected_id);
+			break;
+
+		case LLIMModel::LLIMSession::GROUP_SESSION:
+			LLGroupActions::startCall(selected_id);
+			break;
+
+		default:
+			break;
+		}
+	}
+	else if ("view_profile" == command_name)
+	{
+		switch (stype)
+		{
+		case LLIMModel::LLIMSession::P2P_SESSION:
+			LLAvatarActions::showProfile(selected_id);
+			break;
+
+		case LLIMModel::LLIMSession::GROUP_SESSION:
+			LLGroupActions::show(selected_id);
+			break;
+
+		default:
+			break;
+		}
+	}
+	else if ("chat_history" == command_name)
+	{
+		const LLUUID& session_id = getSelectedConversation()->getSessionID();
+		LLFloaterReg::showInstance("preview_conversation", session_id, true);
+	}
+	else if ("offer_teleport" == command_name)
+	{
+		LLAvatarActions::offerTeleport(selected_id);
+	}
+	else if("add_rem_friend" == command_name)
+	{
+		if (LLAvatarActions::isFriend(selected_id))
+		{
+			LLAvatarActions::removeFriendDialog(selected_id);
+		}
+		else
+		{
+			LLAvatarActions::requestFriendshipDialog(selected_id);
+		}
+	}
+	else if ("invite_to_group" == command_name)
+	{
+		LLAvatarActions::inviteToGroup(selected_id);
+	}
+	else if ("show_on_map" == command_name)
+	{
+		LLAvatarActions::showOnMap(selected_id);
+	}
+	else if ("share" == command_name)
+	{
+		LLAvatarActions::share(selected_id);
+	}
+	else if ("pay" == command_name)
+	{
+		LLAvatarActions::pay(selected_id);
+	}
+	else if ("block" == command_name)
+	{
+		LLAvatarActions::toggleBlock(selected_id);
+	}
+}
+
+bool LLConversationLogList::isActionEnabled(const LLSD& userdata)
+{
+	if (numSelected() != 1)
+	{
+		return false;
+	}
+
+	const std::string command_name = userdata.asString();
+
+	LLIMModel::LLIMSession::SType stype = getSelectedSessionType();
+	const LLUUID& selected_id = getSelectedConversation()->getParticipantID();
+
+	bool is_p2p   = LLIMModel::LLIMSession::P2P_SESSION == stype;
+	bool is_group = LLIMModel::LLIMSession::GROUP_SESSION == stype;
+
+	if ("can_im" == command_name || "can_view_profile" == command_name)
+	{
+		return is_p2p || is_group;
+	}
+	else if ("can_view_chat_history" == command_name)
+	{
+		return true;
+	}
+	else if ("can_call"	== command_name)
+	{
+		return (is_p2p || is_group) && LLAvatarActions::canCall();
+	}
+	else if ("add_rem_friend"		== command_name ||
+			 "can_invite_to_group"	== command_name ||
+			 "can_share"			== command_name ||
+			 "can_block"			== command_name ||
+			 "can_pay"				== command_name)
+	{
+		return is_p2p;
+	}
+	else if("can_offer_teleport" == command_name)
+	{
+		return is_p2p && LLAvatarActions::canOfferTeleport(selected_id);
+	}
+	else if ("can_show_on_map")
+	{
+		return is_p2p && ((LLAvatarTracker::instance().isBuddyOnline(selected_id) && is_agent_mappable(selected_id)) || gAgent.isGodlike());
+	}
+
+	return false;
+}
+
+bool LLConversationLogList::isActionChecked(const LLSD& userdata)
+{
+	const std::string command_name = userdata.asString();
+
+	const LLUUID& selected_id = getSelectedConversation()->getParticipantID();
+	bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == getSelectedSessionType();
+
+	if ("is_blocked" == command_name)
+	{
+		return is_p2p && LLAvatarActions::isBlocked(selected_id);
+	}
+	else if ("is_friend" == command_name)
+	{
+		return is_p2p && LLAvatarActions::isFriend(selected_id);
+	}
+
+	return false;
+}
+
+LLIMModel::LLIMSession::SType LLConversationLogList::getSelectedSessionType()
+{
+	const LLConversationLogListItem* item = getSelectedConversationPanel();
+
+	if (item)
+	{
+		return item->getConversation()->getConversationType();
+	}
+
+	return LLIMModel::LLIMSession::NONE_SESSION;
+}
+
+const LLConversationLogListItem* LLConversationLogList::getSelectedConversationPanel()
+{
+	LLPanel* panel = LLFlatListViewEx::getSelectedItem();
+	LLConversationLogListItem* conv_panel = dynamic_cast<LLConversationLogListItem*>(panel);
+
+	return conv_panel;
+}
+
+const LLConversation* LLConversationLogList::getSelectedConversation()
+{
+	const LLConversationLogListItem* panel = getSelectedConversationPanel();
+
+	if (panel)
+	{
+		return panel->getConversation();
+	}
+
+	return NULL;
+}
+
+bool LLConversationLogListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const
+{
+	const LLConversationLogListItem* conversation_item1 = dynamic_cast<const LLConversationLogListItem*>(item1);
+	const LLConversationLogListItem* conversation_item2 = dynamic_cast<const LLConversationLogListItem*>(item2);
+
+	if (!conversation_item1 || !conversation_item2)
+	{
+		llerror("conversation_item1 and conversation_item2 cannot be null", 0);
+		return true;
+	}
+
+	return doCompare(conversation_item1, conversation_item2);
+}
+
+bool LLConversationLogListNameComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const
+{
+	std::string name1 = conversation1->getConversation()->getConversationName();
+	std::string name2 = conversation2->getConversation()->getConversationName();
+	const LLUUID& id1 = conversation1->getConversation()->getParticipantID();
+	const LLUUID& id2 = conversation2->getConversation()->getParticipantID();
+
+	LLStringUtil::toUpper(name1);
+	LLStringUtil::toUpper(name2);
+
+	bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst");
+	if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2)))
+	{
+		return LLAvatarActions::isFriend(id1);
+	}
+
+	return name1 < name2;
+}
+
+bool LLConversationLogListDateComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const
+{
+	time_t date1 = conversation1->getConversation()->getTime();
+	time_t date2 = conversation2->getConversation()->getTime();
+	const LLUUID& id1 = conversation1->getConversation()->getParticipantID();
+	const LLUUID& id2 = conversation2->getConversation()->getParticipantID();
+
+	bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst");
+	if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2)))
+	{
+		return LLAvatarActions::isFriend(id1);
+	}
+
+	return date1 > date2;
+}
diff --git a/indra/newview/llconversationloglist.h b/indra/newview/llconversationloglist.h
new file mode 100644
index 0000000000000000000000000000000000000000..dff34a74c62d2b6750beb62c80156ce82956e6b1
--- /dev/null
+++ b/indra/newview/llconversationloglist.h
@@ -0,0 +1,143 @@
+/**
+ * @file llconversationloglist.h
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLCONVERSATIONLOGLIST_H_
+#define LLCONVERSATIONLOGLIST_H_
+
+#include "llconversationlog.h"
+#include "llflatlistview.h"
+#include "lltoggleablemenu.h"
+
+class LLConversationLogListItem;
+
+/**
+ * List of all agent's conversations. I.e. history of conversations.
+ * This list represents contents of the LLConversationLog.
+ * Each change in LLConversationLog leads to rebuilding this list, so
+ * it's always in actual state.
+ */
+
+class LLConversationLogList: public LLFlatListViewEx, public LLConversationLogObserver
+{
+	LOG_CLASS(LLConversationLogList);
+public:
+	struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params>
+	{
+		Params(){};
+	};
+
+	LLConversationLogList(const Params& p);
+	virtual ~LLConversationLogList();
+
+	virtual void draw();
+
+	virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+
+	LLToggleableMenu*	getContextMenu() const { return mContextMenu.get(); }
+
+	void addNewItem(const LLConversation* conversation);
+	void setNameFilter(const std::string& filter);
+	void sortByName();
+	void sortByDate();
+	void toggleSortFriendsOnTop();
+	bool getSortFriendsOnTop() const { return mIsFriendsOnTop; }
+
+	/**
+	 * Changes from LLConversationLogObserver
+	 */
+	virtual void changed();
+
+private:
+
+	void setDirty(bool dirty = true) { mIsDirty = dirty; }
+	void refresh();
+
+	/**
+	 * Clears list and re-adds items from LLConverstationLog
+	 * If filter is not empty re-adds items which match the filter
+	 */
+	void rebuildList();
+
+	bool findInsensitive(std::string haystack, const std::string& needle_upper);
+
+	void onCustomAction (const LLSD& userdata);
+	bool isActionEnabled(const LLSD& userdata);
+	bool isActionChecked(const LLSD& userdata);
+
+	LLIMModel::LLIMSession::SType getSelectedSessionType();
+	const LLConversationLogListItem* getSelectedConversationPanel();
+	const LLConversation* getSelectedConversation();
+
+	LLHandle<LLToggleableMenu>	mContextMenu;
+	bool mIsDirty;
+	bool mIsFriendsOnTop;
+	std::string mNameFilter;
+};
+
+/**
+ * Abstract comparator for ConversationLogList items
+ */
+class LLConversationLogListItemComparator : public LLFlatListView::ItemComparator
+{
+	LOG_CLASS(LLConversationLogListItemComparator);
+
+public:
+	LLConversationLogListItemComparator() {};
+	virtual ~LLConversationLogListItemComparator() {};
+
+	virtual bool compare(const LLPanel* item1, const LLPanel* item2) const;
+
+protected:
+
+	virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const = 0;
+};
+
+class LLConversationLogListNameComparator : public LLConversationLogListItemComparator
+{
+	LOG_CLASS(LLConversationLogListNameComparator);
+
+public:
+	LLConversationLogListNameComparator() {};
+	virtual ~LLConversationLogListNameComparator() {};
+
+protected:
+
+	virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const;
+};
+
+class LLConversationLogListDateComparator : public LLConversationLogListItemComparator
+{
+	LOG_CLASS(LLConversationLogListDateComparator);
+
+public:
+	LLConversationLogListDateComparator() {};
+	virtual ~LLConversationLogListDateComparator() {};
+
+protected:
+
+	virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const;
+};
+
+#endif /* LLCONVERSATIONLOGLIST_H_ */
diff --git a/indra/newview/llconversationloglistitem.cpp b/indra/newview/llconversationloglistitem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc2e75786408fa3960aec6c3e489c20213dbecfe
--- /dev/null
+++ b/indra/newview/llconversationloglistitem.cpp
@@ -0,0 +1,157 @@
+/**
+ * @file llconversationloglistitem.cpp
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// llui
+#include "lliconctrl.h"
+#include "lltextbox.h"
+#include "lltextutil.h"
+
+// newview
+#include "llavatariconctrl.h"
+#include "llconversationlog.h"
+#include "llconversationloglistitem.h"
+#include "llgroupiconctrl.h"
+#include "llinventoryicon.h"
+
+LLConversationLogListItem::LLConversationLogListItem(const LLConversation* conversation)
+:	LLPanel(),
+	mConversation(conversation),
+	mConversationName(NULL),
+	mConversationDate(NULL)
+{
+	buildFromFile("panel_conversation_log_list_item.xml");
+
+	LLIMFloater* floater = LLIMFloater::findInstance(mConversation->getSessionID());
+
+	bool has_offline_ims = !mConversation->isVoice() && mConversation->hasOfflineMessages();
+	bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus();
+
+	if (has_offline_ims && !ims_are_read)
+	{
+		mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversationLogListItem::onIMFloaterShown, this, _1));
+	}
+}
+
+LLConversationLogListItem::~LLConversationLogListItem()
+{
+	mIMFloaterShowedConnection.disconnect();
+}
+
+BOOL LLConversationLogListItem::postBuild()
+{
+	initIcons();
+
+	// set conversation name
+	mConversationName = getChild<LLTextBox>("conversation_name");
+	mConversationName->setValue(mConversation->getConversationName());
+
+	// set conversation date and time
+	mConversationDate = getChild<LLTextBox>("date_time");
+	mConversationDate->setValue(mConversation->getTimestamp());
+
+	getChild<LLButton>("delete_btn")->setClickedCallback(boost::bind(&LLConversationLogListItem::onRemoveBtnClicked, this));
+
+	return TRUE;
+}
+
+void LLConversationLogListItem::initIcons()
+{
+	switch (mConversation->getConversationType())
+	{
+		case LLIMModel::LLIMSession::P2P_SESSION:
+		case LLIMModel::LLIMSession::ADHOC_SESSION:
+		{
+			LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon");
+			avatar_icon->setVisible(TRUE);
+			avatar_icon->setValue(mConversation->getParticipantID());
+			break;
+		}
+		case LLIMModel::LLIMSession::GROUP_SESSION:
+		{
+			LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon");
+			group_icon->setVisible(TRUE);
+			group_icon->setValue(mConversation->getSessionID());
+			break;
+		}
+		default:
+			break;
+	}
+
+	if (mConversation->isVoice())
+	{
+		getChild<LLIconCtrl>("voice_session_icon")->setVisible(TRUE);
+	}
+	else
+	{
+		if (mConversation->hasOfflineMessages())
+		{
+			getChild<LLIconCtrl>("unread_ims_icon")->setVisible(TRUE);
+		}
+	}
+}
+
+void LLConversationLogListItem::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+	getChildView("hovered_icon")->setVisible(true);
+	LLPanel::onMouseEnter(x, y, mask);
+}
+
+void LLConversationLogListItem::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+	getChildView("hovered_icon")->setVisible(false);
+	LLPanel::onMouseLeave(x, y, mask);
+}
+
+void LLConversationLogListItem::setValue(const LLSD& value)
+{
+	if (!value.isMap() || !value.has("selected"))
+	{
+		return;
+	}
+
+	getChildView("selected_icon")->setVisible(value["selected"]);
+}
+
+void LLConversationLogListItem::onIMFloaterShown(const LLUUID& session_id)
+{
+	if (mConversation->getSessionID() == session_id)
+	{
+		getChild<LLIconCtrl>("unread_ims_icon")->setVisible(FALSE);
+	}
+}
+
+void LLConversationLogListItem::onRemoveBtnClicked()
+{
+	LLConversationLog::instance().removeConversation(*mConversation);
+}
+
+void LLConversationLogListItem::highlightNameDate(const std::string& highlited_text)
+{
+	LLStyle::Params params;
+	LLTextUtil::textboxSetHighlightedVal(mConversationName, params, mConversation->getConversationName(), highlited_text);
+	LLTextUtil::textboxSetHighlightedVal(mConversationDate, params, mConversation->getTimestamp(), highlited_text);
+}
diff --git a/indra/newview/llconversationloglistitem.h b/indra/newview/llconversationloglistitem.h
new file mode 100644
index 0000000000000000000000000000000000000000..deba7d4563e7f9eb1b86eb679de2d968a4af630e
--- /dev/null
+++ b/indra/newview/llconversationloglistitem.h
@@ -0,0 +1,77 @@
+/**
+ * @file llconversationloglistitem.h
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLCONVERSATIONLOGLISTITEM_H_
+#define LLCONVERSATIONLOGLISTITEM_H_
+
+#include "llimfloater.h"
+#include "llpanel.h"
+
+class LLTextBox;
+class LLConversation;
+
+/**
+ * This class is a visual representation of LLConversation, each of which is LLConversationLog entry.
+ * LLConversationLogList consists of these LLConversationLogListItems.
+ * LLConversationLogListItem consists of:
+ *		conversaion_type_icon
+ *		conversaion_name
+ *		conversaion_date
+ * Also LLConversationLogListItem holds pointer to its LLConversationLog.
+ */
+
+class LLConversationLogListItem : public LLPanel
+{
+public:
+	LLConversationLogListItem(const LLConversation* conversation);
+	virtual ~LLConversationLogListItem();
+
+	void onMouseEnter(S32 x, S32 y, MASK mask);
+	void onMouseLeave(S32 x, S32 y, MASK mask);
+
+	virtual void setValue(const LLSD& value);
+
+	virtual BOOL postBuild();
+
+	void onIMFloaterShown(const LLUUID& session_id);
+	void onRemoveBtnClicked();
+
+	const LLConversation* getConversation() const { return mConversation; }
+
+	void highlightNameDate(const std::string& highlited_text);
+
+private:
+
+	void initIcons();
+
+	const LLConversation* mConversation;
+
+	LLTextBox*		mConversationName;
+	LLTextBox*		mConversationDate;
+
+	boost::signals2::connection mIMFloaterShowedConnection;
+};
+
+#endif /* LLCONVERSATIONLOGITEM_H_ */
diff --git a/indra/newview/llfloaterconversationlog.cpp b/indra/newview/llfloaterconversationlog.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c77a9e74bb23bf493f8476cd1fe371a58aba914e
--- /dev/null
+++ b/indra/newview/llfloaterconversationlog.cpp
@@ -0,0 +1,127 @@
+/**
+ * @file llfloaterconversationlog.cpp
+ * @brief Functionality of the "conversation log" floater
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+#include "llviewerprecompiledheaders.h"
+
+#include "llconversationloglist.h"
+#include "llfiltereditor.h"
+#include "llfloaterconversationlog.h"
+#include "llmenubutton.h"
+
+LLFloaterConversationLog::LLFloaterConversationLog(const LLSD& key)
+:	LLFloater(key),
+	mConversationLogList(NULL)
+{
+	mCommitCallbackRegistrar.add("CallLog.Action",	boost::bind(&LLFloaterConversationLog::onCustomAction,  this, _2));
+	mEnableCallbackRegistrar.add("CallLog.Check",	boost::bind(&LLFloaterConversationLog::isActionChecked, this, _2));
+}
+
+BOOL LLFloaterConversationLog::postBuild()
+{
+	mConversationLogList = getChild<LLConversationLogList>("conversation_log_list");
+
+	switch (gSavedSettings.getU32("CallLogSortOrder"))
+	{
+	case E_SORT_BY_NAME:
+		mConversationLogList->sortByName();
+		break;
+
+	case E_SORT_BY_DATE:
+		mConversationLogList->sortByDate();
+		break;
+	}
+
+	// Use the context menu of the Conversation list for the Conversation tab gear menu.
+	LLToggleableMenu* conversations_gear_menu = mConversationLogList->getContextMenu();
+	if (conversations_gear_menu)
+	{
+		getChild<LLMenuButton>("conversations_gear_btn")->setMenu(conversations_gear_menu, LLMenuButton::MP_BOTTOM_LEFT);
+	}
+
+	getChild<LLFilterEditor>("people_filter_input")->setCommitCallback(boost::bind(&LLFloaterConversationLog::onFilterEdit, this, _2));
+
+	return LLFloater::postBuild();
+}
+
+void LLFloaterConversationLog::draw()
+{
+	LLFloater::draw();
+}
+
+void LLFloaterConversationLog::onFilterEdit(const std::string& search_string)
+{
+	std::string filter = search_string;
+	LLStringUtil::trimHead(filter);
+
+	mConversationLogList->setNameFilter(filter);
+}
+
+
+void LLFloaterConversationLog::onCustomAction (const LLSD& userdata)
+{
+	const std::string command_name = userdata.asString();
+
+	if ("sort_by_name" == command_name)
+	{
+		mConversationLogList->sortByName();
+		gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_NAME);
+	}
+	else if ("sort_by_date" == command_name)
+	{
+		mConversationLogList->sortByDate();
+		gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_DATE);
+	}
+	else if ("sort_friends_on_top" == command_name)
+	{
+		mConversationLogList->toggleSortFriendsOnTop();
+	}
+}
+
+bool LLFloaterConversationLog::isActionEnabled(const LLSD& userdata)
+{
+	return true;
+}
+
+bool LLFloaterConversationLog::isActionChecked(const LLSD& userdata)
+{
+	const std::string command_name = userdata.asString();
+
+	U32 sort_order = gSavedSettings.getU32("CallLogSortOrder");
+
+	if ("sort_by_name" == command_name)
+	{
+		return sort_order == E_SORT_BY_NAME;
+	}
+	else if ("sort_by_date" == command_name)
+	{
+		return sort_order == E_SORT_BY_DATE;
+	}
+	else if ("sort_friends_on_top" == command_name)
+	{
+		return gSavedSettings.getBOOL("SortFriendsFirst");
+	}
+
+	return false;
+}
diff --git a/indra/newview/llfloaterconversationlog.h b/indra/newview/llfloaterconversationlog.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d788c029008eb211b4de1f3afec0b7194bf3e13
--- /dev/null
+++ b/indra/newview/llfloaterconversationlog.h
@@ -0,0 +1,61 @@
+/**
+ * @file llfloaterconversationlog.h
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERCONVERSATIONLOG_H_
+#define LL_LLFLOATERCONVERSATIONLOG_H_
+
+#include "llfloater.h"
+
+class LLConversationLogList;
+
+class LLFloaterConversationLog : public LLFloater
+{
+public:
+
+	typedef enum e_sort_oder{
+		E_SORT_BY_NAME = 0,
+		E_SORT_BY_DATE = 1,
+	} ESortOrder;
+
+	LLFloaterConversationLog(const LLSD& key);
+	virtual ~LLFloaterConversationLog(){};
+
+	virtual BOOL postBuild();
+
+	virtual void draw();
+
+	void onFilterEdit(const std::string& search_string);
+
+private:
+
+	void onCustomAction (const LLSD& userdata);
+	bool isActionEnabled(const LLSD& userdata);
+	bool isActionChecked(const LLSD& userdata);
+
+	LLConversationLogList* mConversationLogList;
+};
+
+
+#endif /* LLFLOATERCONVERSATIONLOG_H_ */
diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8554bb066e1cb131a996c6d50c2281a47cfe350
--- /dev/null
+++ b/indra/newview/llfloaterconversationpreview.cpp
@@ -0,0 +1,112 @@
+/**
+ * @file llfloaterconversationpreview.cpp
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llconversationlog.h"
+#include "llfloaterconversationpreview.h"
+#include "llimview.h"
+#include "lllineeditor.h"
+
+LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_id)
+:	LLFloater(session_id),
+	mChatHistory(NULL),
+	mSessionID(session_id.asUUID())
+{}
+
+BOOL LLFloaterConversationPreview::postBuild()
+{
+	mChatHistory = getChild<LLChatHistory>("chat_history");
+
+	const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID);
+	if (conv)
+	{
+		std::string name = conv->getConversationName();
+		LLStringUtil::format_map_t args;
+		args["[NAME]"] = name;
+		std::string title = getString("Title", args);
+		setTitle(title);
+
+		getChild<LLLineEditor>("description")->setValue(name);
+	}
+
+	return LLFloater::postBuild();
+}
+
+void LLFloaterConversationPreview::draw()
+{
+	LLFloater::draw();
+}
+
+void LLFloaterConversationPreview::onOpen(const LLSD& session_id)
+{
+	const LLConversation* conv = LLConversationLog::instance().getConversation(session_id);
+	if (!conv)
+	{
+		return;
+	}
+	std::list<LLSD> messages;
+	std::string file = conv->getHistoryFileName();
+	LLLogChat::loadAllHistory(file, messages);
+
+	if (messages.size())
+	{
+		std::ostringstream message;
+		std::list<LLSD>::const_iterator iter = messages.begin();
+		for (; iter != messages.end(); ++iter)
+		{
+			LLSD msg = *iter;
+
+			std::string time	= msg["time"].asString();
+			LLUUID from_id		= msg["from_id"].asUUID();
+			std::string from	= msg["from"].asString();
+			std::string message	= msg["message"].asString();
+			bool is_history	= msg["is_history"].asBoolean();
+
+			LLChat chat;
+			chat.mFromID = from_id;
+			chat.mSessionID = session_id;
+			chat.mFromName = from;
+			chat.mTimeStr = time;
+			chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle;
+			chat.mText = message;
+
+			appendMessage(chat);
+		}
+	}
+}
+
+void LLFloaterConversationPreview::appendMessage(const LLChat& chat)
+{
+	if (!chat.mMuted)
+	{
+		LLSD args;
+		args["use_plain_text_chat_history"] = true;
+		args["show_time"] = true;
+		args["show_names_for_p2p_conv"] = true;
+
+		mChatHistory->appendMessage(chat);
+	}
+}
diff --git a/indra/newview/llfloaterconversationpreview.h b/indra/newview/llfloaterconversationpreview.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfc7c3448529f6ecb2da81970440cc00449a8311
--- /dev/null
+++ b/indra/newview/llfloaterconversationpreview.h
@@ -0,0 +1,51 @@
+/**
+ * @file llfloaterconversationpreview.h
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLFLOATERCONVERSATIONPREVIEW_H_
+#define LLFLOATERCONVERSATIONPREVIEW_H_
+
+#include "llchathistory.h"
+#include "llfloater.h"
+
+class LLFloaterConversationPreview : public LLFloater
+{
+public:
+
+	LLFloaterConversationPreview(const LLSD& session_id);
+	virtual ~LLFloaterConversationPreview(){};
+
+	virtual BOOL postBuild();
+
+	virtual void draw();
+	virtual void onOpen(const LLSD& session_id);
+
+private:
+	void appendMessage(const LLChat& chat);
+
+	LLChatHistory*	mChatHistory;
+	LLUUID			mSessionID;
+};
+
+#endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */
diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp
index 260957011e79c42fa441b76acbdf3b1e789dfb95..1b08c454b7caef9ef1e17cea63e0c34e9ea28186 100644
--- a/indra/newview/llimfloater.cpp
+++ b/indra/newview/llimfloater.cpp
@@ -60,6 +60,8 @@
 #include "llnotificationmanager.h"
 #include "llautoreplace.h"
 
+floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal;
+
 LLIMFloater::LLIMFloater(const LLUUID& session_id)
   : LLIMConversation(session_id),
 	mLastMessageIndex(-1),
@@ -771,6 +773,11 @@ void LLIMFloater::setVisible(BOOL visible)
 			chiclet->setToggleState(false);
 		}
 	}
+
+	if (visible)
+	{
+		sIMFloaterShowedSignal(mSessionID);
+	}
 }
 
 BOOL LLIMFloater::getVisible()
@@ -1340,3 +1347,8 @@ void LLIMFloater::addToHost(const LLUUID& session_id)
 	}
 	}
 }
+
+boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb)
+{
+	return LLIMFloater::sIMFloaterShowedSignal.connect(cb);
+}
diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h
index 2ac11ded20af49ab69740291a1252383c14ebf1d..7e45cf42c239c8b6d9bff90c26ccb06c39c96ff7 100644
--- a/indra/newview/llimfloater.h
+++ b/indra/newview/llimfloater.h
@@ -44,6 +44,8 @@ class LLChatHistory;
 class LLInventoryItem;
 class LLInventoryCategory;
 
+typedef boost::signals2::signal<void(const LLUUID& session_id)> floater_showed_signal_t;
+
 /**
  * Individual IM window that appears at the bottom of the screen,
  * optionally "docked" to the bottom tray.
@@ -125,7 +127,11 @@ class LLIMFloater
 
 	bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; }
 
+	static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb);
+	static floater_showed_signal_t sIMFloaterShowedSignal;
+
 private:
+
 	// process focus events to set a currently active session
 	/* virtual */ void onFocusLost();
 	/* virtual */ void onFocusReceived();
diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp
index 386afab77fe96515a5e1dbe190e9e03544671d9b..adcd840dfc7994d860b2108a26336b60dbc3b68a 100644
--- a/indra/newview/llimfloatercontainer.cpp
+++ b/indra/newview/llimfloatercontainer.cpp
@@ -107,6 +107,7 @@ BOOL LLIMFloaterContainer::postBuild()
 	p.rect = mConversationsListPanel->getLocalRect();
 	p.follows.flags = FOLLOWS_ALL;
 	p.listener = base_item;
+	p.root = NULL;
 
 	mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
 	mConversationsListPanel->addChild(mConversationsRoot);
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index ae08b7f8b9304864030f0df504cb23fefe0a6ece..d88a558125d637c44c25886cc15947d3c6bf5c39 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -175,10 +175,11 @@ LLIMModel::LLIMModel()
 	addNewMsgCallback(boost::bind(&toast_callback, _1));
 }
 
-LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice)
+LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg)
 :	mSessionID(session_id),
 	mName(name),
 	mType(type),
+	mHasOfflineMessage(has_offline_msg),
 	mParticipantUnreadMessageCount(0),
 	mNumUnread(0),
 	mOtherParticipantID(other_participant_id),
@@ -375,6 +376,8 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES
 				break;
 			}
 		}
+	default:
+		break;
 	}
 	// Update speakers list when connected
 	if (LLVoiceChannel::STATE_CONNECTED == new_state)
@@ -676,7 +679,7 @@ void LLIMModel::testMessages()
 
 //session name should not be empty
 bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, 
-						   const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice)
+						   const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg)
 {
 	if (name.empty())
 	{
@@ -690,7 +693,7 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co
 		return false;
 	}
 
-	LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice);
+	LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg);
 	mId2SessionMap[session_id] = session;
 
 	// When notifying observer, name of session is used instead of "name", because they may not be the
@@ -702,10 +705,10 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co
 
 }
 
-bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice)
+bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice, bool has_offline_msg)
 {
 	uuid_vec_t no_ids;
-	return newSession(session_id, name, type, other_participant_id, no_ids, voice);
+	return newSession(session_id, name, type, other_participant_id, no_ids, voice, has_offline_msg);
 }
 
 bool LLIMModel::clearSession(const LLUUID& session_id)
@@ -2398,6 +2401,7 @@ void LLIMMgr::addMessage(
 	const LLUUID& target_id,
 	const std::string& from,
 	const std::string& msg,
+	bool  is_offline_msg,
 	const std::string& session_name,
 	EInstantMessage dialog,
 	U32 parent_estate_id,
@@ -2423,7 +2427,7 @@ void LLIMMgr::addMessage(
 	bool new_session = !hasSession(new_session_id);
 	if (new_session)
 	{
-		LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id);
+		LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg);
 
 		// When we get a new IM, and if you are a god, display a bit
 		// of information about the source. This is to help liaisons
@@ -3307,6 +3311,7 @@ class LLViewerChatterBoxInvitation : public LLHTTPNode
 				from_id,
 				name,
 				buffer,
+				IM_OFFLINE == offline,
 				std::string((char*)&bin_bucket[0]),
 				IM_SESSION_INVITE,
 				message_params["parent_estate_id"].asInteger(),
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index 80bf315aa8518073e376ba1b645be622770e0d65..fa9d20ca5305d169655a420f8ec639611dde05ec 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -70,10 +70,11 @@ class LLIMModel :  public LLSingleton<LLIMModel>
 			GROUP_SESSION,
 			ADHOC_SESSION,
 			AVALINE_SESSION,
+			NONE_SESSION,
 		} SType;
 
 		LLIMSession(const LLUUID& session_id, const std::string& name, 
-			const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice);
+			const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg);
 		virtual ~LLIMSession();
 
 		void sessionInitReplyReceived(const LLUUID& new_session_id);
@@ -133,6 +134,8 @@ class LLIMModel :  public LLSingleton<LLIMModel>
 		//if IM session is created for a voice call
 		bool mStartedAsIMCall;
 
+		bool mHasOfflineMessage;
+
 	private:
 		void onAdHocNameCache(const LLAvatarName& av_name);
 
@@ -181,10 +184,10 @@ class LLIMModel :  public LLSingleton<LLIMModel>
 	 * @param name session name should not be empty, will return false if empty
 	 */
 	bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, 
-		const uuid_vec_t& ids, bool voice = false);
+		const uuid_vec_t& ids, bool voice = false, bool has_offline_msg = false);
 
 	bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type,
-		const LLUUID& other_participant_id, bool voice = false);
+		const LLUUID& other_participant_id, bool voice = false, bool has_offline_msg = false);
 
 	/**
 	 * Remove all session data associated with a session specified by session_id
@@ -325,6 +328,7 @@ class LLIMMgr : public LLSingleton<LLIMMgr>
 					const LLUUID& target_id,
 					const std::string& from,
 					const std::string& msg,
+					bool  is_offline_msg = false,
 					const std::string& session_name = LLStringUtil::null,
 					EInstantMessage dialog = IM_NOTHING_SPECIAL,
 					U32 parent_estate_id = 0,
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 6a7ee3b6a053c4a1936be4ea4325c86a96e70169..be1cd2510da42fe5e5969b259f7cb319e35ee284 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -659,6 +659,7 @@ LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool u
 	p.allow_multiselect = mAllowMultiSelect;
 	p.show_empty_message = mShowEmptyMessage;
 	p.show_item_link_overlays = mShowItemLinkOverlays;
+	p.root = NULL;
 
 	return LLUICtrlFactory::create<LLFolderView>(p);
 }
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index 5887f4d244d2cc5f8fe553446f190c7895425b75..4f2c515bde66623ea187efa4e0b12c311f8a6a46 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -1563,6 +1563,7 @@ void LLPanelObjectInventory::reset()
 	p.listener = LLTaskInvFVBridge::createObjectBridge(this, NULL);
 	p.folder_indentation = -14; // subtract space normally reserved for folder expanders
 	p.view_model = &mInventoryViewModel;
+	p.root = NULL;
 	mFolders = LLUICtrlFactory::create<LLFolderView>(p);
 	// this ensures that we never say "searching..." or "no items found"
 	//TODO RN: make this happen by manipulating filter object directly
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 0b70184920c7426dbf57e996ec818a0b5ce71584..68b4063df9c134491e14891fb0f0198e420161e3 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -94,6 +94,7 @@
 #include "llcallingcard.h"
 #include "llconsole.h"
 #include "llcontainerview.h"
+#include "llconversationlog.h"
 #include "lldebugview.h"
 #include "lldrawable.h"
 #include "lleventnotifier.h"
@@ -1268,6 +1269,8 @@ bool idle_startup()
 		display_startup();
 		LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT );
 		
+		LLConversationLog::getInstance();
+
 		return FALSE;
 	}
 
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index e45426a8d6bbfeabe2e82dcf65a1bc42e4ffcccc..795c171d2835f1626c5886d752c701690e2bea19 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -50,6 +50,8 @@
 #include "llfloaterbump.h"
 #include "llfloaterbvhpreview.h"
 #include "llfloatercamera.h"
+#include "llfloaterconversationlog.h"
+#include "llfloaterconversationpreview.h"
 #include "llfloaterdeleteenvpreset.h"
 #include "llfloaterdisplayname.h"
 #include "llfloatereditdaycycle.h"
@@ -189,6 +191,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>);
 	LLFloaterReg::add("chat_bar", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChat>);
 	LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>);
+	LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>);
 
 	LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>);
 
@@ -262,6 +265,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("picks", "floater_picks.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
 	LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterJoystick>);
 	LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewAnim>, "preview");
+	LLFloaterReg::add("preview_conversation", "floater_conversation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationPreview>);
 	LLFloaterReg::add("preview_gesture", "floater_preview_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewGesture>, "preview");
 	LLFloaterReg::add("preview_notecard", "floater_preview_notecard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewNotecard>, "preview");
 	LLFloaterReg::add("preview_script", "floater_script_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewLSL>, "preview");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index d03a58e9e3a8ee5e03becc54f18268254b5c426a..f4a3f31ba5100bab82ae0b5f403cd2f1ef4d42c4 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2438,6 +2438,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
 				from_id,
 				name,
 				buffer,
+				IM_OFFLINE == offline,
 				LLStringUtil::null,
 				dialog,
 				parent_estate_id,
@@ -2477,7 +2478,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
 				if (!gIMMgr->isNonFriendSessionNotified(session_id))
 				{
 					std::string message = LLTrans::getString("IM_unblock_only_groups_friends");
-					gIMMgr->addMessage(session_id, from_id, name, message);
+					gIMMgr->addMessage(session_id, from_id, name, message, IM_OFFLINE == offline);
 					gIMMgr->addNotifiedNonFriendSessionID(session_id);
 				}
 
@@ -2490,6 +2491,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
 					from_id,
 					name,
 					buffer,
+					IM_OFFLINE == offline,
 					LLStringUtil::null,
 					dialog,
 					parent_estate_id,
@@ -2830,6 +2832,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
 			from_id,
 			name,
 			buffer,
+			IM_OFFLINE == offline,
 			ll_safe_string((char*)binary_bucket),
 			IM_SESSION_INVITE,
 			parent_estate_id,
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 820d1d73e149434cddde9f7f8bd9bc72b6baf63c..f1bf4a6d75d81a182eeb9eccdcfa4d8149f035a3 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -3964,6 +3964,7 @@ void LLVivoxVoiceClient::messageEvent(
 						session->mCallerID,
 						session->mName.c_str(),
 						message.c_str(),
+						false,
 						LLStringUtil::null,		// default arg
 						IM_NOTHING_SPECIAL,		// default arg
 						0,						// default arg
diff --git a/indra/newview/skins/default/xui/en/floater_conversation_log.xml b/indra/newview/skins/default/xui/en/floater_conversation_log.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9cdeb0d788ff6ead48be727eb3af39b256d0910c
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_conversation_log.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<floater
+  can_resize="true"
+  positioning="cascading"
+  height="400"
+  min_height="100"
+  min_width="390"
+  layout="topleft"
+  name="floater_conversation_log"
+  save_rect="true"
+  single_instance="true"
+  reuse_instance="true"
+  title="CONVERSATION LOG"
+  width="450">
+	<panel
+      follows="left|top|right"
+      height="27"
+      layout="topleft"
+      left="0"
+      name="buttons_panel"
+      top="0">
+        <filter_editor
+          follows="left|top|right"
+          height="23"
+          layout="topleft"
+          left="8"
+          label="Filter People"
+          max_length_chars="300"
+          name="people_filter_input"
+          text_color="Black"
+          text_pad_left="10"
+          top="4"
+          width="364" />
+        <menu_button
+          follows="right"
+          height="25"
+          image_hover_unselected="Toolbar_Middle_Over"
+          image_overlay="Conv_toolbar_sort"
+          image_selected="Toolbar_Middle_Selected"
+          image_unselected="Toolbar_Middle_Off"
+          layout="topleft"
+          left_pad="5"
+          menu_filename="menu_conversation_log_view.xml"
+          menu_position="bottomleft"
+          name="conversation_view_btn"
+          top="3"
+          width="31" />
+        <menu_button
+          follows="right"
+          height="25"
+          image_hover_unselected="Toolbar_Middle_Over"
+          image_overlay="OptionsMenu_Off"
+          image_selected="Toolbar_Middle_Selected"
+          image_unselected="Toolbar_Middle_Off"
+          layout="topleft"
+          left_pad="2"
+          name="conversations_gear_btn"
+          top="3"
+          width="31" />
+    </panel>
+    <panel
+      follows="all"
+      height="370"
+      layout="topleft"
+      left="5"
+      name="buttons_panel"
+      right="-5"
+      top_pad="5">
+    <conversation_log_list
+      opaque="true"
+      allow_select="true"
+      follows="all"
+      height="360"
+      layout="topleft"
+      left="3"
+      keep_selection_visible_on_reshape="true"
+      item_pad="2"
+      multi_select="false"
+      name="conversation_log_list"
+      right="-3"
+      top="5" />
+    </panel>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_conversation_preview.xml b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..27b744aefbe53a40ed3edadce708ed0fb9854e78
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_resize="true"
+ default_tab_group="1"
+ height="361"
+ layout="topleft"
+ min_height="243"
+ min_width="234"
+ name="preview_conversation"
+ title="CONVERSATION:"
+ width="400">
+    <floater.string
+     name="Title">
+        CONVERSATION: [NAME]
+    </floater.string>
+    <text
+     type="string"
+     length="1"
+     follows="left|top"
+     font="SansSerif"
+     height="19"
+     layout="topleft"
+     left="10"
+     name="desc txt"
+     top="22"
+     width="90">
+        Description:
+    </text>
+    <line_editor
+     border_style="line"
+     border_thickness="1"
+     enabled="false"
+     follows="left|top|right"
+     font="SansSerif"
+     height="22"
+     layout="topleft"
+     left_pad="0"
+     max_length_bytes="127"
+     name="description"
+     width="296" />
+    <chat_history
+     font="SansSerifSmall"
+     follows="all"
+     visible="true"
+     height="310"
+     name="chat_history"
+     parse_highlights="true"
+     parse_urls="true"
+     left="5"
+     width="390">
+    </chat_history>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml
index e5ef80e352ca49a795245f85196dd387e9c18653..e8ef3c1df92516eded3b17799031487f1c92be68 100644
--- a/indra/newview/skins/default/xui/en/floater_im_container.xml
+++ b/indra/newview/skins/default/xui/en/floater_im_container.xml
@@ -56,6 +56,7 @@
                      image_overlay="Conv_toolbar_sort"
                      image_selected="Toolbar_Middle_Selected"
                      image_unselected="Toolbar_Middle_Off"
+                     menu_filename="menu_participant_view.xml"
                      layout="topleft"
                      left="10"
                      name="sort_btn"
diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b8d0eef956de1dde6f1512ea864b4de118454f9e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ layout="topleft"
+ name="Conversation Context Menu">
+    <menu_item_call
+     label="IM..."
+     layout="topleft"
+     name="IM">
+        <on_click
+         function="Calllog.Action"
+         parameter="im" />
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_im" />
+    </menu_item_call>
+    <menu_item_call
+     label="Voice call..."
+     layout="topleft"
+     name="Call">
+        <on_click
+         function="Calllog.Action"
+         parameter="call" />
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_call" />
+    </menu_item_call>
+    <menu_item_call
+     label="Open chat history..."
+     layout="topleft"
+     name="Chat history">
+        <on_click
+         function="Calllog.Action"
+         parameter="chat_history" />
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_view_chat_history" />
+    </menu_item_call>
+    <menu_item_call
+     label="View Profile"
+     layout="topleft"
+     name="View Profile">
+        <on_click
+         function="Calllog.Action"
+         parameter="view_profile" />
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_view_profile" />
+    </menu_item_call>
+    <menu_item_call
+    label="Offer Teleport"
+    name="teleport">
+      <on_click
+       function="Calllog.Action"
+       parameter="offer_teleport"/>
+      <on_enable
+      function="Calllog.Enable"
+      parameter="can_offer_teleport"/>
+    </menu_item_call>
+    <menu_item_separator />
+    <menu_item_check
+     label="Add friend/Remove friend"
+     layout="topleft"
+     name="Friend_add_remove">
+        <menu_item_check.on_click
+         function="Calllog.Action"
+         parameter="add_rem_friend" />
+        <menu_item_check.on_check
+         function="Calllog.Check"
+         parameter="is_friend" />
+        <menu_item_check.on_enable
+         function="Calllog.Enable"
+         parameter="add_rem_friend" />
+    </menu_item_check>
+    <menu_item_call
+     label="Invite to group..."
+     layout="topleft"
+     name="Invite">
+        <on_click
+         function="Calllog.Action"
+         parameter="invite_to_group"/>
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_invite_to_group" />
+    </menu_item_call>
+    <menu_item_separator />
+    <menu_item_call
+     label="Map"
+     layout="topleft"
+     name="Map">
+        <on_click
+         function="Calllog.Action"
+         parameter="show_on_map" />
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_show_on_map" />
+    </menu_item_call>
+    <menu_item_call
+     label="Share"
+     layout="topleft"
+     name="Share">
+        <on_click
+         function="Calllog.Action"
+         parameter="share" />
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_share" />
+    </menu_item_call>
+    <menu_item_call
+     label="Pay"
+     layout="topleft"
+     name="Pay">
+        <on_click
+         function="Calllog.Action"
+         parameter="pay" />
+        <on_enable
+         function="Calllog.Enable"
+         parameter="can_pay" />
+    </menu_item_call>
+    <menu_item_check
+     label="Block/Unblock"
+     layout="topleft"
+     name="Block/Unblock">
+        <menu_item_check.on_click
+         function="Calllog.Action"
+         parameter="block"/>
+        <menu_item_check.on_check
+         function="Calllog.Check"
+         parameter="is_blocked" />
+        <menu_item_check.on_enable
+         function="Calllog.Enable"
+         parameter="can_block" />
+    </menu_item_check>
+
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4ab8cb4f7d0610c946fe54de26ad198ba3d4af8b
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+     name="menu_conversation_view"
+     left="0" bottom="0" visible="false"
+     mouse_opaque="false">
+  <menu_item_check
+   label="Sort by name"
+   name="sort_by_name">
+      <on_click
+       function="CallLog.Action"
+       parameter="sort_by_name"/>
+      <on_check
+       function="CallLog.Check"
+       parameter="sort_by_name"/>
+  </menu_item_check>
+  <menu_item_check
+   label="Sort by date"
+   name="sort_by_date">
+      <on_click
+       function="CallLog.Action"
+       parameter="sort_by_date" />
+      <on_check
+       function="CallLog.Check"
+       parameter="sort_by_date" />
+  </menu_item_check>
+  <menu_item_separator />
+  <menu_item_check
+   label="Sort friends on top"
+   name="sort_by_friends">
+      <on_click
+       function="CallLog.Action"
+       parameter="sort_friends_on_top" />
+      <on_check
+       function="CallLog.Check"
+       parameter="sort_friends_on_top" />
+  </menu_item_check>
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_participant_view.xml b/indra/newview/skins/default/xui/en/menu_participant_view.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6401b0e3b7eeaec91acee7bf00fb59193c7ddfb0
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_participant_view.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ layout="topleft"
+ name="participant_manu_view">
+    <menu_item_check
+         label="Open conversation log"
+         name="Conversation"
+         visible="true">
+        <menu_item_check.on_check
+         function="Floater.Visible"
+         parameter="conversation" />
+        <menu_item_check.on_click
+         function="Floater.Toggle"
+         parameter="conversation" />
+      </menu_item_check>
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3c98e32e7dfc72006155f6662f90ee01bda59e1c
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ follows="top|right|left"
+ height="23"
+ layout="topleft"
+ left="0"
+ name="conversation_log_list_item"
+ top="0"
+ width="380">
+    <icon
+     height="24"
+     follows="top|right|left"
+     image_name="ListItem_Select"
+     layout="topleft"
+     left="0"
+     name="selected_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     follows="top|right|left"
+     height="24"
+     image_name="ListItem_Over"
+     layout="topleft"
+     left="0"
+     name="hovered_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     default_icon_name="voice_session_icon"
+     follows="top|left"
+     height="20"
+     layout="topleft"
+     left="5"
+     image_name="Audio_Press"
+     mouse_opaque="true"
+     name="voice_session_icon"
+     top="2"
+     visible="false"
+     width="20" />
+    <icon
+     default_icon_name="incoming_unread_im_icon"
+     follows="top|left"
+     height="20"
+     layout="topleft"
+     left="5"
+     image_name="Movement_Backward_Off"
+     mouse_opaque="false"
+     name="unread_ims_icon"
+     top="2"
+     visible="false"
+     width="20" />
+    <avatar_icon
+     default_icon_name="Generic_Person"
+     follows="top|left"
+     height="20"
+     layout="topleft"
+     left_pad="5"
+     mouse_opaque="true"
+     top="2"
+     visible="false"
+     width="20" />
+    <group_icon
+     default_icon_name="Generic_Group"
+     follows="top|left"
+     height="20"
+     layout="topleft"
+     mouse_opaque="true"
+     top="2"
+     visible="false"
+     width="20" />
+    <text
+     follows="left|right"
+     font="SansSerifSmall"
+     font.color="DkGray"
+     height="15"
+     layout="topleft"
+     left_pad="5"
+     name="conversation_name"
+     parse_urls="false"
+     top="6"
+     use_ellipses="true"
+     width="180" />
+    <text
+     follows="right"
+     font="SansSerifSmall"
+     font.color="DkGray"
+     height="15"
+     layout="topleft"
+     left_pad="5"
+     name="date_time"
+     parse_urls="false"
+     top="6"
+     use_ellipses="true"
+     width="110"/>
+    <button
+     name="delete_btn"
+     layout="topleft"
+     follows="top|right"
+     image_unselected="Toast_CloseBtn"
+     image_selected="Toast_CloseBtn"
+     top="5"
+     left_pad="0"
+     height="14"
+     width="14"
+     tab_stop="false"/>
+</panel>
\ No newline at end of file