1. simon_linden
  2. viewer-rabbit

Commits

Bryan O'Sullivan  committed e23b581 Merge

Merge

  • Participants
  • Parent commits abd2459, 16e39be
  • Branches default

Comments (0)

Files changed (179)

File indra/integration_tests/llui_libtest/CMakeLists.txt

View file
 include(LLImage)
 include(LLImageJ2COJ)   # ugh, needed for images
 include(LLMath)
+include(LLMessage)
 include(LLRender)
 include(LLWindow)
 include(LLUI)
 # Sort by high-level to low-level
 target_link_libraries(llui_libtest
     llui
+    llmessage
     ${OS_LIBRARIES}
     ${GOOGLE_PERFTOOLS_LIBRARIES}
     )

File indra/linux_updater/linux_updater.cpp

View file
 
 	// extract paths string vector from comma-delimited flat string
 	std::vector<std::string> paths;
-	LLStringUtil::getTokens(comma_delim_path_list, paths); // split over ','
+	LLStringUtil::getTokens(comma_delim_path_list, paths, ","); // split over ','
 
 	// suck the translation xml files into memory
 	LLXMLNodePtr root;

File indra/llaudio/CMakeLists.txt

View file
     if (LINUX)
       if (${CXX_VERSION} MATCHES "4.[23]")
         set_source_files_properties(llaudioengine_fmod.cpp
-                                    COMPILE_FLAGS -Wno-error=write-strings)
+                                    llstreamingaudio_fmod.cpp
+                                    COMPILE_FLAGS -Wno-write-strings)
       endif (${CXX_VERSION} MATCHES "4.[23]")
     endif (LINUX)
 endif (FMOD)

File indra/llcommon/llchat.h

View file
 	CHAT_AUDIBLE_FULLY = 1
 } EChatAudible;
 
+typedef enum e_chat_style
+{
+	CHAT_STYLE_NORMAL,
+	CHAT_STYLE_IRC
+}EChatStyle;
+
 // A piece of chat
 class LLChat
 {
 		mMuted(FALSE),
 		mTime(0.0),
 		mPosAgent(),
-		mURL()
+		mURL(),
+		mChatStyle(CHAT_STYLE_NORMAL)
 	{ }
 	
 	std::string		mText;		// UTF-8 line of text
 	F64				mTime;		// viewer only, seconds from viewer start
 	LLVector3		mPosAgent;
 	std::string		mURL;
+	EChatStyle		mChatStyle;
 };
 
 #endif

File indra/llcommon/lldate.cpp

View file
 	return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
 }
 
+LLFastTimer::DeclareTimer FT_DATE_FORMAT("Date Format");
+
 std::string LLDate::toHTTPDateString (std::string fmt) const
 {
+	LLFastTimer ft1(FT_DATE_FORMAT);
+	
 	std::ostringstream stream;
 	time_t locSeconds = (time_t) mSecondsSinceEpoch;
 	struct tm * gmt = gmtime (&locSeconds);
 
 std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
 {
+	LLFastTimer ft1(FT_DATE_FORMAT);
+	
 	std::ostringstream stream;
 	stream.imbue (std::locale(LLStringUtil::getLocale().c_str()));
 	toHTTPDateStream (stream, gmt, fmt);
 
 void LLDate::toHTTPDateStream(std::ostream& s, tm * gmt, std::string fmt)
 {
-	using namespace std;
+	LLFastTimer ft1(FT_DATE_FORMAT);
 
 	const char * pBeg = fmt.c_str();
 	const char * pEnd = pBeg + fmt.length();
-	const time_put<char>& tp = use_facet<time_put<char> >(s.getloc());
+	const std::time_put<char>& tp = std::use_facet<std::time_put<char> >(s.getloc());
 	tp.put (s, s, s.fill(), gmt, pBeg, pEnd);
 }
 

File indra/llcommon/llstring.cpp

View file
 #include <winnls.h> // for WideCharToMultiByte
 #endif
 
-LLFastTimer::DeclareTimer STRING_LOCALIZATION("String Localization");
+LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format");
 
 
 std::string ll_safe_string(const char* in)
 	// https://wiki.lindenlab.com/wiki/Unicode_Guidelines has details on
 	// allowable code points for XML. Specifically, they are:
 	// 0x09, 0x0a, 0x0d, and 0x20 on up.  JC
-	std::string strip_invalid_xml(const std::string& input)
+	std::string strip_invalid_xml(const std::string& instr)
 	{
 		std::string output;
-		output.reserve( input.size() );
-		std::string::const_iterator it = input.begin();
-		while (it != input.end())
+		output.reserve( instr.size() );
+		std::string::const_iterator it = instr.begin();
+		while (it != instr.end())
 		{
 			// Must compare as unsigned for >=
 			// Test most likely match first
 	}
 }
 
+////////////////////////////////////////////////////////////
+
+//static
+template<> 
+void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims)
+{
+	std::string currToken;
+	std::string::size_type begIdx, endIdx;
+
+	begIdx = instr.find_first_not_of (delims);
+	while (begIdx != std::string::npos)
+	{
+		endIdx = instr.find_first_of (delims, begIdx);
+		if (endIdx == std::string::npos)
+		{
+			endIdx = instr.length();
+		}
+
+		currToken = instr.substr(begIdx, endIdx - begIdx);
+		LLStringUtil::trim (currToken);
+		tokens.push_back(currToken);
+		begIdx = instr.find_first_not_of (delims, endIdx);
+	}
+}
+
+template<> 
+LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr, size_type& start, std::vector<std::string>& tokens)
+{
+	const std::string delims (",");
+	
+	// Find the first ]
+	size_type pos2 = instr.find(']', start);
+	if (pos2 == std::string::npos)
+		return std::string::npos;
+
+	// Find the last [ before ]
+	size_type pos1 = instr.find_last_of('[', pos2-1);
+	if (pos1 == std::string::npos || pos1 < start)
+		return std::string::npos;
+	
+	getTokens(std::string(instr,pos1+1,pos2-pos1-1), tokens, delims);
+	start = pos2+1;
+	
+	return pos1;
+}
+
+// static
+template<> 
+bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const format_map_t& substitutions)
+{
+	// see if we have a replacement for the bracketed string (without the brackets)
+	// test first using has() because if we just look up with operator[] we get back an
+	// empty string even if the value is missing. We want to distinguish between 
+	// missing replacements and deliberately empty replacement strings.
+	format_map_t::const_iterator iter = substitutions.find(token);
+	if (iter != substitutions.end())
+	{
+		replacement = iter->second;
+		return true;
+	}
+	// if not, see if there's one WITH brackets
+	iter = substitutions.find(std::string("[" + token + "]"));
+	if (iter != substitutions.end())
+	{
+		replacement = iter->second;
+		return true;
+	}
+
+	return false;
+}
+
+// static
+template<> 
+bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const LLSD& substitutions)
+{
+	// see if we have a replacement for the bracketed string (without the brackets)
+	// test first using has() because if we just look up with operator[] we get back an
+	// empty string even if the value is missing. We want to distinguish between 
+	// missing replacements and deliberately empty replacement strings.
+	if (substitutions.has(token))
+	{
+		replacement = substitutions[token].asString();
+		return true;
+	}
+	// if not, see if there's one WITH brackets
+	else if (substitutions.has(std::string("[" + token + "]")))
+	{
+		replacement = substitutions[std::string("[" + token + "]")].asString();
+		return true;
+	}
+
+	return false;
+}
+
+// static
+template<> 
+void LLStringUtil::formatNumber(std::string& numStr, std::string decimals)
+{
+	std::stringstream strStream;
+	S32 intDecimals = 0;
+
+	convertToS32 (decimals, intDecimals);
+	if (!sLocale.empty())
+	{
+		strStream.imbue (std::locale(sLocale.c_str()));
+	}
+
+	if (!intDecimals)
+	{
+		S32 intStr;
+
+		if (convertToS32(numStr, intStr))
+		{
+			strStream << intStr;
+			numStr = strStream.str();
+		}
+	}
+	else
+	{
+		F32 floatStr;
+
+		if (convertToF32(numStr, floatStr))
+		{
+			strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr;
+			numStr = strStream.str();
+		}
+	}
+}
+
+// static
+template<> 
+bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
+								  std::string param, S32 secFromEpoch)
+{
+	if (param == "local")   // local
+	{
+		secFromEpoch -= LLStringOps::getLocalTimeOffset();
+	}
+	else if (param != "utc") // slt
+	{
+		secFromEpoch -= LLStringOps::getSltOffset();
+	}
+		
+	// if never fell into those two ifs above, param must be utc
+	if (secFromEpoch < 0) secFromEpoch = 0;
+
+	LLDate * datetime = new LLDate((F64)secFromEpoch);
+	std::string code = LLStringOps::getDatetimeCode (token);
+
+	// special case to handle timezone
+	if (code == "%Z") {
+		if (param == "utc") replacement = "GMT";
+		else if (param != "local") replacement = LLStringOps::getDaylightSavings()? "PDT" : "PST";
+		return true;
+	}
+	replacement = datetime->toHTTPDateString(code);
+
+	if (code.empty())
+	{
+		return false;
+	}
+	else
+	{
+		return true;
+	}
+}
+
+// LLStringUtil::format recogizes the following patterns.
+// All substitutions *must* be encased in []'s in the input string.
+// The []'s are optional in the substitution map.
+// [FOO_123]
+// [FOO,number,precision]
+// [FOO,datetime,format]
+
+
+// static
+template<> 
+S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
+{
+	LLFastTimer ft(FT_STRING_FORMAT);
+	S32 res = 0;
+
+	std::string output;
+	std::vector<std::string> tokens;
+
+	std::string::size_type start = 0;
+	std::string::size_type prev_start = 0;
+	std::string::size_type key_start = 0;
+	while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos)
+	{
+		output += std::string(s, prev_start, key_start-prev_start);
+		prev_start = start;
+		
+		bool found_replacement = false;
+		std::string replacement;
+
+		if (tokens.size() == 1)
+		{
+			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+		}
+		else if (tokens[1] == "number")
+		{
+			std::string param = "0";
+
+			if (tokens.size() > 2) param = tokens[2];
+			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+			if (found_replacement) formatNumber (replacement, param);
+		}
+		else if (tokens[1] == "datetime")
+		{
+			std::string param;
+			if (tokens.size() > 2) param = tokens[2];
+			
+			format_map_t::const_iterator iter = substitutions.find("datetime");
+			if (iter != substitutions.end())
+			{
+				S32 secFromEpoch = 0;
+				BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch);
+				if (r)
+				{
+					found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch);
+				}
+			}
+		}
+
+		if (found_replacement)
+		{
+			output += replacement;
+			res++;
+		}
+		else
+		{
+			// we had no replacement, so leave the string we searched for so that it gets noticed by QA
+			// "hello [NAME_NOT_FOUND]" is output
+			output += std::string("[") + tokens[0] + std::string("]");
+		}
+		tokens.clear();
+	}
+	// send the remainder of the string (with no further matches for bracketed names)
+	output += std::string(s, start);
+	s = output;
+	return res;
+}
+
+//static
+template<> 
+S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
+{
+	LLFastTimer ft(FT_STRING_FORMAT);
+	S32 res = 0;
+
+	if (!substitutions.isMap()) 
+	{
+		return res;
+	}
+
+	std::string output;
+	std::vector<std::string> tokens;
+
+	std::string::size_type start = 0;
+	std::string::size_type prev_start = 0;
+	std::string::size_type key_start = 0;
+	while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos)
+	{
+		output += std::string(s, prev_start, key_start-prev_start);
+		prev_start = start;
+		
+		bool found_replacement = false;
+		std::string replacement;
+
+		if (tokens.size() == 1)
+		{
+			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+		}
+		else if (tokens[1] == "number")
+		{
+			std::string param = "0";
+
+			if (tokens.size() > 2) param = tokens[2];
+			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+			if (found_replacement) formatNumber (replacement, param);
+		}
+		else if (tokens[1] == "datetime")
+		{
+			std::string param;
+			if (tokens.size() > 2) param = tokens[2];
+			
+			S32 secFromEpoch = (S32) substitutions["datetime"].asInteger();
+			found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch);
+		}
+
+		if (found_replacement)
+		{
+			output += replacement;
+			res++;
+		}
+		else
+		{
+			// we had no replacement, so leave the string we searched for so that it gets noticed by QA
+			// "hello [NAME_NOT_FOUND]" is output
+			output += std::string("[") + tokens[0] + std::string("]");
+		}
+		tokens.clear();
+	}
+	// send the remainder of the string (with no further matches for bracketed names)
+	output += std::string(s, start);
+	s = output;
+	return res;
+}
 
 ////////////////////////////////////////////////////////////
 // Testing

File indra/llcommon/llstring.h

View file
 	static std::basic_string<T> null;
 	
 	typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
-	static void getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens);
+	static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
+	static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
 	static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
 	static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
 	static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
 }
 
 ////////////////////////////////////////////////////////////
+// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
+// There is no LLWStringUtil::format implementation currently.
+// Calling thse for anything other than LLStringUtil will produce link errors.
 
-//static
-template<class T>
-void LLStringUtilBase<T>::getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens)
-{
-	const std::basic_string<T> delims (",");
-	std::basic_string<T> currToken;
-	size_type begIdx, endIdx;
+////////////////////////////////////////////////////////////
 
-	begIdx = input.find_first_not_of (delims);
-	while (begIdx != std::basic_string<T>::npos)
-	{
-		endIdx = input.find_first_of (delims, begIdx);
-		if (endIdx == std::basic_string<T>::npos)
-		{
-			endIdx = input.length();
-		}
-
-		currToken = input.substr(begIdx, endIdx - begIdx);
-		trim (currToken);
-		tokens.push_back(currToken);
-		begIdx = input.find_first_not_of (delims, endIdx);
-	}
-}
-
-extern LLFastTimer::DeclareTimer STRING_LOCALIZATION;
-
-// static
-template<class T> 
-S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& substitutions)
-{
-	LLFastTimer ft(STRING_LOCALIZATION);
-	S32 res = 0;
-
-	std::basic_ostringstream<T> output;
-	// match strings like [NAME,number,3]
-	const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]");
-
-
-	typename std::basic_string<T>::const_iterator start = s.begin();
-	typename std::basic_string<T>::const_iterator end = s.end();
-	boost::smatch match;
-	
-
-	while (boost::regex_search(start, end, match, key, boost::match_default))
-	{
-		bool found_replacement = false;
-		std::vector<std::basic_string<T> > tokens;
-		std::basic_string<T> replacement;
-
-		getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens);
-
-		if (tokens.size() == 1)
-		{
-			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
-		}
-		else if (tokens[1] == "number")
-		{
-			std::basic_string<T> param = "0";
-
-			if (tokens.size() > 2) param = tokens[2];
-			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
-			if (found_replacement) formatNumber (replacement, param);
-		}
-		else if (tokens[1] == "datetime")
-		{
-			std::basic_string<T> param;
-			if (tokens.size() > 2) param = tokens[2];
-			
-			format_map_t::const_iterator iter = substitutions.find("datetime");
-			if (iter != substitutions.end())
-			{
-				S32 secFromEpoch = 0;
-				BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch);
-				if (r)
-				{
-					found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch);
-				}
-			}
-		}
-
-		if (found_replacement)
-		{
-			output << std::basic_string<T>(start, match[0].first) << replacement;
-			res++;
-		}
-		else
-		{
-			// we had no replacement, so leave the string we searched for so that it gets noticed by QA
-			// "hello [NAME_NOT_FOUND]" is output
-			output << std::basic_string<T>(start, match[0].second);
-		}
-		
-		// update search position 
-		start = match[0].second; 
-	}
-	// send the remainder of the string (with no further matches for bracketed names)
-	output << std::basic_string<T>(start, end);
-	s = output.str();
-	return res;
-}
-
-//static
-template<class T>
-S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutions)
-{
-	LLFastTimer ft(STRING_LOCALIZATION);
-
-	S32 res = 0;
-
-	if (!substitutions.isMap()) 
-	{
-		return res;
-	}
-
-	std::basic_ostringstream<T> output;
-	// match strings like [NAME,number,3]
-	const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]");
-
-
-	typename std::basic_string<T>::const_iterator start = s.begin();
-	typename std::basic_string<T>::const_iterator end = s.end();
-	boost::smatch match;
-	
-
-	while (boost::regex_search(start, end, match, key, boost::match_default))
-	{
-		bool found_replacement = false;
-		std::vector<std::basic_string<T> > tokens;
-		std::basic_string<T> replacement;
-
-		getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens);
-
-		if (tokens.size() == 1)
-		{
-			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
-		}
-		else if (tokens[1] == "number")
-		{
-			std::basic_string<T> param = "0";
-
-			if (tokens.size() > 2) param = tokens[2];
-			found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
-			if (found_replacement) formatNumber (replacement, param);
-		}
-		else if (tokens[1] == "datetime")
-		{
-			std::basic_string<T> param;
-			if (tokens.size() > 2) param = tokens[2];
-			
-			S32 secFromEpoch = (S32) substitutions["datetime"].asInteger();
-			found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch);
-		}
-
-		if (found_replacement)
-		{
-			output << std::basic_string<T>(start, match[0].first) << replacement;
-			res++;
-		}
-		else
-		{
-			// we had no replacement, so leave the string we searched for so that it gets noticed by QA
-			// "hello [NAME_NOT_FOUND]" is output
-			output << std::basic_string<T>(start, match[0].second);
-		}
-		
-		// update search position 
-		start = match[0].second; 
-	}
-	// send the remainder of the string (with no further matches for bracketed names)
-	output << std::basic_string<T>(start, end);
-	s = output.str();
-	return res;
-}
-
-// static
-template<class T>
-bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const format_map_t& substitutions)
-{
-	// see if we have a replacement for the bracketed string (without the brackets)
-	// test first using has() because if we just look up with operator[] we get back an
-	// empty string even if the value is missing. We want to distinguish between 
-	// missing replacements and deliberately empty replacement strings.
-	format_map_t::const_iterator iter = substitutions.find(token);
-	if (iter != substitutions.end())
-	{
-		replacement = iter->second;
-		return true;
-	}
-	// if not, see if there's one WITH brackets
-	iter = substitutions.find(std::basic_string<T>("[" + token + "]"));
-	if (iter != substitutions.end())
-	{
-		replacement = iter->second;
-		return true;
-	}
-
-	return false;
-}
-
-// static
-template<class T>
-bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const LLSD& substitutions)
-{
-	// see if we have a replacement for the bracketed string (without the brackets)
-	// test first using has() because if we just look up with operator[] we get back an
-	// empty string even if the value is missing. We want to distinguish between 
-	// missing replacements and deliberately empty replacement strings.
-	if (substitutions.has(token))
-	{
-		replacement = substitutions[token].asString();
-		return true;
-	}
-	// if not, see if there's one WITH brackets
-	else if (substitutions.has(std::basic_string<T>("[" + token + "]")))
-	{
-		replacement = substitutions[std::basic_string<T>("[" + token + "]")].asString();
-		return true;
-	}
-
-	return false;
-}
-
-// static
-template<class T>
-void LLStringUtilBase<T>::formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals)
-{
-	typedef typename std::basic_string<T>::size_type string_size_type_t;
-	std::basic_stringstream<T> strStream;
-	S32 intDecimals = 0;
-
-	convertToS32 (decimals, intDecimals);
-	if (!sLocale.empty())
-	{
-		strStream.imbue (std::locale(sLocale.c_str()));
-	}
-
-	if (!intDecimals)
-	{
-		S32 intStr;
-
-		if (convertToS32(numStr, intStr))
-		{
-			strStream << intStr;
-			numStr = strStream.str();
-		}
-	}
-	else
-	{
-		F32 floatStr;
-
-		if (convertToF32(numStr, floatStr))
-		{
-			strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr;
-			numStr = strStream.str();
-		}
-	}
-}
-
-// static
-template<class T>
-bool LLStringUtilBase<T>::formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token,
-										 std::basic_string<T> param, S32 secFromEpoch)
-{
-	if (param == "local")   // local
-	{
-		secFromEpoch -= LLStringOps::getLocalTimeOffset();
-	}
-	else if (param != "utc") // slt
-	{
-		secFromEpoch -= LLStringOps::getSltOffset();
-	}
-		
-	// if never fell into those two ifs above, param must be utc
-	if (secFromEpoch < 0) secFromEpoch = 0;
-
-	LLDate * datetime = new LLDate((F64)secFromEpoch);
-	std::string code = LLStringOps::getDatetimeCode (token);
-
-	// special case to handle timezone
-	if (code == "%Z") {
-		if (param == "utc") replacement = "GMT";
-		else if (param != "local") replacement = LLStringOps::getDaylightSavings()? "PDT" : "PST";
-		return true;
-	}
-
-	replacement = datetime->toHTTPDateString(code);
-	if (code.empty())
-	{
-		return false;
-	}
-	else
-	{
-		return true;
-	}
-}
 
 // static
 template<class T> 

File indra/llinventory/llinventory.h

View file
 	BOOL getIsLinkType() const;
 	// mutators - will not call updateServer();
 	void setUUID(const LLUUID& new_uuid);
-	void rename(const std::string& new_name);
+	virtual void rename(const std::string& new_name);
 	void setParent(const LLUUID& new_parent);
 	void setType(LLAssetType::EType type);
 

File indra/llrender/llfontregistry.cpp

View file
 	size_t pos = str.find(substr);
 	if (pos != string::npos)
 	{
-		str.erase(pos);
+		str.erase(pos, substr.size());
 		return true;
 	}
 	return false;

File indra/llui/CMakeLists.txt

View file
     llconsole.cpp
     llcontainerview.cpp
     llctrlselectioninterface.cpp
+    lldockablefloater.cpp
+    lldockcontrol.cpp
     lldraghandle.cpp
     lleditmenuhandler.cpp
     llf32uictrl.cpp
     llkeywords.cpp
     lllayoutstack.cpp
     lllineeditor.cpp
-    lllink.cpp
+    lllistctrl.cpp
     llmenugl.cpp
     llmodaldialog.cpp
     llmultifloater.cpp 
     llstatview.cpp
     llstyle.cpp
     lltabcontainer.cpp
+    lltextbase.cpp
     lltextbox.cpp
     lltexteditor.cpp
     lltextparser.cpp
     lluiimage.cpp
     lluistring.cpp
     llundo.cpp
+    llurlaction.cpp
+    llurlentry.cpp
+    llurlmatch.cpp
+    llurlregistry.cpp
     llviewborder.cpp
     llviewmodel.cpp
     llview.cpp
     llcontainerview.h
     llctrlselectioninterface.h
     lldraghandle.h
+    lldockablefloater.h
+    lldockcontrol.h
     lleditmenuhandler.h
     llf32uictrl.h
     llfiltereditor.h 
     lllayoutstack.h
     lllazyvalue.h
     lllineeditor.h
-    lllink.h
+    lllistctrl.h
     llmenugl.h
     llmodaldialog.h
     llmultifloater.h 
     llstatview.h
     llstyle.h
     lltabcontainer.h
+    lltextbase.h
     lltextbox.h
     lltexteditor.h
     lltextparser.h
     lluiimage.h
     lluistring.h
     llundo.h
+    llurlaction.h
+    llurlentry.h
+    llurlmatch.h
+    llurlregistry.h
     llviewborder.h
     llviewmodel.h
     llview.h
 # Libraries on which this library depends, needed for Linux builds
 # Sort by high-level to low-level
 target_link_libraries(llui
-    llrender
-    llwindow
-    llimage
-    llvfs       # ugh, just for LLDir
-    llxuixml
-    llxml
-    llcommon    # must be after llimage, llwindow, llrender
-    llmath
+    ${LLMESSAGE_LIBRARIES}
+    ${LLRENDER_LIBRARIES}
+    ${LLWINDOW_LIBRARIES}
+    ${LLIMAGE_LIBRARIES}
+    ${LLVFS_LIBRARIES}    # ugh, just for LLDir
+    ${LLXUIXML_LIBRARIES}
+    ${LLXML_LIBRARIES}
+    ${LLMATH_LIBRARIES}
+    ${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender
     )
+
+# Add tests
+include(LLAddBuildTest)
+SET(llui_TEST_SOURCE_FILES
+    llurlmatch.cpp
+    llurlentry.cpp
+    )
+LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}")

File indra/llui/llbutton.cpp

View file
 			setFocus(TRUE);
 		}
 
+		/*
+		 * ATTENTION! This call fires another mouse down callback.
+		 * If you wish to remove this call emit that signal directly
+		 * by calling LLUICtrl::mMouseDownSignal(x, y, mask);
+		 */
+		LLUICtrl::handleMouseDown(x, y, mask);
+
 		mMouseDownSignal(this, LLSD());
 
 		mMouseDownTimer.start();
 		mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
 		mMouseHeldDownCount = 0;
+
 		
 		if (getSoundFlags() & MOUSE_DOWN)
 		{
 		// Always release the mouse
 		gFocusMgr.setMouseCapture( NULL );
 
+		/*
+		 * ATTENTION! This call fires another mouse up callback.
+		 * If you wish to remove this call emit that signal directly
+		 * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
+		 */
+		LLUICtrl::handleMouseUp(x, y, mask);
+
 		// Regardless of where mouseup occurs, handle callback
 		mMouseUpSignal(this, LLSD());
 
 
 void LLButton::onMouseEnter(S32 x, S32 y, MASK mask)
 {
+	LLUICtrl::onMouseEnter(x, y, mask);
+
 	if (isInEnabledChain())
 		mNeedsHighlight = TRUE;
 }
 
 void LLButton::onMouseLeave(S32 x, S32 y, MASK mask)
 {
+	LLUICtrl::onMouseLeave(x, y, mask);
+
 	mNeedsHighlight = FALSE;
 }
 

File indra/llui/lldockablefloater.cpp

View file
+/** 
+ * @file lldockablefloater.cpp
+ * @brief Creates a panel of a specific kind for a toast
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ * 
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldockablefloater.h"
+
+LLDockableFloater::LLDockableFloater(LLDockControl* dockControl,
+		const LLSD& key, const Params& params) :
+	LLFloater(key, params), mDockControl(dockControl)
+{
+}
+
+LLDockableFloater::~LLDockableFloater()
+{
+}
+
+BOOL LLDockableFloater::postBuild()
+{
+	mDockTongue = LLUI::getUIImage("windows/Flyout_Pointer.png");
+	LLFloater::setDocked(true);
+	return LLView::postBuild();
+}
+
+void LLDockableFloater::setDocked(bool docked, bool pop_on_undock)
+{
+	if (docked)
+	{
+		mDockControl.get()->on();
+	}
+	else
+	{
+		mDockControl.get()->off();
+	}
+	LLFloater::setDocked(docked, pop_on_undock);
+}
+
+void LLDockableFloater::draw()
+{
+	mDockControl.get()->repositionDockable();
+	mDockControl.get()->drawToungue();
+	LLFloater::draw();
+}
+
+void LLDockableFloater::setDockControl(LLDockControl* dockControl)
+{
+	mDockControl.reset(dockControl);
+}
+const LLUIImagePtr& LLDockableFloater::getDockTongue()
+{
+	return mDockTongue;
+}
+
+LLDockControl* LLDockableFloater::getDockControl()
+{
+	return mDockControl.get();
+}

File indra/llui/lldockablefloater.h

View file
+/** 
+ * @file lldockablefloater.h
+ * @brief Creates a panel of a specific kind for a toast.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_DOCKABLEFLOATER_H
+#define LL_DOCKABLEFLOATER_H
+
+#include "llerror.h"
+#include "llfloater.h"
+#include "lldockcontrol.h"
+
+/**
+ * Represents floater that can dock.
+ * In case impossibility deriving from LLDockableFloater use LLDockControl.
+ */
+class LLDockableFloater : public LLFloater
+{
+public:
+	LOG_CLASS(LLDockableFloater);
+	LLDockableFloater(LLDockControl* dockControl, const LLSD& key, const Params& params = getDefaultParams());
+	virtual ~LLDockableFloater();
+
+	/* virtula */BOOL postBuild();
+	/* virtual */void setDocked(bool docked, bool pop_on_undock = true);
+	/* virtual */void draw();
+
+protected:
+	void setDockControl(LLDockControl* dockControl);
+	LLDockControl* getDockControl();
+	const LLUIImagePtr& getDockTongue();
+
+private:
+	std::auto_ptr<LLDockControl> mDockControl;
+	LLUIImagePtr mDockTongue;
+};
+
+#endif /* LL_DOCKABLEFLOATER_H */

File indra/llui/lldockcontrol.cpp

View file
+/** 
+ * @file lldockcontrol.cpp
+ * @brief Creates a panel of a specific kind for a toast
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ * 
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldockcontrol.h"
+
+LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
+		const LLUIImagePtr& dockTongue, DocAt dockAt, bool enabled) :
+	mDockWidget(dockWidget), mDockableFloater(dockableFloater), mDockTongue(
+			dockTongue)
+{
+	mDockAt = dockAt;
+	if (enabled)
+	{
+		on();
+	}
+	else
+	{
+		off();
+	}
+}
+
+LLDockControl::~LLDockControl()
+{
+}
+
+void LLDockControl::repositionDockable()
+{
+	if (mEnabled)
+	{
+		calculateDockablePosition();
+	}
+}
+
+void LLDockControl::calculateDockablePosition()
+{
+	LLRect dockRect = mDockWidget->calcScreenRect();
+	if (mPrevDockRect != dockRect || mRecalculateDocablePosition)
+	{
+		LLRect dockableRect = mDockableFloater->calcScreenRect();
+		LLRect rootRect = mDockableFloater->getRootView()->getRect();
+
+		S32 x = 0;
+		S32 y = 0;
+		switch (mDockAt)
+		{
+		case TOP:
+			x = dockRect.getCenterX() - dockableRect.getWidth() / 2;
+			y = dockRect.mTop + mDockTongue->getHeight()
+					+ dockableRect.getHeight();
+			if (x < rootRect.mLeft)
+			{
+				x = rootRect.mLeft;
+			}
+			if (x + dockableRect.getWidth() > rootRect.mRight)
+			{
+				x = rootRect.mRight - dockableRect.getWidth();
+			}
+			mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2;
+			mDockTongueY = dockRect.mTop;
+			break;
+		}
+		dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(),
+				dockableRect.getHeight());
+		LLRect localDocableParentRect;
+		mDockableFloater->getParent()->screenRectToLocal(dockableRect,
+				&localDocableParentRect);
+		mDockableFloater->setRect(localDocableParentRect);
+
+		mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY,
+				&mDockTongueX, &mDockTongueY);
+		mPrevDockRect = dockRect;
+		mRecalculateDocablePosition = false;
+	}
+}
+
+void LLDockControl::on()
+{
+	mDockableFloater->setCanDrag(false);
+	mEnabled = true;
+	mRecalculateDocablePosition = true;
+}
+
+void LLDockControl::off()
+{
+	mDockableFloater->setCanDrag(true);
+	mEnabled = false;
+}
+
+void LLDockControl::drawToungue()
+{
+	if (mEnabled)
+	{
+		mDockTongue->draw(mDockTongueX, mDockTongueY);
+	}
+}

File indra/llui/lldockcontrol.h

View file
+/** 
+ * @file lldockcontrol.h
+ * @brief Creates a panel of a specific kind for a toast.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_DOCKCONTROL_H
+#define LL_DOCKCONTROL_H
+
+#include "llerror.h"
+#include "llview.h"
+#include "llfloater.h"
+#include "lluiimage.h"
+
+/**
+ * Provides services for docking of specified floater.
+ * This class should be used in case impossibility deriving from LLDockableFloater.
+ */
+class LLDockControl
+{
+public:
+	enum DocAt
+	{
+		TOP
+	};
+
+public:
+	LOG_CLASS(LLDockControl);
+	LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
+			const LLUIImagePtr& dockTongue, DocAt dockAt,
+			bool enabled);
+	virtual ~LLDockControl();
+
+public:
+	void on();
+	void off();
+	void setDock(LLView* dockWidget)
+	{	mDockWidget = dockWidget;};
+	void repositionDockable();
+	void drawToungue();
+protected:
+	virtual void calculateDockablePosition();
+private:
+	bool mEnabled;
+	bool mRecalculateDocablePosition;
+	DocAt mDockAt;
+	LLView* mDockWidget;
+	LLRect mPrevDockRect;
+	LLFloater* mDockableFloater;
+	LLUIImagePtr mDockTongue;
+	S32 mDockTongueX;
+	S32 mDockTongueY;
+};
+
+#endif /* LL_DOCKCONTROL_H */

File indra/llui/llscrollcontainer.cpp

View file
 #include "llscrollingpanellist.h"
 #include "llcontainerview.h"
 #include "llpanel.h"
+#include "lllistctrl.h"
+
 static ScrollContainerRegistry::Register<LLScrollingPanelList> r1("scrolling_panel_list");
 static ScrollContainerRegistry::Register<LLContainerView> r2("container_view");
 static ScrollContainerRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);
+static ScrollContainerRegistry::Register<LLListCtrl> r4("list");
 
 LLScrollContainer::Params::Params()
 :	is_opaque("opaque"),

File indra/llui/llscrolllistctrl.cpp

View file
 #include "llviewborder.h"
 #include "lltextbox.h"
 #include "llsdparam.h"
+#include "llcachename.h"
+#include "llmenugl.h"
+#include "llurlaction.h"
+
+#include <boost/bind.hpp>
 
 static LLDefaultChildRegistry::Register<LLScrollListCtrl> r("scroll_list");
 
 	sort_ascending("sort_ascending", true),
 	commit_on_keyboard_movement("commit_on_keyboard_movement", true),
 	heading_height("heading_height"),
+	page_lines("page_lines", 0),
 	background_visible("background_visible"),
 	draw_stripes("draw_stripes"),
 	column_padding("column_padding"),
 :	LLUICtrl(p),
 	mLineHeight(0),
 	mScrollLines(0),
-	mPageLines(0),
+	mPageLines(p.page_lines),
 	mMaxSelectable(0),
 	mAllowKeyboardMovement(TRUE),
 	mCommitOnKeyboardMovement(p.commit_on_keyboard_movement),
 	mOnSortChangedCallback( NULL ),
 	mHighlightedItem(-1),
 	mBorder(NULL),
+	mPopupMenu(NULL),
 	mNumDynamicWidthColumns(0),
 	mTotalStaticColumnWidth(0),
 	mTotalColumnPadding(0),
 	mHighlightedColor(p.highlighted_color()),
 	mHoveredColor(p.hovered_color()),
 	mSearchColumn(p.search_column),
-	mColumnPadding(p.column_padding)
+	mColumnPadding(p.column_padding),
+	mContextMenuType(MENU_NONE)
 {
 	mItemListRect.setOriginAndSize(
 		mBorderThickness,
 
 	updateLineHeight();
 
-	mPageLines = mLineHeight? (mItemListRect.getHeight()) / mLineHeight : 0;
-
 	// Init the scrollbar
 	static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
 
 	sbparams.orientation(LLScrollbar::VERTICAL);
 	sbparams.doc_size(getItemCount());
 	sbparams.doc_pos(mScrollLines);
-	sbparams.page_size(mPageLines);
+	sbparams.page_size( mPageLines ? mPageLines : getItemCount() );
 	sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2));
 	sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
 	sbparams.visible(false);
 	getChildView("comment_text")->setShape(mItemListRect);
 
 	// how many lines of content in a single "page"
-	mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0;
-	BOOL scrollbar_visible = getItemCount() > mPageLines;
+	S32 page_lines =  mLineHeight? mItemListRect.getHeight() / mLineHeight : getItemCount();
+	//if mPageLines is NOT provided display all item
+	if(mPageLines)
+		page_lines = mPageLines;
+
+	BOOL scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight();
 	if (scrollbar_visible)
 	{
 		// provide space on the right for scrollbar
 
 	mScrollbar->setOrigin(getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom);
 	mScrollbar->reshape(scrollbar_size, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0));
-	mScrollbar->setPageSize( mPageLines );
+	mScrollbar->setPageSize(page_lines);
 	mScrollbar->setDocSize( getItemCount() );
 	mScrollbar->setVisible(scrollbar_visible);
 
 void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height)
 {
 	S32 height = llmin( getRequiredRect().getHeight(), max_height );
+	if(mPageLines)
+		height = llmin( mPageLines * mLineHeight + (mDisplayColumnHeaders ? mHeadingHeight : 0), height );
+
 	S32 width = getRect().getWidth();
 
 	reshape( width, height );
 	updateLayout();
 
 }
+void LLScrollListCtrl::setPageLines(S32 new_page_lines)
+{
+	mPageLines  = new_page_lines;
+	
+	updateLayout();
+}
 
 BOOL LLScrollListCtrl::selectFirstItem()
 {
 	S32 y = mItemListRect.mTop - mLineHeight;
 
 	// allow for partial line at bottom
-	S32 num_page_lines = mPageLines + 1;
+	S32 num_page_lines = (mPageLines)? mPageLines : getItemCount() + 1;
 
 	LLRect item_rect;
 
 	return LLUICtrl::handleMouseUp(x, y, mask);
 }
 
+// virtual
+BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+	LLScrollListItem *item = hitItem(x, y);
+	if (item)
+	{
+		// check to see if we have a UUID for this row
+		std::string id = item->getValue().asString();
+		LLUUID uuid(id);
+		if (! uuid.isNull() && mContextMenuType != MENU_NONE)
+		{
+			// set up the callbacks for all of the avatar/group menu items
+			// (N.B. callbacks don't take const refs as id is local scope)
+			bool is_group = (mContextMenuType == MENU_GROUP);
+			LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+			registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group));
+			registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group));
+			registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group));
+
+			// create the context menu from the XUI file and display it
+			std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml";
+			delete mPopupMenu;
+			mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
+				menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+			if (mPopupMenu)
+			{
+				mPopupMenu->show(x, y);
+				LLMenuGL::showPopup(this, mPopupMenu, x, y);
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+void LLScrollListCtrl::showNameDetails(std::string id, bool is_group)
+{
+	// show the resident's profile or the group profile
+	std::string sltype = is_group ? "group" : "agent";
+	std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
+	LLUrlAction::clickAction(slurl);
+}
+
+void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group)
+{
+	// copy the name of the avatar or group to the clipboard
+	std::string name;
+	if (is_group)
+	{
+		gCacheName->getGroupName(LLUUID(id), name);
+	}
+	else
+	{
+		gCacheName->getFullName(LLUUID(id), name);
+	}
+	LLUrlAction::copyURLToClipboard(name);
+}
+
+void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group)
+{
+	// copy a SLURL for the avatar or group to the clipboard
+	std::string sltype = is_group ? "group" : "agent";
+	std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
+	LLUrlAction::copyURLToClipboard(slurl);
+}
+
 BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
 {
 	//BOOL handled = FALSE;
 		mLineHeight );
 
 	// allow for partial line at bottom
-	S32 num_page_lines = mPageLines + 1;
+	S32 num_page_lines = (mPageLines)? mPageLines : getItemCount() + 1;
 
 	S32 line = 0;
 	item_list::iterator iter;
 	}
 
 	S32 lowest = mScrollLines;
-	S32 highest = mScrollLines + mPageLines;
+	S32 page_lines = (mPageLines)? mPageLines : getItemCount();
+	S32 highest = mScrollLines + page_lines;
 
 	if (index < lowest)
 	{
 	}
 	else if (highest <= index)
 	{
-		setScrollPos(index - mPageLines + 1);
+		setScrollPos(index - page_lines + 1);
 	}
 }
 

File indra/llui/llscrolllistctrl.h

View file
 
 class LLScrollListCell;
 class LLTextBox;
+class LLContextMenu;
 
 class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler, 
 	public LLCtrlListInterface, public LLCtrlScrollInterface
 
 		// layout
 		Optional<S32>	column_padding,
+							page_lines,
 						heading_height;
 
 		// sort and search behavior
 
 	void			clearSearchString() { mSearchString.clear(); }
 
+	// support right-click context menus for avatar/group lists
+	enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP };
+	void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; }
+
 	// Overridden from LLView
 	/*virtual*/ void    draw();
 	/*virtual*/ BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
 	/*virtual*/ BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
+	/*virtual*/ BOOL	handleRightMouseDown(S32 x, S32 y, MASK mask);
 	/*virtual*/ BOOL	handleDoubleClick(S32 x, S32 y, MASK mask);
 	/*virtual*/ BOOL	handleHover(S32 x, S32 y, MASK mask);
 	/*virtual*/ BOOL	handleKeyHere(KEY key, MASK mask);
 	S32 getMaxContentWidth() { return mMaxContentWidth; }
 
 	void setHeadingHeight(S32 heading_height);
+	/**
+	 * Sets  max visible  lines without scroolbar, if this value equals to 0,
+	 * then display all items.
+	 */
+	void setPageLines(S32 page_lines );
 	void setCollapseEmptyColumns(BOOL collapse);
 
 	LLScrollListItem*	hitItem(S32 x,S32 y);
 	typedef std::deque<LLScrollListItem *> item_list;
 	item_list&		getItemList() { return mItemList; }
 
+	void			updateLineHeight();
+
 private:
 	void			selectPrevItem(BOOL extend_selection);
 	void			selectNextItem(BOOL extend_selection);
 	void			drawItems();
-	void			updateLineHeight();
+	
 	void            updateLineHeightInsert(LLScrollListItem* item);
 	void			reportInvalidInput();
 	BOOL			isRepeatedChars(const LLWString& string) const;
 	void			commitIfChanged();
 	BOOL			setSort(S32 column, BOOL ascending);
 
+	static void		showNameDetails(std::string id, bool is_group);
+	static void		copyNameToClipboard(std::string id, bool is_group);
+	static void		copySLURLToClipboard(std::string id, bool is_group);
+
 	S32				mLineHeight;	// the max height of a single line
 	S32				mScrollLines;	// how many lines we've scrolled down
 	S32				mPageLines;		// max number of lines is it possible to see on the screen given mRect and mLineHeight
 
 	S32				mHighlightedItem;
 	class LLViewBorder*	mBorder;
+	LLContextMenu	*mPopupMenu;
 
 	LLWString		mSearchString;
 	LLFrameTimer	mSearchTimer;
 	BOOL			mDirty;
 	S32				mOriginalSelection;
 
+	ContextMenuType mContextMenuType;
+
 	typedef std::vector<LLScrollListColumn*> ordered_columns_t;
 	ordered_columns_t	mColumnsIndexed;
 

File indra/llui/llstyle.cpp

View file
 	mFont(p.font()),
 	mLink(p.link_href),
 	mDropShadow(p.drop_shadow),
-	mImageHeight(0),
-	mImageWidth(0),
 	mImagep(p.image())
 {}
 
 	mImagep = LLUI::getUIImageByID(src);
 }
 
-
-void LLStyle::setImageSize(S32 width, S32 height)
+void LLStyle::setImage(const std::string& name)
 {
-    mImageWidth = width;
-    mImageHeight = height;
+	mImagep = LLUI::getUIImage(name);
 }

File indra/llui/llstyle.h

View file
 
 	LLUIImagePtr getImage() const;
 	void setImage(const LLUUID& src);
+	void setImage(const std::string& name);
 
-	BOOL isImage() const { return ((mImageWidth != 0) && (mImageHeight != 0)); }
-	void setImageSize(S32 width, S32 height);
+	BOOL isImage() const { return mImagep.notNull(); }
 
 	// inlined here to make it easier to compare to member data below. -MG
 	bool operator==(const LLStyle &rhs) const
 			&& mFont == rhs.mFont
 			&& mLink == rhs.mLink
 			&& mImagep == rhs.mImagep
-			&& mImageHeight == rhs.mImageHeight
-			&& mImageWidth == rhs.mImageWidth
 			&& mItalic == rhs.mItalic
 			&& mBold == rhs.mBold
 			&& mUnderline == rhs.mUnderline
 	BOOL        mBold;
 	BOOL        mUnderline;
 	BOOL		mDropShadow;
-	S32         mImageWidth;
-	S32         mImageHeight;
 
 protected:
 	~LLStyle() { }

File indra/llui/lltextbase.cpp

View file
+/** 
+ * @file lltextbase.cpp
+ * @author Martin Reddy
+ * @brief The base class of text box/editor, providing Url handling support
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * 
+ * Copyright (c) 2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltextbase.h"
+#include "llstl.h"
+#include "llview.h"
+#include "llwindow.h"
+#include "llmenugl.h"
+#include "lluictrl.h"
+#include "llurlaction.h"
+#include "llurlregistry.h"
+
+#include <boost/bind.hpp>
+
+// global state for all text fields
+LLUIColor LLTextBase::mLinkColor = LLColor4::blue;
+
+bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const
+{
+	return a->getEnd() < b->getEnd();
+}
+
+//
+// LLTextSegment
+//
+
+LLTextSegment::~LLTextSegment()
+{}
+
+S32	LLTextSegment::getWidth(S32 first_char, S32 num_chars) const { return 0; }
+S32	LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; }
+S32	LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { return 0; }
+void LLTextSegment::updateLayout(const LLTextBase& editor) {}
+F32	LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { return draw_rect.mLeft; }
+S32	LLTextSegment::getMaxHeight() const { return 0; }
+bool LLTextSegment::canEdit() const { return false; }
+void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
+void LLTextSegment::linkToDocument(LLTextBase*) {}
+void LLTextSegment::setHasMouseHover(bool hover) {}
+const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; }
+void LLTextSegment::setColor(const LLColor4 &color) {}
+const LLStyleSP LLTextSegment::getStyle() const {static LLStyleSP sp(new LLStyle()); return sp; }
+void LLTextSegment::setStyle(const LLStyleSP &style) {}
+void LLTextSegment::setToken( LLKeywordToken* token ) {}
+LLKeywordToken*	LLTextSegment::getToken() const { return NULL; }
+BOOL LLTextSegment::getToolTip( std::string& msg ) const { return FALSE; }
+void LLTextSegment::setToolTip( const std::string &msg ) {}
+void LLTextSegment::dump() const {}
+
+
+//
+// LLNormalTextSegment
+//
+
+LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 end, LLTextBase& editor ) 
+:	LLTextSegment(start, end),
+	mStyle( style ),
+	mToken(NULL),
+	mHasMouseHover(false),
+	mEditor(editor)
+{
+	mMaxHeight = llceil(mStyle->getFont()->getLineHeight());
+}
+
+LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) 
+:	LLTextSegment(start, end),
+	mToken(NULL),
+	mHasMouseHover(false),
+	mEditor(editor)
+{
+	mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color));
+
+	mMaxHeight = llceil(mStyle->getFont()->getLineHeight());
+}
+
+F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+	if( end - start > 0 )
+	{
+		if ( mStyle->isImage() && (start >= 0) && (end <= mEnd - mStart))
+		{
+			LLUIImagePtr image = mStyle->getImage();
+			S32 style_image_height = image->getHeight();
+			S32 style_image_width = image->getWidth();
+			image->draw(draw_rect.mLeft, draw_rect.mTop-style_image_height, 
+				style_image_width, style_image_height);
+		}
+
+		return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect.mLeft, draw_rect.mBottom);
+	}
+	return draw_rect.mLeft;
+}
+
+// Draws a single text segment, reversing the color for selection if needed.
+F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, F32 x, F32 y)
+{
+	const LLWString &text = mEditor.getWText();
+
+	F32 right_x = x;
+	if (!mStyle->isVisible())