diff options
| author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-03-12 16:52:30 +0100 | 
|---|---|---|
| committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-03-12 16:52:30 +0100 | 
| commit | eb1ed3896fd82d4f115b2ef6ba742315ad32cc27 (patch) | |
| tree | 110015e1703b331715fbf7fb1a01639c74c47e62 /indra | |
| parent | 748c0eb50d87a3f8895b25791409ce5e2e4926c4 (diff) | |
| parent | afc943acbc2bb79e2e1aa5d5eaf448e01b6c2b00 (diff) | |
Merge branch 'main' of https://github.com/secondlife/viewer into DRTVWR-600-maint-A
# Conflicts:
#	autobuild.xml
#	indra/llrender/llfontbitmapcache.cpp
#	indra/llrender/llfontbitmapcache.h
#	indra/llrender/llfontfreetype.cpp
#	indra/llrender/llfontfreetype.h
#	indra/llrender/llfontgl.cpp
#	indra/llrender/llfontgl.h
#	indra/llui/llbutton.h
#	indra/llui/llfloater.cpp
#	indra/llui/llfloater.h
#	indra/llui/llfolderviewitem.cpp
#	indra/llui/lllineeditor.cpp
#	indra/llui/lllineeditor.h
#	indra/llui/llscrollcontainer.cpp
#	indra/llui/llscrollingpanellist.cpp
#	indra/llui/llscrollingpanellist.h
#	indra/llui/llscrolllistctrl.h
#	indra/llui/lltextbase.cpp
#	indra/llui/lltextbase.h
#	indra/llui/lltexteditor.cpp
#	indra/llui/lltexteditor.h
#	indra/llui/lluictrl.cpp
#	indra/llui/llview.cpp
#	indra/llui/llview.h
#	indra/newview/llchicletbar.h
#	indra/newview/llconversationlog.h
#	indra/newview/llfloaterimsessiontab.cpp
#	indra/newview/llfloaterimsessiontab.h
#	indra/newview/llfloateruipreview.cpp
#	indra/newview/llnavigationbar.h
#	indra/newview/llpaneltopinfobar.h
#	indra/newview/llpathfindingpathtool.h
#	indra/newview/lltextureview.cpp
#	indra/newview/lltoolbrush.h
#	indra/newview/lltoolcomp.h
#	indra/newview/lltooldraganddrop.h
#	indra/newview/lltoolface.h
#	indra/newview/lltoolfocus.h
#	indra/newview/lltoolindividual.h
#	indra/newview/lltoolobjpicker.h
#	indra/newview/lltoolpie.h
#	indra/newview/lltoolpipette.h
#	indra/newview/lltoolselectland.h
#	indra/newview/llviewermediafocus.h
#	indra/newview/llviewerparcelmediaautoplay.h
#	indra/newview/llviewerwindow.cpp
#	indra/newview/llvoicechannel.h
#	indra/newview/llvoicevivox.h
#	indra/newview/llworldmapview.cpp
Diffstat (limited to 'indra')
160 files changed, 6563 insertions, 1408 deletions
| diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 8749c10ffc..737609c89c 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -29,6 +29,7 @@ set(cmake_SOURCE_FILES          GoogleMock.cmake          Havok.cmake          Hunspell.cmake +        ICU4C.cmake          JsonCpp.cmake          LLAddBuildTest.cmake          LLAppearance.cmake @@ -63,7 +64,7 @@ set(cmake_SOURCE_FILES          VisualLeakDetector.cmake          LibVLCPlugin.cmake          XmlRpcEpi.cmake -    xxHash.cmake +        xxHash.cmake          ZLIBNG.cmake          ) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 886629ea73..8b646297b9 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -61,6 +61,15 @@ if(WINDOWS)          uriparser.dll          ) +    # ICU4C (same filenames for 32 and 64 bit builds) +    set(release_files ${release_files} icudt48.dll) +    set(release_files ${release_files} icuin48.dll) +    set(release_files ${release_files} icuio48.dll) +    set(release_files ${release_files} icule48.dll) +    set(release_files ${release_files} iculx48.dll) +    set(release_files ${release_files} icutu48.dll) +    set(release_files ${release_files} icuuc48.dll) +      # OpenSSL      if(ADDRESS_SIZE EQUAL 64)          set(release_files ${release_files} libcrypto-1_1-x64.dll) diff --git a/indra/cmake/ICU4C.cmake b/indra/cmake/ICU4C.cmake new file mode 100644 index 0000000000..7b27665483 --- /dev/null +++ b/indra/cmake/ICU4C.cmake @@ -0,0 +1,23 @@ +# -*- cmake -*- +include(Prebuilt) + +include_guard() + +add_library( ll::icu4c INTERFACE IMPORTED ) + + +use_system_binary(icu4c) +use_prebuilt_binary(icu4c) +if (WINDOWS) +  target_link_libraries( ll::icu4c INTERFACE  icuuc) +elseif(DARWIN) +  target_link_libraries( ll::icu4c INTERFACE  icuuc) +#elseif(LINUX) +##  target_link_libraries( ll::icu4c INTERFACE  ) +else() +  message(FATAL_ERROR "Invalid platform") +endif() + +target_include_directories( ll::icu4c SYSTEM INTERFACE  ${LIBS_PREBUILT_DIR}/include/unicode ) + +use_prebuilt_binary(dictionaries) diff --git a/indra/cmake/ViewerMiscLibs.cmake b/indra/cmake/ViewerMiscLibs.cmake index 00f8b77106..cae68fbc11 100644 --- a/indra/cmake/ViewerMiscLibs.cmake +++ b/indra/cmake/ViewerMiscLibs.cmake @@ -18,3 +18,6 @@ endif()  use_prebuilt_binary(slvoice) +use_prebuilt_binary(nanosvg) +use_prebuilt_binary(viewer-fonts) +use_prebuilt_binary(emoji_shortcodes) diff --git a/indra/llappearance/llwearabletype.h b/indra/llappearance/llwearabletype.h index d2a85581bc..9a68dbe841 100644 --- a/indra/llappearance/llwearabletype.h +++ b/indra/llappearance/llwearabletype.h @@ -37,7 +37,7 @@ class LLWearableType : public LLParamSingleton<LLWearableType>  {  	LLSINGLETON(LLWearableType, LLTranslationBridge::ptr_t &trans);  	~LLWearableType(); -	void initSingleton(); +	void initSingleton() override;  public:   	enum EType  	{ diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 80bc95ffba..26955cfc08 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -3,6 +3,7 @@  project(llcommon)  include(00-Common) +include(ICU4C)  include(LLCommon)  include(bugsplat)  include(Linking) @@ -283,6 +284,7 @@ target_link_libraries(          ll::uriparser          ll::oslibraries          ll::tracy +        ll::icu4c      )  target_include_directories(llcommon INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 966ce03296..fd878f20ad 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -92,7 +92,7 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>      LLSINGLETON(LLCoros);      ~LLCoros(); -    void cleanupSingleton(); +    void cleanupSingleton() override;  public:      /// The viewer's use of the term "coroutine" became deeply embedded before      /// the industry term "fiber" emerged to distinguish userland threads from diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 51ef514cf7..cbe5ab6406 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -802,7 +802,7 @@ public:  private:                                                                \      /* implement LLSingleton pure virtual method whose sole purpose */  \      /* is to remind people to use this macro */                         \ -    virtual void you_must_use_LLSINGLETON_macro() {}                    \ +    virtual void you_must_use_LLSINGLETON_macro() override {}                    \      friend class LLSingleton<DERIVED_CLASS>;                            \      DERIVED_CLASS(__VA_ARGS__) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 48551ab375..98c9d20cdd 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -30,6 +30,7 @@  #include "llerror.h"  #include "llfasttimer.h"  #include "llsd.h" +#include <unicode/uchar.h>  #include <vector>  #if LL_WINDOWS @@ -338,8 +339,6 @@ S32 wchar_utf8_length(const llwchar wc)  {  	if (wc < 0x80)  	{ -		// This case will also catch negative values which are -		// technically invalid.  		return 1;  	}  	else if (wc < 0x800) @@ -364,6 +363,30 @@ S32 wchar_utf8_length(const llwchar wc)  	}  } +std::string wchar_utf8_preview(const llwchar wc) +{ +    std::ostringstream oss; +    oss << std::hex << std::uppercase << (U32)wc; + +    U8 out_bytes[8]; +    U32 size = (U32)wchar_to_utf8chars(wc, (char*)out_bytes); + +    if (size > 1) +    { +        oss << " ["; +        for (U32 i = 0; i < size; ++i) +        { +            if (i) +            { +                oss << ", "; +            } +            oss << (int)out_bytes[i]; +        } +        oss << "]"; +    } + +    return oss.str(); +}  S32 wstring_utf8_length(const LLWString& wstr)  { @@ -600,6 +623,7 @@ std::string mbcsstring_makeASCII(const std::string& wstr)  	}  	return out_str;  } +  std::string utf8str_removeCRLF(const std::string& utf8str)  {  	if (0 == utf8str.length()) @@ -621,6 +645,119 @@ std::string utf8str_removeCRLF(const std::string& utf8str)  	return out;  } +llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length) +{ +    switch (length) +    { +    case 2: +        return ((utf8str[offset] & 0x1F) << 6) + +                (utf8str[offset + 1] & 0x3F); +    case 3: +        return ((utf8str[offset] & 0x0F) << 12) + +                ((utf8str[offset + 1] & 0x3F) << 6) + +                (utf8str[offset + 2] & 0x3F); +    case 4: +        return ((utf8str[offset] & 0x07) << 18) + +                ((utf8str[offset + 1] & 0x3F) << 12) + +                ((utf8str[offset + 2] & 0x3F) << 6) + +                (utf8str[offset + 3] & 0x3F); +    case 5: +        return ((utf8str[offset] & 0x03) << 24) + +                ((utf8str[offset + 1] & 0x3F) << 18) + +                ((utf8str[offset + 2] & 0x3F) << 12) + +                ((utf8str[offset + 3] & 0x3F) << 6) + +                (utf8str[offset + 4] & 0x3F); +    case 6: +        return ((utf8str[offset] & 0x01) << 30) + +                ((utf8str[offset + 1] & 0x3F) << 24) + +                ((utf8str[offset + 2] & 0x3F) << 18) + +                ((utf8str[offset + 3] & 0x3F) << 12) + +                ((utf8str[offset + 4] & 0x3F) << 6) + +                (utf8str[offset + 5] & 0x3F); +    case 7: +        return ((utf8str[offset + 1] & 0x03) << 30) + +                ((utf8str[offset + 2] & 0x3F) << 24) + +                ((utf8str[offset + 3] & 0x3F) << 18) + +                ((utf8str[offset + 4] & 0x3F) << 12) + +                ((utf8str[offset + 5] & 0x3F) << 6) + +                (utf8str[offset + 6] & 0x3F); +    } +    return LL_UNKNOWN_CHAR; +} + +std::string utf8str_showBytesUTF8(const std::string& utf8str) +{ +    std::string result; + +    bool in_sequence = false; +    size_t sequence_size = 0; +    size_t byte_index = 0; +    size_t source_length = utf8str.size(); + +    auto open_sequence = [&]() +        { +            if (!result.empty() && result.back() != '\n') +                result += '\n'; // Use LF as a separator before new UTF-8 sequence +            result += '['; +            in_sequence = true; +        }; + +    auto close_sequence = [&]() +        { +            llwchar unicode = utf8str_to_wchar(utf8str, byte_index - sequence_size, sequence_size); +            if (unicode != LL_UNKNOWN_CHAR) +            { +                result += llformat("+%04X", unicode); +            } +            result += ']'; +            in_sequence = false; +            sequence_size = 0; +        }; + +    while (byte_index < source_length) +    { +        U8 byte = utf8str[byte_index]; +        if (byte >= 0x80) // Part of an UTF-8 sequence +        { +            if (!in_sequence) // Start new UTF-8 sequence +            { +                open_sequence(); +            } +            else if (byte >= 0xC0) // Start another UTF-8 sequence +            { +                close_sequence(); +                open_sequence(); +            } +            else // Continue the same UTF-8 sequence +            { +                result += '.'; +            } +            result += llformat("%02X", byte); // The byte is represented in hexadecimal form +            ++sequence_size; +        } +        else // ASCII symbol is represented as a character +        { +            if (in_sequence) // End of UTF-8 sequence +            { +                close_sequence(); +                if (byte != '\n') +                { +                    result += '\n'; // Use LF as a separator between UTF-8 and ASCII +                } +            } +            result += byte; +        } +        ++byte_index; +    } + +    if (in_sequence) // End of UTF-8 sequence +    { +        close_sequence(); +    } + +    return result; +} +  #if LL_WINDOWS  unsigned int ll_wstring_default_code_page()  { @@ -833,6 +970,40 @@ std::string LLStringOps::sDayFormat;  std::string LLStringOps::sAM;  std::string LLStringOps::sPM; +// static +bool LLStringOps::isEmoji(llwchar wch) +{ +	int ublock = ublock_getCode(wch); +	switch (ublock) +	{ +		case UBLOCK_GENERAL_PUNCTUATION: +		case UBLOCK_LETTERLIKE_SYMBOLS: +		case UBLOCK_ARROWS: +		case UBLOCK_MISCELLANEOUS_TECHNICAL: +		case UBLOCK_ENCLOSED_ALPHANUMERICS: +		case UBLOCK_GEOMETRIC_SHAPES: +		case UBLOCK_MISCELLANEOUS_SYMBOLS: +		case UBLOCK_DINGBATS: +		case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: +		case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS: +		case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS: +		case UBLOCK_EMOTICONS: +		case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS: +#if U_ICU_VERSION_MAJOR_NUM > 56 +		// Boost uses ICU so we can't update it independently +		case UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS: +#endif // U_ICU_VERSION_MAJOR_NUM > 56 +			return true; +		default: +#if U_ICU_VERSION_MAJOR_NUM > 56 +			return false; +#else +			// See https://en.wikipedia.org/wiki/Supplemental_Symbols_and_Pictographs +			return wch >= 0x1F900 && wch <= 0x1F9FF; +#endif // U_ICU_VERSION_MAJOR_NUM > 56 +	} +} +  S32	LLStringOps::collate(const llwchar* a, const llwchar* b)  {  diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 6893b8ebff..605d0ac4d7 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -190,6 +190,8 @@ public:  	static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }  	static bool isAlnum(llwchar a) { return iswalnum(a) != 0; } +	static bool isEmoji(llwchar wch); +  	static S32	collate(const char* a, const char* b) { return strcoll(a, b); }  	static S32	collate(const llwchar* a, const llwchar* b); @@ -356,6 +358,8 @@ public:  	static void	replaceNonstandardASCII( string_type& string, T replacement );  	static void	replaceChar( string_type& string, T target, T replacement );  	static void replaceString( string_type& string, string_type target, string_type replacement ); +	static string_type capitalize(const string_type& str); +	static void capitalize(string_type& str);  	static bool	containsNonprintable(const string_type& string);  	static void	stripNonprintable(string_type& string); @@ -679,6 +683,8 @@ LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);  // Length in bytes of this wide char in a UTF8 string  LL_COMMON_API S32 wchar_utf8_length(const llwchar wc);  +LL_COMMON_API std::string wchar_utf8_preview(const llwchar wc); +  LL_COMMON_API std::string utf8str_tolower(const std::string& utf8str);  // Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string. @@ -738,6 +744,9 @@ LL_COMMON_API std::string mbcsstring_makeASCII(const std::string& str);  LL_COMMON_API std::string utf8str_removeCRLF(const std::string& utf8str); +LL_COMMON_API llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length); + +LL_COMMON_API std::string utf8str_showBytesUTF8(const std::string& utf8str);  #if LL_WINDOWS  /* @name Windows string helpers @@ -1595,6 +1604,29 @@ void LLStringUtilBase<T>::replaceTabsWithSpaces( string_type& str, size_type spa  }  //static +template<class T> +std::basic_string<T> LLStringUtilBase<T>::capitalize(const string_type& str) +{ +	string_type result(str); +	capitalize(result); +	return result; +} + +//static +template<class T> +void LLStringUtilBase<T>::capitalize(string_type& str) +{ +	if (str.size()) +	{ +		auto last = str[0] = toupper(str[0]); +		for (U32 i = 1; i < str.size(); ++i) +		{ +			last = (last == ' ' || last == '-' || last == '_') ? str[i] = toupper(str[i]) : str[i]; +		} +	} +} + +//static  template<class T>   bool LLStringUtilBase<T>::containsNonprintable(const string_type& string)  { diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index 15ffe68e67..6f8aaaa0cb 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -47,8 +47,8 @@ public:                                             \          DEP_INIT  /* dependency in initSingleton */ \      } sDepFlag;                                     \                                                      \ -    void initSingleton();                           \ -    void cleanupSingleton();                        \ +    void initSingleton() override;                  \ +    void cleanupSingleton() override;               \  };                                                  \                                                      \  CLS::dep_flag CLS::sDepFlag = DEP_NONE @@ -300,7 +300,7 @@ namespace tut      {          LLSINGLETON_EMPTY_CTOR(CircularPInit);      public: -        virtual void initSingleton() +        virtual void initSingleton() override          {              // never mind indirection, just go straight for the circularity              CircularPInit *pt = getInstance(); diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp index c1d54bc210..cd32152adc 100644 --- a/indra/llinventory/llfoldertype.cpp +++ b/indra/llinventory/llfoldertype.cpp @@ -60,7 +60,7 @@ class LLFolderDictionary : public LLSingleton<LLFolderDictionary>,  {  	LLSINGLETON(LLFolderDictionary);  protected: -	virtual LLFolderType::EType notFound() const +	virtual LLFolderType::EType notFound() const override  	{  		return LLFolderType::FT_NONE;  	} diff --git a/indra/llinventory/llinventorysettings.cpp b/indra/llinventory/llinventorysettings.cpp index 81485b3a97..bc604097da 100644 --- a/indra/llinventory/llinventorysettings.cpp +++ b/indra/llinventory/llinventorysettings.cpp @@ -62,7 +62,7 @@ class LLSettingsDictionary : public LLSingleton<LLSettingsDictionary>,  {      LLSINGLETON(LLSettingsDictionary); -    void initSingleton(); +    void initSingleton() override;  };  LLSettingsDictionary::LLSettingsDictionary()  diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 1c97133723..8be4c64dfc 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -106,7 +106,7 @@ public:  private:      virtual ~LLExperienceCache(); -    virtual void initSingleton(); +    virtual void initSingleton() override;      typedef boost::function<LLSD(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, LLCore::HttpRequest::ptr_t, std::string)> permissionInvoker_fn; diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index 25f6977e14..8a64cdbfaa 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -226,7 +226,7 @@ class LLProxy: public LLSingleton<LLProxy>  	LLSINGLETON(LLProxy);  	LOG_CLASS(LLProxy); -    /*virtual*/ void initSingleton(); +    /*virtual*/ void initSingleton() override;  public:  	// Static check for enabled status for UDP packets. Call from main thread only. diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index a95daed6cd..7f881c8bb3 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -15,6 +15,7 @@ set(llrender_SOURCE_FILES      llcubemaparray.cpp      llfontbitmapcache.cpp      llfontfreetype.cpp +    llfontfreetypesvg.cpp      llfontgl.cpp      llfontregistry.cpp      llgl.cpp @@ -43,6 +44,7 @@ set(llrender_HEADER_FILES      llcubemaparray.h      llfontgl.h      llfontfreetype.h +    llfontfreetypesvg.h      llfontbitmapcache.h      llfontregistry.h      llgl.h diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp index 3f6d85b542..1c67e3fb14 100644 --- a/indra/llrender/llfontbitmapcache.cpp +++ b/indra/llrender/llfontbitmapcache.cpp @@ -30,14 +30,7 @@  #include "llfontbitmapcache.h"  LLFontBitmapCache::LLFontBitmapCache() -:	mNumComponents(0), -	mBitmapWidth(0), -	mBitmapHeight(0), -	mBitmapNum(-1), -	mMaxCharWidth(0), -	mMaxCharHeight(0), -	mCurrentOffsetX(1), -	mCurrentOffsetY(1) +  {  } @@ -45,121 +38,149 @@ LLFontBitmapCache::~LLFontBitmapCache()  {  } -void LLFontBitmapCache::init(S32 num_components, -							 S32 max_char_width, +void LLFontBitmapCache::init(S32 max_char_width,  							 S32 max_char_height)  {  	reset(); -	mNumComponents = num_components;  	mMaxCharWidth = max_char_width;  	mMaxCharHeight = max_char_height; + +	S32 image_width = mMaxCharWidth * 20; +	S32 pow_iw = 2; +	while (pow_iw < image_width) +	{ +		pow_iw <<= 1; +	} +	image_width = pow_iw; +	image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. + +	mBitmapWidth = image_width; +	mBitmapHeight = image_width;  } -LLImageRaw *LLFontBitmapCache::getImageRaw(U32 bitmap_num) const +LLImageRaw *LLFontBitmapCache::getImageRaw(EFontGlyphType bitmap_type, U32 bitmap_num) const  { -	if (bitmap_num >= mImageRawVec.size()) -		return NULL; +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageRawVec[bitmap_idx].size()) +		return nullptr; -	return mImageRawVec[bitmap_num]; +	return mImageRawVec[bitmap_idx][bitmap_num];  } -LLImageGL *LLFontBitmapCache::getImageGL(U32 bitmap_num) const +LLImageGL *LLFontBitmapCache::getImageGL(EFontGlyphType bitmap_type, U32 bitmap_num) const  { -	if (bitmap_num >= mImageGLVec.size()) -		return NULL; +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (bitmap_type >= EFontGlyphType::Count || bitmap_num >= mImageGLVec[bitmap_idx].size()) +		return nullptr; -	return mImageGLVec[bitmap_num]; +	return mImageGLVec[bitmap_idx][bitmap_num];  } -bool LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num) +bool LLFontBitmapCache::nextOpenPos(S32 width, S32& pos_x, S32& pos_y, EFontGlyphType bitmap_type, U32& bitmap_num)  { -	if ((mBitmapNum<0) || (mCurrentOffsetX + width + 1) > mBitmapWidth) +	if (bitmap_type >= EFontGlyphType::Count) +	{ +		return false; +	} + +	const U32 bitmap_idx = static_cast<U32>(bitmap_type); +	if (mImageRawVec[bitmap_idx].empty() || (mCurrentOffsetX[bitmap_idx] + width + 1) > mBitmapWidth)  	{ -		if ((mBitmapNum<0) || (mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight) +		if ((mImageRawVec[bitmap_idx].empty()) || (mCurrentOffsetY[bitmap_idx] + 2*mMaxCharHeight + 2) > mBitmapHeight)  		{  			// We're out of space in the current image, or no image  			// has been allocated yet.  Make a new one. -			 -			mImageRawVec.push_back(new LLImageRaw); -			mBitmapNum = mImageRawVec.size()-1; -			LLImageRaw *image_raw = getImageRaw(mBitmapNum); - -			// Make corresponding GL image. -			mImageGLVec.push_back(new LLImageGL(false)); -			LLImageGL *image_gl = getImageGL(mBitmapNum); -			 -			S32 image_width = mMaxCharWidth * 20; -			S32 pow_iw = 2; -			while (pow_iw < image_width) +             +            S32 image_width = mMaxCharWidth * 20; +            S32 pow_iw = 2; +            while (pow_iw < image_width) +            { +                pow_iw *= 2; +            } +            image_width = pow_iw; +            image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. +            S32 image_height = image_width; +             +            mBitmapWidth = image_width; +            mBitmapHeight = image_height; +             +			S32 num_components = getNumComponents(bitmap_type); +			mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components)); +			bitmap_num = mImageRawVec[bitmap_idx].size() - 1; + +			LLImageRaw* image_raw = getImageRaw(bitmap_type, bitmap_num); +			if (EFontGlyphType::Grayscale == bitmap_type)  			{ -				pow_iw *= 2; +				image_raw->clear(255, 0);  			} -			image_width = pow_iw; -			image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. -			S32 image_height = image_width; - -			image_raw->resize(image_width, image_height, mNumComponents); - -			mBitmapWidth = image_width; -			mBitmapHeight = image_height; -			switch (mNumComponents) -			{ -				case 1: -					image_raw->clear(); -				break; -				case 2: -					image_raw->clear(255, 0); -				break; -			} +			// Make corresponding GL image. +			mImageGLVec[bitmap_idx].push_back(new LLImageGL(image_raw, false)); +			LLImageGL* image_gl = getImageGL(bitmap_type, bitmap_num);  			// Start at beginning of the new image. -			mCurrentOffsetX = 1; -			mCurrentOffsetY = 1; +			mCurrentOffsetX[bitmap_idx] = 1; +			mCurrentOffsetY[bitmap_idx] = 1; -			// Attach corresponding GL texture. -			image_gl->createGLTexture(0, image_raw); +			// Attach corresponding GL texture. (*TODO: is this needed?)  			gGL.getTexUnit(0)->bind(image_gl);  			image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(true, true);  		}  		else  		{  			// Move to next row in current image. -			mCurrentOffsetX = 1; -			mCurrentOffsetY += mMaxCharHeight + 1; +			mCurrentOffsetX[bitmap_idx] = 1; +			mCurrentOffsetY[bitmap_idx] += mMaxCharHeight + 1;  		}  	} -	pos_x = mCurrentOffsetX; -	pos_y = mCurrentOffsetY; -	bitmap_num = mBitmapNum; +	pos_x = mCurrentOffsetX[bitmap_idx]; +	pos_y = mCurrentOffsetY[bitmap_idx]; +	bitmap_num = getNumBitmaps(bitmap_type) - 1; -	mCurrentOffsetX += width + 1; +	mCurrentOffsetX[bitmap_idx] += width + 1;  	return true;  }  void LLFontBitmapCache::destroyGL()  { -	for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec.begin(); -		 it != mImageGLVec.end(); ++it) +	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++)  	{ -		(*it)->destroyGLTexture(); +		for (LLImageGL* image_gl : mImageGLVec[idx]) +		{ +			image_gl->destroyGLTexture(); +		}  	}  }  void LLFontBitmapCache::reset()  { -	mImageRawVec.clear(); - -	mImageGLVec.clear(); +	for (U32 idx = 0, cnt = static_cast<U32>(EFontGlyphType::Count); idx < cnt; idx++) +	{ +		mImageRawVec[idx].clear(); +		mImageGLVec[idx].clear(); +		mCurrentOffsetX[idx] = 1; +		mCurrentOffsetY[idx] = 1; +	}  	mBitmapWidth = 0;  	mBitmapHeight = 0; -	mBitmapNum = -1; -	mCurrentOffsetX = 1; -	mCurrentOffsetY = 1;  } +//static +U32 LLFontBitmapCache::getNumComponents(EFontGlyphType bitmap_type) +{ +	switch (bitmap_type) +	{ +		case EFontGlyphType::Grayscale: +			return 2; +		case EFontGlyphType::Color: +			return 4; +		default: +			llassert(false); +			return 2; +	} +} diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h index 4bbd464cea..25300a8dff 100644 --- a/indra/llrender/llfontbitmapcache.h +++ b/indra/llrender/llfontbitmapcache.h @@ -30,6 +30,14 @@  #include <vector>  #include "lltrace.h" +enum class EFontGlyphType : U32 +{ +	Grayscale = 0, +	Color, +	Count, +	Unspecified, +}; +  // Maintain a collection of bitmaps containing rendered glyphs.  // Generalizes the single-bitmap logic from LLFontFreetype and LLFontGL.  class LLFontBitmapCache @@ -39,35 +47,35 @@ public:  	~LLFontBitmapCache();  	// Need to call this once, before caching any glyphs. - 	void init(S32 num_components, -			  S32 max_char_width, + 	void init(S32 max_char_width,  			  S32 max_char_height);  	void reset(); -	bool nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum); +	bool nextOpenPos(S32 width, S32& posX, S32& posY, EFontGlyphType bitmapType, U32& bitmapNum);  	void destroyGL(); - 	LLImageRaw *getImageRaw(U32 bitmapNum = 0) const; - 	LLImageGL *getImageGL(U32 bitmapNum = 0) const; -	 + 	LLImageRaw* getImageRaw(EFontGlyphType bitmapType, U32 bitmapNum) const; + 	LLImageGL* getImageGL(EFontGlyphType bitmapType, U32 bitmapNum) const; +  	S32 getMaxCharWidth() const { return mMaxCharWidth; } -	S32 getNumComponents() const { return mNumComponents; } +	U32 getNumBitmaps(EFontGlyphType bitmapType) const { return (bitmapType < EFontGlyphType::Count) ? mImageRawVec[static_cast<U32>(bitmapType)].size() : 0; }  	S32 getBitmapWidth() const { return mBitmapWidth; }  	S32 getBitmapHeight() const { return mBitmapHeight; } +protected: +	static U32 getNumComponents(EFontGlyphType bitmap_type); +  private: -	S32 mNumComponents; -	S32 mBitmapWidth; -	S32 mBitmapHeight; -	S32 mBitmapNum; -	S32 mMaxCharWidth; -	S32 mMaxCharHeight; -	S32 mCurrentOffsetX; -	S32 mCurrentOffsetY; -	std::vector<LLPointer<LLImageRaw> >	mImageRawVec; -	std::vector<LLPointer<LLImageGL> > mImageGLVec; +	S32 mBitmapWidth = 0; +	S32 mBitmapHeight = 0; +	S32 mCurrentOffsetX[static_cast<U32>(EFontGlyphType::Count)] = { 1 }; +	S32 mCurrentOffsetY[static_cast<U32>(EFontGlyphType::Count)] = { 1 }; +	S32 mMaxCharWidth = 0; +	S32 mMaxCharHeight = 0; +	std::vector<LLPointer<LLImageRaw>> mImageRawVec[static_cast<U32>(EFontGlyphType::Count)]; +	std::vector<LLPointer<LLImageGL>> mImageGLVec[static_cast<U32>(EFontGlyphType::Count)];  };  #endif //LL_LLFONTBITMAPCACHE_H diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index 3c702dc53d..fcf910dc84 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -34,14 +34,17 @@  #ifdef LL_WINDOWS  #include <freetype2\freetype\ftsystem.h>  #endif +#include "llfontfreetypesvg.h"  // For some reason, this won't work if it's not wrapped in the ifdef  #ifdef FT_FREETYPE_H  #include FT_FREETYPE_H  #endif +#include "lldir.h"  #include "llerror.h"  #include "llimage.h" +#include "llimagepng.h"  //#include "llimagej2c.h"  #include "llmath.h"	// Linden math  #include "llstring.h" @@ -49,6 +52,8 @@  #include "llfontbitmapcache.h"  #include "llgl.h" +#define ENABLE_OT_SVG_SUPPORT +  FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;  LLFontManager *gFontManagerp = NULL; @@ -81,6 +86,16 @@ LLFontManager::LLFontManager()  		LL_ERRS() << "Freetype initialization failure!" << LL_ENDL;  		FT_Done_FreeType(gFTLibrary);  	} + +#ifdef ENABLE_OT_SVG_SUPPORT +	SVG_RendererHooks hooks = { +		LLFontFreeTypeSvgRenderer::OnInit, +		LLFontFreeTypeSvgRenderer::OnFree, +		LLFontFreeTypeSvgRenderer::OnRender, +		LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot, +	}; +	FT_Property_Set(gFTLibrary, "ot-svg", "svg-hooks", &hooks); +#endif  }  LLFontManager::~LLFontManager() @@ -89,8 +104,9 @@ LLFontManager::~LLFontManager()  } -LLFontGlyphInfo::LLFontGlyphInfo(U32 index) +LLFontGlyphInfo::LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type)  :	mGlyphIndex(index), +	mGlyphType(glyph_type),  	mWidth(0),			// In pixels  	mHeight(0),			// In pixels  	mXAdvance(0.f),		// In pixels @@ -99,8 +115,23 @@ LLFontGlyphInfo::LLFontGlyphInfo(U32 index)  	mYBitmapOffset(0), 	// Offset to the origin in the bitmap  	mXBearing(0),		// Distance from baseline to left in pixels  	mYBearing(0),		// Distance from baseline to top in pixels -	mBitmapNum(0) // Which bitmap in the bitmap cache contains this glyph +	mBitmapEntry(std::make_pair(EFontGlyphType::Unspecified, -1)) // Which bitmap in the bitmap cache contains this glyph +{ +} + +LLFontGlyphInfo::LLFontGlyphInfo(const LLFontGlyphInfo& fgi) +	: mGlyphIndex(fgi.mGlyphIndex) +	, mGlyphType(fgi.mGlyphType) +	, mWidth(fgi.mWidth) +	, mHeight(fgi.mHeight) +	, mXAdvance(fgi.mXAdvance) +	, mYAdvance(fgi.mYAdvance) +	, mXBitmapOffset(fgi.mXBitmapOffset) +	, mYBitmapOffset(fgi.mYBitmapOffset) +	, mXBearing(fgi.mXBearing) +	, mYBearing(fgi.mYBearing)  { +	mBitmapEntry = fgi.mBitmapEntry;  }  LLFontFreetype::LLFontFreetype() @@ -156,7 +187,7 @@ void ft_close_cb(FT_Stream stream) {  }  #endif -bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, bool is_fallback, S32 face_n) +bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n)  {  	// Don't leak face objects.  This is also needed to deal with  	// changed font file names. @@ -220,7 +251,7 @@ bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v  	S32 max_char_width = ll_round(0.5f + (x_max - x_min));  	S32 max_char_height = ll_round(0.5f + (y_max - y_min)); -	mFontBitmapCachep->init(components, max_char_width, max_char_height); +	mFontBitmapCachep->init(max_char_width, max_char_height);  	if (!mFTFace->charmap)  	{ @@ -231,7 +262,7 @@ bool LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v  	if (!mIsFallback)  	{  		// Add the default glyph -		addGlyphFromFont(this, 0, 0); +		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);  	}  	mName = filename; @@ -323,14 +354,11 @@ void LLFontFreetype::clearFontStreams()  }  #endif -void LLFontFreetype::setFallbackFonts(const font_vector_t &font) +void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor)  { -	mFallbackFonts = font; -} - -const LLFontFreetype::font_vector_t &LLFontFreetype::getFallbackFonts() const -{ -	return mFallbackFonts; +	// Insert functor fallbacks before generic fallbacks +	mFallbackFonts.insert((functor) ? std::find_if(mFallbackFonts.begin(), mFallbackFonts.end(), [](const fallback_font_t& fe) { return !fe.second; }) : mFallbackFonts.end(), +	                      std::make_pair(fallback_font, functor));  }  F32 LLFontFreetype::getLineHeight() const @@ -354,7 +382,7 @@ F32 LLFontFreetype::getXAdvance(llwchar wch) const  		return 0.0;  	// Return existing info only if it is current -	LLFontGlyphInfo* gi = getGlyphInfo(wch); +	LLFontGlyphInfo* gi = getGlyphInfo(wch, EFontGlyphType::Unspecified);  	if (gi)  	{  		return gi->mXAdvance; @@ -386,10 +414,10 @@ F32 LLFontFreetype::getXKerning(llwchar char_left, llwchar char_right) const  		return 0.0;  	//llassert(!mIsFallback); -	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left);; +	LLFontGlyphInfo* left_glyph_info = getGlyphInfo(char_left, EFontGlyphType::Unspecified);;  	U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;  	// Kern this puppy. -	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right); +	LLFontGlyphInfo* right_glyph_info = getGlyphInfo(char_right, EFontGlyphType::Unspecified);  	U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;  	FT_Vector  delta; @@ -420,60 +448,94 @@ bool LLFontFreetype::hasGlyph(llwchar wch) const  	return(mCharGlyphInfoMap.find(wch) != mCharGlyphInfoMap.end());  } -LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch) const +LLFontGlyphInfo* LLFontFreetype::addGlyph(llwchar wch, EFontGlyphType glyph_type) const  {  	if (mFTFace == NULL)  		return NULL;  	llassert(!mIsFallback); +	llassert(glyph_type < EFontGlyphType::Count);  	//LL_DEBUGS() << "Adding new glyph for " << wch << " to font" << LL_ENDL;  	FT_UInt glyph_index; +	// Fallback fonts with a functor have precedence over everything else +	fallback_font_vector_t::const_iterator it_fallback = mFallbackFonts.cbegin(); +	/* This leads to a bug SL-19831 "Check marks in the menu are less visible." +	** Also, LLFontRegistry::createFont() says: "Fallback fonts don't render" +	for (; it_fallback != mFallbackFonts.cend() && it_fallback->second; ++it_fallback) +	{ +		if (it_fallback->second(wch)) +		{ +			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch); +			if (glyph_index) +			{ +				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type); +			} +		} +	} +	*/ +  	// Initialize char to glyph map  	glyph_index = FT_Get_Char_Index(mFTFace, wch);  	if (glyph_index == 0)  	{  		//LL_INFOS() << "Trying to add glyph from fallback font!" << LL_ENDL; -		font_vector_t::const_iterator iter; -		for(iter = mFallbackFonts.begin(); iter != mFallbackFonts.end(); iter++) +		for (; it_fallback != mFallbackFonts.cend(); ++it_fallback)  		{ -			glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch); +			glyph_index = FT_Get_Char_Index(it_fallback->first->mFTFace, wch);  			if (glyph_index)  			{ -				return addGlyphFromFont(*iter, wch, glyph_index); +				return addGlyphFromFont(it_fallback->first, wch, glyph_index, glyph_type);  			}  		}  	} -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter == mCharGlyphInfoMap.end()) +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); +	char_glyph_info_map_t::iterator iter =  +		std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; }); +	if (iter == range_it.second)  	{ -		return addGlyphFromFont(this, wch, glyph_index); +		return addGlyphFromFont(this, wch, glyph_index, glyph_type);  	}  	return NULL;  } -LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const +LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType requested_glyph_type) const  {      LL_PROFILE_ZONE_SCOPED;  	if (mFTFace == NULL)  		return NULL;  	llassert(!mIsFallback); -	fontp->renderGlyph(glyph_index); +	fontp->renderGlyph(requested_glyph_type, glyph_index); + +	EFontGlyphType bitmap_glyph_type = EFontGlyphType::Unspecified; +	switch (fontp->mFTFace->glyph->bitmap.pixel_mode) +	{ +		case FT_PIXEL_MODE_MONO: +		case FT_PIXEL_MODE_GRAY: +			bitmap_glyph_type = EFontGlyphType::Grayscale; +			break; +		case FT_PIXEL_MODE_BGRA: +			bitmap_glyph_type = EFontGlyphType::Color; +			break; +		default: +			llassert_always(true); +			break; +	}  	S32 width = fontp->mFTFace->glyph->bitmap.width;  	S32 height = fontp->mFTFace->glyph->bitmap.rows;  	S32 pos_x, pos_y; -	S32 bitmap_num; -	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_num); +	U32 bitmap_num; +	mFontBitmapCachep->nextOpenPos(width, pos_x, pos_y, bitmap_glyph_type, bitmap_num);  	mAddGlyphCount++; -	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index); +	LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index, requested_glyph_type);  	gi->mXBitmapOffset = pos_x;  	gi->mYBitmapOffset = pos_y; -	gi->mBitmapNum = bitmap_num; +	gi->mBitmapEntry = std::make_pair(bitmap_glyph_type, bitmap_num);  	gi->mWidth = width;  	gi->mHeight = height;  	gi->mXBearing = fontp->mFTFace->glyph->bitmap_left; @@ -484,8 +546,12 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l  	insertGlyphInfo(wch, gi); -	llassert(fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO -	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); +	if (requested_glyph_type != bitmap_glyph_type) +	{ +		LLFontGlyphInfo* gi_temp = new LLFontGlyphInfo(*gi); +		gi_temp->mGlyphType = bitmap_glyph_type; +		insertGlyphInfo(wch, gi_temp); +	}  	if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO  	    || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) @@ -520,78 +586,95 @@ LLFontGlyphInfo* LLFontFreetype::addGlyphFromFont(const LLFontFreetype *fontp, l  			buffer_row_stride = width;  		} -		switch (mFontBitmapCachep->getNumComponents()) -		{ -		case 1: -			mFontBitmapCachep->getImageRaw(bitmap_num)->setSubImage(pos_x, -																	pos_y, -																	width, -																	height, -																	buffer_data, -																	buffer_row_stride, -																	true); -			break; -		case 2: -			setSubImageLuminanceAlpha(pos_x,	 -									  pos_y, -									  bitmap_num, -									  width, -									  height, -									  buffer_data, -									  buffer_row_stride); -			break; -		default: -			break; -		} +		setSubImageLuminanceAlpha(pos_x, +									pos_y, +									bitmap_num, +									width, +									height, +									buffer_data, +									buffer_row_stride);  		if (tmp_graydata)  			delete[] tmp_graydata; +	} +	else if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) +	{ +		setSubImageBGRA(pos_x, +		                pos_y, +		                bitmap_num, +		                fontp->mFTFace->glyph->bitmap.width, +		                fontp->mFTFace->glyph->bitmap.rows, +		                fontp->mFTFace->glyph->bitmap.buffer, +		                llabs(fontp->mFTFace->glyph->bitmap.pitch));  	} else { -		// we don't know how to handle this pixel format from FreeType; -		// omit it from the font-image. +		llassert(false);  	} -	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_num); -	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); +	LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_glyph_type, bitmap_num); +	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_glyph_type, bitmap_num);  	image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight());  	return gi;  } -LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch) const +LLFontGlyphInfo* LLFontFreetype::getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const  { -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter != mCharGlyphInfoMap.end()) +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); + +	char_glyph_info_map_t::iterator iter = (EFontGlyphType::Unspecified != glyph_type) +		? std::find_if(range_it.first, range_it.second, [&glyph_type](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == glyph_type; }) +		: range_it.first; +	if (iter != range_it.second)  	{  		return iter->second;  	}  	else  	{  		// this glyph doesn't yet exist, so render it and return the result -		return addGlyph(wch); +		return addGlyph(wch, (EFontGlyphType::Unspecified != glyph_type) ? glyph_type : EFontGlyphType::Grayscale);  	}  }  void LLFontFreetype::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const  { -	char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch); -	if (iter != mCharGlyphInfoMap.end()) +	llassert(gi->mGlyphType < EFontGlyphType::Count); +	std::pair<char_glyph_info_map_t::iterator, char_glyph_info_map_t::iterator> range_it = mCharGlyphInfoMap.equal_range(wch); + +	char_glyph_info_map_t::iterator iter = +		std::find_if(range_it.first, range_it.second, [&gi](const char_glyph_info_map_t::value_type& entry) { return entry.second->mGlyphType == gi->mGlyphType; }); +	if (iter != range_it.second)  	{  		delete iter->second;  		iter->second = gi;  	}  	else  	{ -		mCharGlyphInfoMap[wch] = gi; +		mCharGlyphInfoMap.insert(std::make_pair(wch, gi));  	}  } -void LLFontFreetype::renderGlyph(U32 glyph_index) const +void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const  {  	if (mFTFace == NULL)  		return; -	llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, FT_LOAD_FORCE_AUTOHINT) ); +	FT_Int32 load_flags = FT_LOAD_FORCE_AUTOHINT; +	if (EFontGlyphType::Color == bitmap_type) +	{ +		// We may not actually get a color render so our caller should always examine mFTFace->glyph->bitmap.pixel_mode +		load_flags |= FT_LOAD_COLOR; +	} + +	FT_Error error = FT_Load_Glyph(mFTFace, glyph_index, load_flags); +	if (FT_Err_Ok != error) +	{ +		std::string message = llformat( +			"Error %d (%s) loading glyph %u: bitmap_type=%u, load_flags=%d", +			error, FT_Error_String(error), glyph_index, bitmap_type, load_flags); +		LL_WARNS_ONCE() << message << LL_ENDL; +		error = FT_Load_Glyph(mFTFace, glyph_index, load_flags ^ FT_LOAD_COLOR); +		llassert_always_msg(FT_Err_Ok == error, message.c_str()); +	}  	llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) ); @@ -601,7 +684,7 @@ void LLFontFreetype::renderGlyph(U32 glyph_index) const  void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)  {  	resetBitmapCache();  -	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mFontBitmapCachep->getNumComponents(), mIsFallback); +	loadFace(mName, mPointSize, vert_dpi ,horz_dpi, mIsFallback, 0);  	if (!mIsFallback)  	{  		// This is the head of the list - need to rebuild ourself and all fallbacks. @@ -611,11 +694,9 @@ void LLFontFreetype::reset(F32 vert_dpi, F32 horz_dpi)  		}  		else  		{ -			for(font_vector_t::iterator it = mFallbackFonts.begin(); -				it != mFallbackFonts.end(); -				++it) +			for (fallback_font_vector_t::iterator it = mFallbackFonts.begin(); it != mFallbackFonts.end(); ++it)  			{ -				(*it)->reset(vert_dpi, horz_dpi); +				it->first->reset(vert_dpi, horz_dpi);  			}  		}  	} @@ -637,7 +718,7 @@ void LLFontFreetype::resetBitmapCache()  	if(!mIsFallback)  	{  		// Add the empty glyph -		addGlyphFromFont(this, 0, 0); +		addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale);  	}  } @@ -651,6 +732,34 @@ const std::string &LLFontFreetype::getName() const  	return mName;  } +static void dumpFontBitmap(const LLImageRaw* image_raw, const std::string& file_name) +{ +	LLPointer<LLImagePNG> tmpImage = new LLImagePNG(); +	if ( (tmpImage->encode(image_raw, 0.0f)) && (tmpImage->save(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name))) ) +	{ +		LL_INFOS("Font") << "Successfully saved " << file_name << LL_ENDL; +	} +	else +	{ +		LL_WARNS("Font") << "Failed to save " << file_name << LL_ENDL; +	} +} + +void LLFontFreetype::dumpFontBitmaps() const +{ +	// Dump all the regular bitmaps (if any) +	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Grayscale); idx < cnt; idx++) +	{ +		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, idx), llformat("%s_%d_%d_%d.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx)); +	} + +	// Dump all the color bitmaps (if any) +	for (int idx = 0, cnt = mFontBitmapCachep->getNumBitmaps(EFontGlyphType::Color); idx < cnt; idx++) +	{ +		dumpFontBitmap(mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, idx), llformat("%s_%d_%d_%d_clr.png", mFTFace->family_name, (int)(mPointSize * 10), mStyle, idx)); +	} +} +  const LLFontBitmapCache* LLFontFreetype::getFontBitmapCache() const  {  	return mFontBitmapCachep; @@ -666,18 +775,47 @@ U8 LLFontFreetype::getStyle() const  	return mStyle;  } +bool LLFontFreetype::setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const +{ +	LLImageRaw* image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Color, bitmap_num); +	llassert(!mIsFallback); +	llassert(image_raw && (image_raw->getComponents() == 4)); + +	// NOTE: inspired by LLImageRaw::setSubImage() +	U32* image_data = (U32*)image_raw->getData(); +	if (!image_data) +	{ +		return false; +	} + +	for (U32 idxRow = 0; idxRow < height; idxRow++) +	{ +		const U32 nSrcRow = height - 1 - idxRow; +		const U32 nSrcOffset = nSrcRow * width * image_raw->getComponents(); +		const U32 nDstOffset = (y + idxRow) * image_raw->getWidth() + x; + +		for (U32 idxCol = 0; idxCol < width; idxCol++) +		{ +			U32 nTemp = nSrcOffset + idxCol * 4; +			image_data[nDstOffset + idxCol] = data[nTemp + 3] << 24 | data[nTemp] << 16 | data[nTemp + 1] << 8 | data[nTemp + 2]; +		} +	} + +	return true; +} +  void LLFontFreetype::setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride) const  { -	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); +	LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(EFontGlyphType::Grayscale, bitmap_num);  	LLImageDataLock lock(image_raw);  	llassert(!mIsFallback);  	llassert(image_raw && (image_raw->getComponents() == 2)); -  	U8 *target = image_raw->getData(); +    llassert(target); -	if (!data) +	if (!data || !target)  	{  		return;  	} diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h index 543c6fbd8e..9800db6f53 100644 --- a/indra/llrender/llfontfreetype.h +++ b/indra/llrender/llfontfreetype.h @@ -56,9 +56,11 @@ private:  struct LLFontGlyphInfo  { -	LLFontGlyphInfo(U32 index); +	LLFontGlyphInfo(U32 index, EFontGlyphType glyph_type); +	LLFontGlyphInfo(const LLFontGlyphInfo& fgi);  	U32 mGlyphIndex; +	EFontGlyphType mGlyphType;  	// Metrics  	S32 mWidth;			// In pixels @@ -71,7 +73,7 @@ struct LLFontGlyphInfo  	S32 mYBitmapOffset; // Offset to the origin in the bitmap  	S32 mXBearing;	// Distance from baseline to left in pixels  	S32 mYBearing;	// Distance from baseline to top in pixels -	S32 mBitmapNum; // Which bitmap in the bitmap cache contains this glyph +	std::pair<EFontGlyphType, S32> mBitmapEntry; // Which bitmap in the bitmap cache contains this glyph  };  extern LLFontManager *gFontManagerp; @@ -84,7 +86,7 @@ public:  	// is_fallback should be true for fallback fonts that aren't used  	// to render directly (Unicode backup, primarily) -	bool loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, bool is_fallback, S32 face_n = 0); +	bool loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, bool is_fallback, S32 face_n);  	S32 getNumFaces(const std::string& filename); @@ -93,10 +95,8 @@ public:  	void clearFontStreams();  #endif -	typedef std::vector<LLPointer<LLFontFreetype> > font_vector_t; - -	void setFallbackFonts(const font_vector_t &font); -	const font_vector_t &getFallbackFonts() const; +	typedef std::function<bool(llwchar)> char_functor_t; +	void addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor = nullptr);  	// Global font metrics - in units of pixels  	F32 getLineHeight() const; @@ -135,7 +135,7 @@ public:  	F32 getXKerning(llwchar char_left, llwchar char_right) const; // Get the kerning between the two characters  	F32 getXKerning(const LLFontGlyphInfo* left_glyph_info, const LLFontGlyphInfo* right_glyph_info) const; // Get the kerning between the two characters -	LLFontGlyphInfo* getGlyphInfo(llwchar wch) const; +	LLFontGlyphInfo* getGlyphInfo(llwchar wch, EFontGlyphType glyph_type) const;  	void reset(F32 vert_dpi, F32 horz_dpi); @@ -143,6 +143,7 @@ public:  	const std::string& getName() const; +	void       dumpFontBitmaps() const;  	const LLFontBitmapCache* getFontBitmapCache() const;  	void setStyle(U8 style); @@ -151,10 +152,11 @@ public:  private:  	void resetBitmapCache();  	void setSubImageLuminanceAlpha(U32 x, U32 y, U32 bitmap_num, U32 width, U32 height, U8 *data, S32 stride = 0) const; +	bool setSubImageBGRA(U32 x, U32 y, U32 bitmap_num, U16 width, U16 height, const U8* data, U32 stride) const;  	bool hasGlyph(llwchar wch) const;		// Has a glyph for this character -	LLFontGlyphInfo* addGlyph(llwchar wch) const;		// Add a new character to the font if necessary -	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found) -	void renderGlyph(U32 glyph_index) const; +	LLFontGlyphInfo* addGlyph(llwchar wch, EFontGlyphType glyph_type) const;		// Add a new character to the font if necessary +	LLFontGlyphInfo* addGlyphFromFont(const LLFontFreetype *fontp, llwchar wch, U32 glyph_index, EFontGlyphType bitmap_type) const;	// Add a glyph from this font to the other (returns the glyph_index, 0 if not found) +	void renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) const;  	void insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const;  	std::string mName; @@ -174,9 +176,12 @@ private:  #endif  	bool mIsFallback; -	font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) +	typedef std::pair<LLPointer<LLFontFreetype>, char_functor_t> fallback_font_t; +	typedef std::vector<fallback_font_t> fallback_font_vector_t; +	fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) -	typedef boost::unordered_map<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t; +	// *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique) +	typedef boost::unordered_multimap<llwchar, LLFontGlyphInfo*> char_glyph_info_map_t;  	mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap  	mutable LLFontBitmapCache* mFontBitmapCachep; diff --git a/indra/llrender/llfontfreetypesvg.cpp b/indra/llrender/llfontfreetypesvg.cpp new file mode 100644 index 0000000000..19d327a4c9 --- /dev/null +++ b/indra/llrender/llfontfreetypesvg.cpp @@ -0,0 +1,205 @@ +/** + * @file llfontfreetypesvg.cpp + * @brief Freetype font library SVG glyph rendering + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "linden_common.h" + +#include "llfontfreetypesvg.h" + +#if LL_WINDOWS +#pragma warning (push) +#pragma warning (disable : 4702) +#endif + +#define NANOSVG_IMPLEMENTATION +#include <nanosvg/nanosvg.h> +#define NANOSVGRAST_IMPLEMENTATION +#include <nanosvg/nanosvgrast.h> + +#if LL_WINDOWS +#pragma warning (pop) +#endif + +struct LLSvgRenderData +{ +	FT_UInt    GlyphIndex = 0; +	FT_Error   Error = FT_Err_Ok; // FreeType currently (@2.12.1) ignores the error value returned by the preset glyph slot callback so we return it at render time +	// (See https://github.com/freetype/freetype/blob/5faa1df8b93ebecf0f8fd5fe8fda7b9082eddced/src/base/ftobjs.c#L1170) +	NSVGimage* pNSvgImage = nullptr; +	float      Scale = 0.f; +}; + +// static +FT_Error LLFontFreeTypeSvgRenderer::OnInit(FT_Pointer* state) +{ +	// The SVG driver hook state is shared across all callback invocations; since our state is lightweight +	// we store it in the glyph instead. +	*state = nullptr; + +	return FT_Err_Ok; +} + +// static +void LLFontFreeTypeSvgRenderer::OnFree(FT_Pointer* state) +{ +} + +// static +void LLFontFreeTypeSvgRenderer::OnDataFinalizer(void* objectp) +{ +	FT_GlyphSlot glyph_slot = static_cast<FT_GlyphSlot>(objectp); + +	LLSvgRenderData* pData = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	glyph_slot->generic.data = nullptr; +	glyph_slot->generic.finalizer = nullptr; +	delete(pData); +} + +//static +FT_Error LLFontFreeTypeSvgRenderer::OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer*) +{ +	FT_SVG_Document document = static_cast<FT_SVG_Document>(glyph_slot->other); + +	llassert(!glyph_slot->generic.data || !cache || glyph_slot->glyph_index == ((LLSvgRenderData*)glyph_slot->generic.data)->GlyphIndex); +	if (!glyph_slot->generic.data) +	{ +		glyph_slot->generic.data = new LLSvgRenderData(); +		glyph_slot->generic.finalizer = LLFontFreeTypeSvgRenderer::OnDataFinalizer; +	} +	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	if (!cache) +	{ +		datap->GlyphIndex = glyph_slot->glyph_index; +		datap->Error = FT_Err_Ok; +	} + +	// NOTE: nsvgParse modifies the input string so we need a temporary copy +	llassert(!datap->pNSvgImage || cache); +	if (!datap->pNSvgImage) +	{ +		char* document_buffer = new char[document->svg_document_length + 1]; +		memcpy(document_buffer, document->svg_document, document->svg_document_length); +		document_buffer[document->svg_document_length] = '\0'; + +		datap->pNSvgImage = nsvgParse(document_buffer, "px", 0.); + +		delete[] document_buffer; +	} + +	if (!datap->pNSvgImage) +	{ +		datap->Error = FT_Err_Invalid_SVG_Document; +		return FT_Err_Invalid_SVG_Document; +	} + +	// We don't (currently) support transformations so test for an identity rotation matrix + zero translation +	if (document->transform.xx != 1 << 16 || document->transform.yx != 0 || +		document->transform.xy != 0 || document->transform.yy != 1 << 16 || +		document->delta.x > 0 || document->delta.y > 0) +	{ +		datap->Error = FT_Err_Unimplemented_Feature; +		return FT_Err_Unimplemented_Feature; +	} + +	float svg_width = datap->pNSvgImage->width; +	float svg_height = datap->pNSvgImage->height; +	if (svg_width == 0.f || svg_height == 0.f) +	{ +		svg_width = document->units_per_EM; +		svg_height = document->units_per_EM; +	} + +	float svg_x_scale = (float)document->metrics.x_ppem / floorf(svg_width); +	float svg_y_scale = (float)document->metrics.y_ppem / floorf(svg_height); +	float svg_scale = llmin(svg_x_scale, svg_y_scale); +	datap->Scale = svg_scale; + +	glyph_slot->bitmap.width = floorf(svg_width) * svg_scale; +	glyph_slot->bitmap.rows = floorf(svg_height) * svg_scale; +	glyph_slot->bitmap_left = (document->metrics.x_ppem - glyph_slot->bitmap.width) / 2; +	glyph_slot->bitmap_top = glyph_slot->face->size->metrics.ascender / 64.f; +	glyph_slot->bitmap.pitch = glyph_slot->bitmap.width * 4; +	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; + +	/* Copied as-is from fcft (MIT license) */ + +	// Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box. +	float horiBearingX = 0.; +	float horiBearingY = -glyph_slot->bitmap_top; + +	// XXX parentheses correct? +	float vertBearingX = glyph_slot->metrics.horiBearingX / 64.0f - glyph_slot->metrics.horiAdvance / 64.0f / 2; +	float vertBearingY = (glyph_slot->metrics.vertAdvance / 64.0f - glyph_slot->metrics.height / 64.0f) / 2; + +	// Do conversion in two steps to avoid 'bad function cast' warning +	glyph_slot->metrics.width = glyph_slot->bitmap.width * 64; +	glyph_slot->metrics.height = glyph_slot->bitmap.rows * 64; +	glyph_slot->metrics.horiBearingX = horiBearingX * 64; +	glyph_slot->metrics.horiBearingY = horiBearingY * 64; +	glyph_slot->metrics.vertBearingX = vertBearingX * 64; +	glyph_slot->metrics.vertBearingY = vertBearingY * 64; +	if (glyph_slot->metrics.vertAdvance == 0) +	{ +		glyph_slot->metrics.vertAdvance = glyph_slot->bitmap.rows * 1.2f * 64; +	} + +	return FT_Err_Ok; +} + +// static +FT_Error LLFontFreeTypeSvgRenderer::OnRender(FT_GlyphSlot glyph_slot, FT_Pointer*) +{ +	LLSvgRenderData* datap = static_cast<LLSvgRenderData*>(glyph_slot->generic.data); +	llassert(FT_Err_Ok == datap->Error); +	if (FT_Err_Ok != datap->Error) +	{ +		return datap->Error; +	} + +	// Render to glyph bitmap +	NSVGrasterizer* nsvgRasterizer = nsvgCreateRasterizer(); +	nsvgRasterize(nsvgRasterizer, datap->pNSvgImage, 0, 0, datap->Scale, glyph_slot->bitmap.buffer, glyph_slot->bitmap.width, glyph_slot->bitmap.rows, glyph_slot->bitmap.pitch); +	nsvgDeleteRasterizer(nsvgRasterizer); +	nsvgDelete(datap->pNSvgImage); +	datap->pNSvgImage = nullptr; + +	// Convert from RGBA to BGRA +	U32* pixel_buffer = (U32*)glyph_slot->bitmap.buffer; U8* byte_buffer = glyph_slot->bitmap.buffer; +	for (size_t y = 0, h = glyph_slot->bitmap.rows; y < h; y++) +	{ +		for (size_t x = 0, w = glyph_slot->bitmap.pitch / 4; x < w; x++) +		{ +			size_t pixel_idx = y * w + x; +			size_t byte_idx = pixel_idx * 4; +			U8 alpha = byte_buffer[byte_idx + 3]; +			// Store as ARGB (*TODO - do we still have to care about endianness?) +			pixel_buffer[y * w + x] = alpha << 24 | (byte_buffer[byte_idx] * alpha / 0xFF) << 16 | (byte_buffer[byte_idx + 1] * alpha / 0xFF) << 8 | (byte_buffer[byte_idx + 2] * alpha / 0xFF); +		} +	} + +	glyph_slot->format = FT_GLYPH_FORMAT_BITMAP; +	glyph_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; +	return FT_Err_Ok; +} diff --git a/indra/llrender/llfontfreetypesvg.h b/indra/llrender/llfontfreetypesvg.h new file mode 100644 index 0000000000..b5f541991a --- /dev/null +++ b/indra/llrender/llfontfreetypesvg.h @@ -0,0 +1,54 @@ +/** + * @file llfontfreetypesvg.h + * @brief Freetype font library SVG glyph rendering + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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$ + */ + +#pragma once + +#include <ft2build.h> +#include FT_TYPES_H +#include FT_MODULE_H +#include FT_OTSVG_H + + // See https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html +class LLFontFreeTypeSvgRenderer +{ +public: +	// Called when the very first OT-SVG glyph is rendered (across the entire lifetime of our FT_Library object) +	static FT_Error OnInit(FT_Pointer* state); + +	// Called when the ot-svg module is being freed (but only called if the init hook was called previously) +	static void     OnFree(FT_Pointer* state); + +	// Called to preset the glyph slot, twice per glyph: +	//   - when FT_Load_Glyph needs to preset the glyph slot (with cache == false) +	//   - right before the svg module calls the render callback hook. (with cache == true) +	static FT_Error OnPresetGlypthSlot(FT_GlyphSlot glyph_slot, FT_Bool cache, FT_Pointer* state); + +	// Called to render an OT-SVG glyph (right after the preset hook OnPresetGlypthSlot was called with cache set to TRUE) +	static FT_Error OnRender(FT_GlyphSlot glyph_slot, FT_Pointer* state); + +	// Called to deallocate our per glyph slot data +	static void OnDataFinalizer(void* objectp); +}; diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 4dfc6f2021..3bdbccb252 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -89,14 +89,14 @@ void LLFontGL::destroyGL()  	mFontFreetype->destroyGL();  } -bool LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, bool is_fallback, S32 face_n) +bool LLFontGL::loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n)  {  	if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))  	{  		mFontFreetype = new LLFontFreetype;  	} -	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback, face_n); +	return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, is_fallback, face_n);  }  S32 LLFontGL::getNumFaces(const std::string& filename) @@ -110,14 +110,14 @@ S32 LLFontGL::getNumFaces(const std::string& filename)  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, -    ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses) const +    ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses, bool use_color) const  {      LLRectf rect_float(rect.mLeft, rect.mTop, rect.mRight, rect.mBottom); -    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses); +    return render(wstr, begin_offset, rect_float, color, halign, valign, style, shadow, max_chars, right_x, use_ellipses, use_color);  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,  -					 ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses) const +					 ShadowType shadow, S32 max_chars, F32* right_x, bool use_ellipses, bool use_color) const  {  	F32 x = rect.mLeft;  	F32 y = 0.f; @@ -138,12 +138,12 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRectf& rec  		y = rect.mBottom;  		break;  	} -	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses); +	return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses, use_color);  }  S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,  -					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses) const +					 ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses, bool use_color) const  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; @@ -193,7 +193,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  	if (-1 == max_chars)  	{ -		length = (S32)wstr.length() - begin_offset; +		max_chars = length = (S32)wstr.length() - begin_offset;  	}  	else  	{ @@ -254,7 +254,6 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  	const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; -  	bool draw_ellipses = false;  	if (use_ellipses)  	{ @@ -278,7 +277,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  	LLColor4U text_color(color); -	S32 bitmap_num = -1; +	std::pair<EFontGlyphType, S32> bitmap_entry = std::make_pair(EFontGlyphType::Grayscale, -1);  	S32 glyph_count = 0;  	for (i = begin_offset; i < begin_offset + length; i++)  	{ @@ -288,7 +287,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);  		}  		if (!fgi)  		{ @@ -296,8 +295,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  			break;  		}  		// Per-glyph bitmap texture. -		S32 next_bitmap_num = fgi->mBitmapNum; -		if (next_bitmap_num != bitmap_num) +		std::pair<EFontGlyphType, S32> next_bitmap_entry = fgi->mBitmapEntry; +		if (next_bitmap_entry != bitmap_entry)  		{  			// Actually draw the queued glyphs before switching their texture;  			// otherwise the queued glyphs will be taken from wrong textures. @@ -311,8 +310,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  				glyph_count = 0;  			} -			bitmap_num = next_bitmap_num; -			LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); +			bitmap_entry = next_bitmap_entry; +			LLImageGL* font_image = font_bitmap_cache->getImageGL(bitmap_entry.first, bitmap_entry.second);  			gGL.getTexUnit(0)->bind(font_image);  		} @@ -345,7 +344,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  			glyph_count = 0;  		} -		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); +		drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, (bitmap_entry.first == EFontGlyphType::Grayscale) ? text_color : LLColor4U::white, style_to_add, shadow, drop_shadow_strength);  		chars_drawn++;  		cur_x += fgi->mXAdvance; @@ -355,7 +354,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  		if (next_char && (next_char < LAST_CHARACTER))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(next_char); +			next_glyph = mFontFreetype->getGlyphInfo(next_char, (!use_color) ? EFontGlyphType::Grayscale : EFontGlyphType::Color);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		} @@ -409,7 +408,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  				shadow,  				S32_MAX, max_pixels,  				right_x, -				false);  +				false, +				use_color);   		gGL.popUIMatrix();  	} @@ -420,22 +420,22 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons  S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const  { -	return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, false); +	return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);  } -S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, bool use_ellipses) const +S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, bool use_ellipses, bool use_color) const  { -	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses); +	return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);  }  S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const  { -	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, false); +	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);  }  S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const  { -	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, false); +	return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow);  }  // font metrics - override for LLFontFreetype that returns units of virtual pixels @@ -488,7 +488,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars) const  	return getWidthF32(wchars, 0, S32_MAX);  } -F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars ) const +F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars) const  {  	LLWString wtext = utf8str_to_wstring(utf8text);  	return getWidthF32(wtext.c_str(), begin_offset, max_chars); @@ -512,7 +512,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		}  		F32 advance = mFontFreetype->getXAdvance(fgi); @@ -535,7 +535,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars  			&& (next_char < LAST_CHARACTER))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(next_char); +			next_glyph = mFontFreetype->getGlyphInfo(next_char, EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		}  		// Round after kerning. @@ -556,7 +556,7 @@ void LLFontGL::generateASCIIglyphs()      LL_PROFILE_ZONE_SCOPED_CATEGORY_UI      for (U32 i = 32; (i < 127); i++)      { -        mFontFreetype->getGlyphInfo(i); +        mFontFreetype->getGlyphInfo(i, EFontGlyphType::Grayscale);      }  } @@ -630,7 +630,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch  		next_glyph = NULL;  		if(!fgi)  		{ -			fgi = mFontFreetype->getGlyphInfo(wch); +			fgi = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  			if (NULL == fgi)  			{ @@ -655,7 +655,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch  		if (((i+1) < max_chars) && wchars[i+1])  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1]); +			next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1], EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(fgi, next_glyph);  		} @@ -702,7 +702,7 @@ S32	LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_  	{  		llwchar wch = wchars[i]; -		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); +		const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		// last character uses character width, since the whole character needs to be visible  		// other characters just use advance @@ -777,7 +777,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t  		next_glyph = NULL;  		if(!glyph)  		{ -			glyph = mFontFreetype->getGlyphInfo(wch); +			glyph = mFontFreetype->getGlyphInfo(wch, EFontGlyphType::Unspecified);  		}  		F32 char_width = mFontFreetype->getXAdvance(glyph); @@ -807,7 +807,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t  			&& (wchars[(pos + 1)]))  		{  			// Kern this puppy. -			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]); +			next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1], EFontGlyphType::Unspecified);  			cur_x += mFontFreetype->getXKerning(glyph, next_glyph);  		} @@ -847,6 +847,26 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st  	LLFontGL::loadDefaultFonts();  } +void LLFontGL::dumpTextures() +{ +	if (mFontFreetype.notNull()) +	{ +		mFontFreetype->dumpFontBitmaps(); +	} +} + +// static +void LLFontGL::dumpFonts() +{ +	sFontRegistry->dump(); +} + +// static +void LLFontGL::dumpFontTextures() +{ +	sFontRegistry->dumpTextures(); +} +  // Force standard fonts to get generated up front.  // This is primarily for error detection purposes.  // Don't do this during initClass because it can be slow and we want to get @@ -1010,6 +1030,20 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name)  }  //static +LLFontGL* LLFontGL::getFontEmoji() +{ +	static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Large", 0)); +	return fontp;; +} + +//static +LLFontGL* LLFontGL::getFontEmojiHuge() +{ +	static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Huge", 0)); +	return fontp;; +} + +//static  LLFontGL* LLFontGL::getFontMonospace()  {  	static LLFontGL* fontp = getFont(LLFontDescriptor("Monospace","Monospace",0)); diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 551e704aca..87d86c925c 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -87,7 +87,7 @@ public:  	void destroyGL(); -	bool loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, bool is_fallback, S32 face_n = 0); +	bool loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, bool is_fallback, S32 face_n);  	S32 getNumFaces(const std::string& filename); @@ -98,7 +98,8 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX,  				F32* right_x=NULL,  -				bool use_ellipses = false) const; +				bool use_ellipses = false, +				bool use_color = true) const;  	S32 render(const LLWString &text, S32 begin_offset,   				const LLRectf& rect,  @@ -107,7 +108,8 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX,  				F32* right_x=NULL,  -				bool use_ellipses = false) const; +				bool use_ellipses = false, +				bool use_color = true) const;  	S32 render(const LLWString &text, S32 begin_offset,   				F32 x, F32 y,  @@ -116,12 +118,13 @@ public:  				U8 style = NORMAL, ShadowType shadow = NO_SHADOW,   				S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,   				F32* right_x=NULL,  -				bool use_ellipses = false) const; +				bool use_ellipses = false, +				bool use_color = true) const;  	S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const;  	// renderUTF8 does a conversion, so is slower! -	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels,  F32* right_x, bool use_ellipses) const; +	S32 renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign,  VAlign valign, U8 style, ShadowType shadow, S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,  F32* right_x = NULL, bool use_ellipses = false, bool use_color = true) const;  	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const;  	S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const; @@ -132,12 +135,12 @@ public:  	S32 getWidth(const std::string& utf8text) const;  	S32 getWidth(const llwchar* wchars) const; -	S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars ) const; +	S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars) const;  	S32 getWidth(const llwchar* wchars, S32 offset, S32 max_chars) const;  	F32 getWidthF32(const std::string& utf8text) const;  	F32 getWidthF32(const llwchar* wchars) const; -	F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars ) const; +	F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars) const;  	F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars, bool no_padding = false) const;  	// The following are called often, frequently with large buffers, so do not use a string interface @@ -165,6 +168,10 @@ public:  	static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true); +	       void dumpTextures(); +	static void dumpFonts(); +	static void dumpFontTextures(); +  	// Load sans-serif, sans-serif-small, etc.  	// Slow, requires multiple seconds to load fonts.  	static bool loadDefaultFonts(); @@ -187,6 +194,8 @@ public:  	static void setFontDisplay(bool flag) { sDisplayFont = flag; } +	static LLFontGL* getFontEmoji(); +	static LLFontGL* getFontEmojiHuge();  	static LLFontGL* getFontMonospace();  	static LLFontGL* getFontSansSerifSmall();      static LLFontGL* getFontSansSerifSmallBold(); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index ae27fcbe48..edd1f1047a 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -47,6 +47,10 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node);  const std::string MACOSX_FONT_PATH_LIBRARY = "/Library/Fonts/";  const std::string MACOSX_FONT_SUPPLEMENTAL = "Supplemental/"; +LLFontDescriptor::char_functor_map_t LLFontDescriptor::mCharFunctors({ +	{ "is_emoji", LLStringOps::isEmoji } +}); +  LLFontDescriptor::LLFontDescriptor():  	mStyle(0)  { @@ -55,22 +59,22 @@ LLFontDescriptor::LLFontDescriptor():  LLFontDescriptor::LLFontDescriptor(const std::string& name,  								   const std::string& size,   								   const U8 style, -								   const string_vec_t& file_names): +								   const font_file_info_vec_t& font_files):  	mName(name),  	mSize(size),  	mStyle(style), -	mFileNames(file_names) +	mFontFiles(font_files)  {  }  LLFontDescriptor::LLFontDescriptor(const std::string& name,  	const std::string& size,  	const U8 style, -	const string_vec_t& file_names, -	const string_vec_t& ft_collection_listections) : -	LLFontDescriptor(name, size, style, file_names) +	const font_file_info_vec_t& font_list, +	const font_file_info_vec_t& font_collection_files) : +	LLFontDescriptor(name, size, style, font_list)  { -	mFontCollectionsList = ft_collection_listections; +	mFontCollectionFiles = font_collection_files;  }  LLFontDescriptor::LLFontDescriptor(const std::string& name, @@ -82,7 +86,6 @@ LLFontDescriptor::LLFontDescriptor(const std::string& name,  {  } -  bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const  {  	if (mName < b.mName) @@ -175,7 +178,19 @@ LLFontDescriptor LLFontDescriptor::normalize() const  	if (removeSubString(new_name,"Italic"))  		new_style |= LLFontGL::ITALIC; -	return LLFontDescriptor(new_name,new_size,new_style,getFileNames(),getFontCollectionsList()); +	return LLFontDescriptor(new_name,new_size,new_style, getFontFiles(), getFontCollectionFiles()); +} + +void LLFontDescriptor::addFontFile(const std::string& file_name, const std::string& char_functor) +{ +	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor); +	mFontFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr)); +} + +void LLFontDescriptor::addFontCollectionFile(const std::string& file_name, const std::string& char_functor) +{ +	char_functor_map_t::const_iterator it = mCharFunctors.find(char_functor); +	mFontCollectionFiles.push_back(LLFontFileInfo(file_name, (mCharFunctors.end() != it) ? it->second : nullptr));  }  LLFontRegistry::LLFontRegistry(bool create_gl_textures) @@ -273,17 +288,24 @@ bool font_desc_init_from_xml(LLXMLNodePtr node, LLFontDescriptor& desc)  		if (child->hasName("file"))  		{  			std::string font_file_name = child->getTextContents(); -			desc.getFileNames().push_back(font_file_name); -			 +			std::string char_functor; + +			if (child->hasAttribute("functor")) +			{ +				child->getAttributeString("functor", char_functor); +			} +  			if (child->hasAttribute("load_collection"))  			{  				bool col = false;  				child->getAttributeBOOL("load_collection", col);  				if (col)  				{ -					desc.getFontCollectionsList().push_back(font_file_name); +					desc.addFontCollectionFile(font_file_name, char_functor);  				}  			} + +			desc.addFontFile(font_file_name, char_functor);  		}  		else if (child->hasName("os"))  		{ @@ -326,19 +348,19 @@ bool init_from_xml(LLFontRegistry* registry, LLXMLNodePtr node)  					// A little roundabout because the map key is const,  					// so we have to fetch it, make a new map key, and  					// replace the old entry. -					string_vec_t match_file_names = match_desc->getFileNames(); -					match_file_names.insert(match_file_names.begin(), -											desc.getFileNames().begin(), -											desc.getFileNames().end()); +					font_file_info_vec_t font_files = match_desc->getFontFiles(); +					font_files.insert(font_files.begin(), +									  desc.getFontFiles().begin(), +									  desc.getFontFiles().end()); -					string_vec_t collections_list = match_desc->getFontCollectionsList(); -					collections_list.insert(collections_list.begin(), -						desc.getFontCollectionsList().begin(), -						desc.getFontCollectionsList().end()); +					font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles(); +					font_collection_files.insert(font_collection_files.begin(), +						desc.getFontCollectionFiles().begin(), +						desc.getFontCollectionFiles().end());  					LLFontDescriptor new_desc = *match_desc; -					new_desc.getFileNames() = match_file_names; -					new_desc.getFontCollectionsList() = collections_list; +					new_desc.setFontFiles(font_files); +					new_desc.setFontCollectionFiles(font_collection_files);  					registry->mFontMap.erase(*match_desc);  					registry->mFontMap[new_desc] = NULL;  				} @@ -423,82 +445,80 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  	// Build list of font names to look for.  	// Files specified for this font come first, followed by those from the default descriptor. -	string_vec_t file_names = match_desc->getFileNames(); -	string_vec_t ft_collection_list = match_desc->getFontCollectionsList(); -	string_vec_t default_file_names; +	font_file_info_vec_t font_files = match_desc->getFontFiles(); +	font_file_info_vec_t font_collection_files = match_desc->getFontCollectionFiles();  	LLFontDescriptor default_desc("default",s_template_string,0);  	const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);  	if (match_default_desc)  	{ -		file_names.insert(file_names.end(), -						  match_default_desc->getFileNames().begin(), -						  match_default_desc->getFileNames().end()); -		ft_collection_list.insert(ft_collection_list.end(), -			match_default_desc->getFontCollectionsList().begin(), -			match_default_desc->getFontCollectionsList().end()); +		font_files.insert(font_files.end(), +						  match_default_desc->getFontFiles().begin(), +						  match_default_desc->getFontFiles().end()); +		font_collection_files.insert(font_collection_files.end(), +			match_default_desc->getFontCollectionFiles().begin(), +			match_default_desc->getFontCollectionFiles().end());  	}  	// Add ultimate fallback list - generated dynamically on linux,  	// null elsewhere. -	file_names.insert(file_names.end(), -					  getUltimateFallbackList().begin(), -					  getUltimateFallbackList().end()); +	std::transform(getUltimateFallbackList().begin(), getUltimateFallbackList().end(), std::back_inserter(font_files), +	               [](const std::string& file_name) { return LLFontFileInfo(file_name); });  	// Load fonts based on names. -	if (file_names.empty()) +	if (font_files.empty())  	{  		LL_WARNS() << "createFont failed, no file names specified" << LL_ENDL;  		return NULL;  	} -	LLFontFreetype::font_vector_t fontlist;  	LLFontGL *result = NULL; -	// Snarf all fonts we can into fontlist.  First will get pulled -	// off the list and become the "head" font, set to non-fallback. +	// The first font will get pulled will be the "head" font, set to non-fallback.  	// Rest will consitute the fallback list.  	bool is_first_found = true; -	std::string local_path = LLFontGL::getFontPathLocal(); -	std::string sys_path = LLFontGL::getFontPathSystem(); -	 +	string_vec_t font_search_paths; +	font_search_paths.push_back(LLFontGL::getFontPathLocal()); +	font_search_paths.push_back(LLFontGL::getFontPathSystem()); +#if LL_DARWIN +	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY); +	font_search_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL); +	font_search_paths.push_back(LLFontGL::getFontPathSystem() + MACOSX_FONT_SUPPLEMENTAL); +#endif +  	// The fontname string may contain multiple font file names separated by semicolons.  	// Break it apart and try loading each one, in order. -	for(string_vec_t::iterator file_name_it = file_names.begin(); -		file_name_it != file_names.end();  -		++file_name_it) +	for(font_file_info_vec_t::iterator font_file_it = font_files.begin(); +		font_file_it != font_files.end(); +		++font_file_it)  	{  		LLFontGL *fontp = NULL; -		string_vec_t font_paths; -		font_paths.push_back(local_path + *file_name_it); -		font_paths.push_back(sys_path + *file_name_it); -#if LL_DARWIN -		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + *file_name_it); -		font_paths.push_back(MACOSX_FONT_PATH_LIBRARY + MACOSX_FONT_SUPPLEMENTAL + *file_name_it); -		font_paths.push_back(sys_path +  MACOSX_FONT_SUPPLEMENTAL + *file_name_it); -#endif -		 -		bool is_ft_collection = (std::find(ft_collection_list.begin(), ft_collection_list.end(), *file_name_it) != ft_collection_list.end()); + +		bool is_ft_collection = (std::find_if(font_collection_files.begin(), font_collection_files.end(), +		                                      [&font_file_it](const LLFontFileInfo& ffi) { return font_file_it->FileName == ffi.FileName; }) != font_collection_files.end()); +  		// *HACK: Fallback fonts don't render, so we can use that to suppress  		// creation of OpenGL textures for test apps. JC  		bool is_fallback = !is_first_found || !mCreateGLTextures;  		F32 extra_scale = (is_fallback)?fallback_scale:1.0;  		F32 point_size_scale = extra_scale * point_size;  		bool is_font_loaded = false; -		for(string_vec_t::iterator font_paths_it = font_paths.begin(); -			font_paths_it != font_paths.end(); -			++font_paths_it) +		for(string_vec_t::iterator font_search_path_it = font_search_paths.begin(); +			font_search_path_it != font_search_paths.end(); +			++font_search_path_it)  		{ +			const std::string font_path = *font_search_path_it + font_file_it->FileName; +  			fontp = new LLFontGL; -			S32 num_faces = is_ft_collection ? fontp->getNumFaces(*font_paths_it) : 1; +			S32 num_faces = is_ft_collection ? fontp->getNumFaces(font_path) : 1;  			for (S32 i = 0; i < num_faces; i++)  			{  				if (fontp == NULL)  				{  					fontp = new LLFontGL;  				} -				if (fontp->loadFace(*font_paths_it, point_size_scale, -								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback, i)) +				if (fontp->loadFace(font_path, point_size_scale, +								 LLFontGL::sVertDPI, LLFontGL::sHorizDPI, is_fallback, i))  				{  					is_font_loaded = true;  					if (is_first_found) @@ -508,7 +528,8 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  					}  					else  					{ -						fontlist.push_back(fontp->mFontFreetype); +						result->mFontFreetype->addFallbackFont(fontp->mFontFreetype, font_file_it->CharFunctor); +  						delete fontp;  						fontp = NULL;  					} @@ -523,17 +544,12 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)  		}  		if(!is_font_loaded)  		{ -			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it <<  LL_ENDL; +			LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << font_file_it->FileName <<  LL_ENDL;  			delete fontp;  			fontp = NULL;  		}  	} -	if (result && !fontlist.empty()) -	{ -		result->mFontFreetype->setFallbackFonts(fontlist); -	} -  	if (result)  	{  		result->mFontDescriptor = desc; @@ -720,11 +736,22 @@ void LLFontRegistry::dump()  				<< " size=[" << desc.getSize() << "]"  				<< " fileNames="  				<< LL_ENDL; -		for (string_vec_t::const_iterator file_it=desc.getFileNames().begin(); -			 file_it != desc.getFileNames().end(); +		for (font_file_info_vec_t::const_iterator file_it=desc.getFontFiles().begin(); +			 file_it != desc.getFontFiles().end();  			 ++file_it)  		{ -			LL_INFOS() << "  file: " << *file_it <<LL_ENDL; +			LL_INFOS() << "  file: " << file_it->FileName << LL_ENDL; +		} +	} +} + +void LLFontRegistry::dumpTextures() +{ +	for (const auto& fontEntry : mFontMap) +	{ +		if (fontEntry.second) +		{ +			fontEntry.second->dumpTextures();  		}  	}  } diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h index e30c81c630..b0ef72c5de 100644 --- a/indra/llrender/llfontregistry.h +++ b/indra/llrender/llfontregistry.h @@ -34,13 +34,32 @@ class LLFontGL;  typedef std::vector<std::string> string_vec_t; +struct LLFontFileInfo +{ +	LLFontFileInfo(const std::string& file_name, const std::function<bool(llwchar)>& char_functor = nullptr) +		: FileName(file_name) +		, CharFunctor(char_functor) +	{ +	} + +	LLFontFileInfo(const LLFontFileInfo& ffi) +		: FileName(ffi.FileName) +		, CharFunctor(ffi.CharFunctor) +	{ +	} + +	std::string FileName; +	std::function<bool(llwchar)> CharFunctor; +}; +typedef std::vector<LLFontFileInfo> font_file_info_vec_t; +  class LLFontDescriptor  {  public:  	LLFontDescriptor();  	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style); -	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names); -	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names, const string_vec_t& font_collections); +	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list); +	LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const font_file_info_vec_t& font_list, const font_file_info_vec_t& font_collection_list);  	LLFontDescriptor normalize() const;  	bool operator<(const LLFontDescriptor& b) const; @@ -51,19 +70,26 @@ public:  	void setName(const std::string& name) { mName = name; }  	const std::string& getSize() const { return mSize; }  	void setSize(const std::string& size) { mSize = size; } -	const std::vector<std::string>& getFileNames() const { return mFileNames; } -	std::vector<std::string>& getFileNames() { return mFileNames; } -	const std::vector<std::string>& getFontCollectionsList() const { return mFontCollectionsList; } -	std::vector<std::string>& getFontCollectionsList() { return mFontCollectionsList; } + +	void addFontFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null); +	const font_file_info_vec_t & getFontFiles() const { return mFontFiles; } +	void setFontFiles(const font_file_info_vec_t& font_files) { mFontFiles = font_files; } +	void addFontCollectionFile(const std::string& file_name, const std::string& char_functor = LLStringUtil::null); +	const font_file_info_vec_t& getFontCollectionFiles() const { return mFontCollectionFiles; } +	void setFontCollectionFiles(const font_file_info_vec_t& font_collection_files) { mFontCollectionFiles = font_collection_files; } +  	const U8 getStyle() const { return mStyle; }  	void setStyle(U8 style) { mStyle = style; }  private:  	std::string mName;  	std::string mSize; -	string_vec_t mFileNames; -	string_vec_t mFontCollectionsList; +	font_file_info_vec_t mFontFiles; +	font_file_info_vec_t mFontCollectionFiles;  	U8 mStyle; + +	typedef std::map<std::string, std::function<bool(llwchar)>> char_functor_map_t; +	static char_functor_map_t mCharFunctors;  };  class LLFontRegistry @@ -94,6 +120,7 @@ public:  	bool nameToSize(const std::string& size_name, F32& size);  	void dump(); +	void dumpTextures();  	const string_vec_t& getUltimateFallbackList() const; diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 3d1b978c09..408f9eee42 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2890,7 +2890,7 @@ void LLGLSyncFence::wait()  	if (mSync)  	{  		while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED) -		{ +		{ //track the number of times we've waited here  		}  	}  } diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 9108c6143c..a0314cb5f2 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -29,6 +29,8 @@ set(llui_SOURCE_FILES      lldockcontrol.cpp      lldraghandle.cpp      lleditmenuhandler.cpp +    llemojidictionary.cpp +    llemojihelper.cpp      llf32uictrl.cpp      llfiltereditor.cpp      llflashtimer.cpp @@ -139,6 +141,8 @@ set(llui_HEADER_FILES      lldockablefloater.h      lldockcontrol.h      lleditmenuhandler.h +    llemojidictionary.h +    llemojihelper.h      llf32uictrl.h      llfiltereditor.h       llflashtimer.h diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index b0737c8238..f34398cb6b 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -68,6 +68,7 @@ LLButton::Params::Params()  	label_shadow("label_shadow", true),  	auto_resize("auto_resize", false),  	use_ellipses("use_ellipses", false), +	use_font_color("use_font_color", true),  	image_unselected("image_unselected"),  	image_selected("image_selected"),  	image_hover_selected("image_hover_selected"), @@ -160,6 +161,7 @@ LLButton::LLButton(const LLButton::Params& p)  	mDropShadowedText(p.label_shadow),  	mAutoResize(p.auto_resize),  	mUseEllipses( p.use_ellipses ), +	mUseFontColor( p.use_font_color),  	mHAlign(p.font_halign),  	mLeftHPad(p.pad_left),  	mRightHPad(p.pad_right), @@ -961,7 +963,7 @@ void LLButton::draw()  			LLFontGL::NORMAL,  			mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,  			S32_MAX, text_width, -			NULL, mUseEllipses); +			NULL, mUseEllipses, mUseFontColor);  	}  	LLUICtrl::draw(); @@ -1021,6 +1023,16 @@ bool LLButton::toggleState()  	return flipped;   } +void LLButton::setLabel( const std::string& label ) +{ +	mUnselectedLabel = mSelectedLabel = label; +} + +void LLButton::setLabel( const LLUIString& label ) +{ +	mUnselectedLabel = mSelectedLabel = label; +} +  void LLButton::setLabel( const LLStringExplicit& label )  {  	setLabelUnselected(label); @@ -1052,14 +1064,7 @@ bool LLButton::labelIsTruncated() const  const LLUIString& LLButton::getCurrentLabel() const  { -	if( getToggleState() ) -	{ -		return mSelectedLabel; -	} -	else -	{ -		return mUnselectedLabel; -	} +	return getToggleState() ? mSelectedLabel : mUnselectedLabel;  }  void LLButton::setImageUnselected(LLPointer<LLUIImage> image) diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index d3cdad874f..2e6ac29bc0 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -73,6 +73,7 @@ public:  		Optional<bool>			label_shadow;  		Optional<bool>			auto_resize;  		Optional<bool>			use_ellipses; +		Optional<bool>			use_font_color;  		// images  		Optional<LLUIImage*>	image_unselected, @@ -174,6 +175,7 @@ public:  	void			setUnselectedLabelColor( const LLColor4& c )		{ mUnselectedLabelColor = c; }  	void			setSelectedLabelColor( const LLColor4& c )			{ mSelectedLabelColor = c; }  	void			setUseEllipses( bool use_ellipses )					{ mUseEllipses = use_ellipses; } +	void			setUseFontColor( bool use_font_color)				{ mUseFontColor = use_font_color; }  	boost::signals2::connection setClickedCallback(const CommitCallbackParam& cb); @@ -238,6 +240,8 @@ public:  	void            autoResize();	// resize with label of current btn state   	void            resize(LLUIString label); // resize with label input +	void			setLabel(const std::string& label); +	void			setLabel(const LLUIString& label);  	void			setLabel( const LLStringExplicit& label);  	virtual bool	setLabelArg( const std::string& key, const LLStringExplicit& text );  	void			setLabelUnselected(const LLStringExplicit& label); @@ -353,6 +357,7 @@ protected:  	bool						mDropShadowedText;  	bool						mAutoResize;  	bool						mUseEllipses; +	bool						mUseFontColor;  	bool						mBorderEnabled;  	bool						mFlashing; diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp new file mode 100644 index 0000000000..f16c38a11a --- /dev/null +++ b/indra/llui/llemojidictionary.cpp @@ -0,0 +1,469 @@ +/** +* @file llemojidictionary.cpp +* @brief Implementation of LLEmojiDictionary +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, 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 "linden_common.h" + +#include "lldir.h" +#include "llemojidictionary.h" +#include "llsdserialize.h" + +#include <boost/algorithm/string.hpp> +#include <boost/range/adaptor/filtered.hpp> +#include <boost/range/algorithm/transform.hpp> + +// ============================================================================ +// Constants +// + +static const std::string SKINNED_EMOJI_FILENAME("emoji_characters.xml"); +static const std::string SKINNED_CATEGORY_FILENAME("emoji_categories.xml"); +static const std::string COMMON_GROUP_FILENAME("emoji_groups.xml"); +static const std::string GROUP_NAME_SKIP("skip"); +// https://www.compart.com/en/unicode/U+1F302 +static const S32 GROUP_OTHERS_IMAGE_INDEX = 0x1F302; + +// ============================================================================ +// Helper functions +// + +template<class T> +std::list<T> llsd_array_to_list(const LLSD& sd, std::function<void(T&)> mutator = {}); + +template<> +std::list<std::string> llsd_array_to_list(const LLSD& sd, std::function<void(std::string&)> mutator) +{ +    std::list<std::string> result; +    for (LLSD::array_const_iterator it = sd.beginArray(), end = sd.endArray(); it != end; ++it) +    { +        const LLSD& entry = *it; +        if (!entry.isString()) +            continue; + +        result.push_back(entry.asStringRef()); +        if (mutator) +        { +            mutator(result.back()); +        } +    } +    return result; +} + +struct emoji_filter_base +{ +    emoji_filter_base(const std::string& needle) +    { +        // Search without the colon (if present) so the user can type ':food' and see all emojis in the 'Food' category +        mNeedle = (boost::starts_with(needle, ":")) ? needle.substr(1) : needle; +        LLStringUtil::toLower(mNeedle); +    } + +protected: +    std::string mNeedle; +}; + +struct emoji_filter_shortcode_or_category_contains : public emoji_filter_base +{ +    emoji_filter_shortcode_or_category_contains(const std::string& needle) : emoji_filter_base(needle) {} + +    bool operator()(const LLEmojiDescriptor& descr) const +    { +        for (const auto& short_code : descr.ShortCodes) +        { +            if (boost::icontains(short_code, mNeedle)) +                return true; +        } + +        if (boost::icontains(descr.Category, mNeedle)) +            return true; + +        return false; +    } +}; + +std::string LLEmojiDescriptor::getShortCodes() const +{ +    std::string result; +    for (const std::string& shortCode : ShortCodes) +    { +        if (!result.empty()) +        { +            result += ", "; +        } +        result += shortCode; +    } +    return result; +} + +// ============================================================================ +// LLEmojiDictionary class +// + +LLEmojiDictionary::LLEmojiDictionary() +{ +} + +// static +void LLEmojiDictionary::initClass() +{ +    LLEmojiDictionary* pThis = &LLEmojiDictionary::initParamSingleton(); + +    pThis->loadTranslations(); +    pThis->loadGroups(); +    pThis->loadEmojis(); +} + +LLWString LLEmojiDictionary::findMatchingEmojis(const std::string& needle) const +{ +    LLWString result; +    boost::transform(mEmojis | boost::adaptors::filtered(emoji_filter_shortcode_or_category_contains(needle)), +                     std::back_inserter(result), [](const auto& descr) { return descr.Character; }); +    return result; +} + +// static +bool LLEmojiDictionary::searchInShortCode(std::size_t& begin, std::size_t& end, const std::string& shortCode, const std::string& needle) +{ +    begin = 0; +    end = 1; +    std::size_t index = 1; +    // Search for begin +    char d = tolower(needle[index++]); +    while (end < shortCode.size()) +    { +        char s = tolower(shortCode[end++]); +        if (s == d) +        { +            begin = end - 1; +            break; +        } +    } +    if (!begin) +        return false; +    // Search for end +    d = tolower(needle[index++]); +    if (!d) +        return true; +    while (end < shortCode.size() && index <= needle.size()) +    { +        char s = tolower(shortCode[end++]); +        if (s == d) +        { +            if (index == needle.size()) +                return true; +            d = tolower(needle[index++]); +            continue; +        } +        switch (s) +        { +        case L'-': +        case L'_': +        case L'+': +            continue; +        } +        break; +    } +    return false; +} + +void LLEmojiDictionary::findByShortCode( +    std::vector<LLEmojiSearchResult>& result, +    const std::string& needle +) const +{ +    result.clear(); + +    if (needle.empty() || needle.front() != ':') +        return; + +    std::map<llwchar, std::vector<LLEmojiSearchResult>> results; + +    for (const LLEmojiDescriptor& d : mEmojis) +    { +        if (!d.ShortCodes.empty()) +        { +            const std::string& shortCode = d.ShortCodes.front(); +            if (shortCode.size() >= needle.size() && shortCode.front() == needle.front()) +            { +                std::size_t begin, end; +                if (searchInShortCode(begin, end, shortCode, needle)) +                { +                    results[begin].emplace_back(d.Character, shortCode, begin, end); +                } +            } +        } +    } + +    for (const auto& it : results) +    { +#ifdef __cpp_lib_containers_ranges +        result.append_range(it.second); +#else +        result.insert(result.end(), it.second.cbegin(), it.second.cend()); +#endif +    } +} + +const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromEmoji(llwchar emoji) const +{ +    const auto it = mEmoji2Descr.find(emoji); +    return (mEmoji2Descr.end() != it) ? it->second : nullptr; +} + +const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromShortCode(const std::string& short_code) const +{ +    const auto it = mShortCode2Descr.find(short_code); +    return (mShortCode2Descr.end() != it) ? it->second : nullptr; +} + +std::string LLEmojiDictionary::getNameFromEmoji(llwchar ch) const +{ +    const auto it = mEmoji2Descr.find(ch); +    return (mEmoji2Descr.end() != it) ? it->second->ShortCodes.front() : LLStringUtil::null; +} + +bool LLEmojiDictionary::isEmoji(llwchar ch) const +{ +    // Currently used codes: A9,AE,203C,2049,2122,...,2B55,3030,303D,3297,3299,1F004,...,1FAF6 +    if (ch == 0xA9 || ch == 0xAE || (ch >= 0x2000 && ch < 0x3300) || (ch >= 0x1F000 && ch < 0x20000)) +    { +        return mEmoji2Descr.find(ch) != mEmoji2Descr.end(); +    } + +    return false; +} + +void LLEmojiDictionary::loadTranslations() +{ +    std::vector<std::string> filenames = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_CATEGORY_FILENAME, LLDir::CURRENT_SKIN); +    if (filenames.empty()) +    { +        LL_WARNS() << "Emoji file categories not found" << LL_ENDL; +        return; +    } + +    const std::string filename = filenames.back(); +    llifstream file(filename.c_str()); +    if (!file.is_open()) +    { +        LL_WARNS() << "Emoji file categories failed to open" << LL_ENDL; +        return; +    } + +    LL_DEBUGS() << "Loading emoji categories file at " << filename << LL_ENDL; + +    LLSD data; +    LLSDSerialize::fromXML(data, file); +    if (data.isUndefined()) +    { +        LL_WARNS() << "Emoji file categories missing or ill-formed" << LL_ENDL; +        return; +    } + +    // Register translations for all categories +    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it) +    { +        const LLSD& sd = *it; +        const std::string& name = sd["Name"].asStringRef(); +        const std::string& category = sd["Category"].asStringRef(); +        if (!name.empty() && !category.empty()) +        { +            mTranslations[name] = category; +        } +        else +        { +            LL_WARNS() << "Skipping invalid emoji category '" << name << "' => '" << category << "'" << LL_ENDL; +        } +    } +} + +void LLEmojiDictionary::loadGroups() +{ +    const std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, COMMON_GROUP_FILENAME); +    llifstream file(filename.c_str()); +    if (!file.is_open()) +    { +        LL_WARNS() << "Emoji file groups failed to open" << LL_ENDL; +        return; +    } + +    LL_DEBUGS() << "Loading emoji groups file at " << filename << LL_ENDL; + +    LLSD data; +    LLSDSerialize::fromXML(data, file); +    if (data.isUndefined()) +    { +        LL_WARNS() << "Emoji file groups missing or ill-formed" << LL_ENDL; +        return; +    } + +    mGroups.clear(); + +    // Register all groups +    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it) +    { +        const LLSD& sd = *it; +        const std::string& name = sd["Name"].asStringRef(); +        if (name == GROUP_NAME_SKIP) +        { +            mSkipCategories = loadCategories(sd); +            translateCategories(mSkipCategories); +        } +        else +        { +            // Add new group +            mGroups.emplace_back(); +            LLEmojiGroup& group = mGroups.back(); +            group.Character = loadIcon(sd); +            group.Categories = loadCategories(sd); +            translateCategories(group.Categories); + +            for (const std::string& category : group.Categories) +            { +                mCategory2Group.insert(std::make_pair(category, &group)); +            } +        } +    } + +    // Add group "others" +    mGroups.emplace_back(); +    mGroups.back().Character = GROUP_OTHERS_IMAGE_INDEX; +} + +void LLEmojiDictionary::loadEmojis() +{ +    std::vector<std::string> filenames = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_EMOJI_FILENAME, LLDir::CURRENT_SKIN); +    if (filenames.empty()) +    { +        LL_WARNS() << "Emoji file characters not found" << LL_ENDL; +        return; +    } + +    const std::string filename = filenames.back(); +    llifstream file(filename.c_str()); +    if (!file.is_open()) +    { +        LL_WARNS() << "Emoji file characters failed to open" << LL_ENDL; +        return; +    } + +    LL_DEBUGS() << "Loading emoji characters file at " << filename << LL_ENDL; + +    LLSD data; +    LLSDSerialize::fromXML(data, file); +    if (data.isUndefined()) +    { +        LL_WARNS() << "Emoji file characters missing or ill-formed" << LL_ENDL; +        return; +    } + +    for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it) +    { +        const LLSD& sd = *it; + +        llwchar icon = loadIcon(sd); +        if (!icon) +        { +            LL_WARNS() << "Skipping invalid emoji descriptor (no icon)" << LL_ENDL; +            continue; +        } + +        std::list<std::string> categories = loadCategories(sd); +        if (categories.empty()) +        { +            LL_WARNS() << "Skipping invalid emoji descriptor (no categories)" << LL_ENDL; +            continue; +        } + +        std::string category = categories.front(); + +        if (std::find(mSkipCategories.begin(), mSkipCategories.end(), category) != mSkipCategories.end()) +        { +            // This category is listed for skip +            continue; +        } + +        std::list<std::string> shortCodes = loadShortCodes(sd); +        if (shortCodes.empty()) +        { +            LL_WARNS() << "Skipping invalid emoji descriptor (no shortCodes)" << LL_ENDL; +            continue; +        } + +        if (mCategory2Group.find(category) == mCategory2Group.end()) +        { +            // Add unknown category to "others" group +            mGroups.back().Categories.push_back(category); +            mCategory2Group.insert(std::make_pair(category, &mGroups.back())); +        } + +        mEmojis.emplace_back(); +        LLEmojiDescriptor& emoji = mEmojis.back(); +        emoji.Character = icon; +        emoji.Category = category; +        emoji.ShortCodes = std::move(shortCodes); + +        mEmoji2Descr.insert(std::make_pair(icon, &emoji)); +        mCategory2Descrs[category].push_back(&emoji); +        for (const std::string& shortCode : emoji.ShortCodes) +        { +            mShortCode2Descr.insert(std::make_pair(shortCode, &emoji)); +        } +    } +} + +llwchar LLEmojiDictionary::loadIcon(const LLSD& sd) +{ +    // We don't currently support character composition +    const LLWString icon = utf8str_to_wstring(sd["Character"].asString()); +    return (1 == icon.size()) ? icon[0] : L'\0'; +} + +std::list<std::string> LLEmojiDictionary::loadCategories(const LLSD& sd) +{ +    static const std::string key("Categories"); +    return llsd_array_to_list<std::string>(sd[key]); +} + +std::list<std::string> LLEmojiDictionary::loadShortCodes(const LLSD& sd) +{ +    static const std::string key("ShortCodes"); +    auto toLower = [](std::string& str) { LLStringUtil::toLower(str); }; +    return llsd_array_to_list<std::string>(sd[key], toLower); +} + +void LLEmojiDictionary::translateCategories(std::list<std::string>& categories) +{ +    for (std::string& category : categories) +    { +        auto it = mTranslations.find(category); +        if (it != mTranslations.end()) +        { +            category = it->second; +        } +    } +} + +// ============================================================================ diff --git a/indra/llui/llemojidictionary.h b/indra/llui/llemojidictionary.h new file mode 100644 index 0000000000..4af376df64 --- /dev/null +++ b/indra/llui/llemojidictionary.h @@ -0,0 +1,126 @@ +/** +* @file llemojidictionary.h +* @brief Header file for LLEmojiDictionary +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, 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$ +*/ + +#pragma once + +#include "lldictionary.h" +#include "llinitdestroyclass.h" +#include "llsingleton.h" + +// ============================================================================ +// LLEmojiDescriptor class +// + +struct LLEmojiDescriptor +{ +    llwchar Character; +    std::string Category; +    std::list<std::string> ShortCodes; +    std::string getShortCodes() const; +}; + +// ============================================================================ +// LLEmojiGroup class +// + +struct LLEmojiGroup +{ +    llwchar Character; +    std::list<std::string> Categories; +}; + +// ============================================================================ +// LLEmojiSearchResult class +// + +struct LLEmojiSearchResult +{ +    llwchar Character; +    std::string String; +    std::size_t Begin, End; + +    LLEmojiSearchResult(llwchar character, const std::string& string, std::size_t begin, std::size_t end) +        : Character(character) +        , String(string) +        , Begin(begin) +        , End(end) +    { +    } +}; + +// ============================================================================ +// LLEmojiDictionary class +// + +class LLEmojiDictionary : public LLParamSingleton<LLEmojiDictionary>, public LLInitClass<LLEmojiDictionary> +{ +    LLSINGLETON(LLEmojiDictionary); +    ~LLEmojiDictionary() override {}; + +public: +    typedef std::map<std::string, std::string> cat2cat_map_t; +    typedef std::map<std::string, const LLEmojiGroup*> cat2group_map_t; +    typedef std::map<llwchar, const LLEmojiDescriptor*> emoji2descr_map_t; +    typedef std::map<std::string, const LLEmojiDescriptor*> code2descr_map_t; +    typedef std::map<std::string, std::vector<const LLEmojiDescriptor*>> cat2descrs_map_t; + +    static void initClass(); +    LLWString findMatchingEmojis(const std::string& needle) const; +    static bool searchInShortCode(std::size_t& begin, std::size_t& end, const std::string& shortCode, const std::string& needle); +    void findByShortCode(std::vector<LLEmojiSearchResult>& result, const std::string& needle) const; +    const LLEmojiDescriptor* getDescriptorFromEmoji(llwchar emoji) const; +    const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const; +    std::string getNameFromEmoji(llwchar ch) const; +    bool isEmoji(llwchar ch) const; + +    const std::vector<LLEmojiGroup>& getGroups() const { return mGroups; } +    const emoji2descr_map_t& getEmoji2Descr() const { return mEmoji2Descr; } +    const cat2descrs_map_t& getCategory2Descrs() const { return mCategory2Descrs; } +    const code2descr_map_t& getShortCode2Descr() const { return mShortCode2Descr; } + +private: +    void loadTranslations(); +    void loadGroups(); +    void loadEmojis(); + +    static llwchar loadIcon(const LLSD& sd); +    static std::list<std::string> loadCategories(const LLSD& sd); +    static std::list<std::string> loadShortCodes(const LLSD& sd); +    void translateCategories(std::list<std::string>& categories); + +private: +    std::vector<LLEmojiGroup> mGroups; +    std::list<LLEmojiDescriptor> mEmojis; +    std::list<std::string> mSkipCategories; + +    cat2cat_map_t mTranslations; +    cat2group_map_t mCategory2Group; +    emoji2descr_map_t mEmoji2Descr; +    cat2descrs_map_t mCategory2Descrs; +    code2descr_map_t mShortCode2Descr; +}; + +// ============================================================================ diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp new file mode 100644 index 0000000000..89e6ddf987 --- /dev/null +++ b/indra/llui/llemojihelper.cpp @@ -0,0 +1,169 @@ +/** +* @file llemojihelper.h +* @brief Header file for LLEmojiHelper +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, 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 "linden_common.h" + +#include "llemojidictionary.h" +#include "llemojihelper.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "lluictrl.h" + +// ============================================================================ +// Constants +// + +constexpr char DEFAULT_EMOJI_HELPER_FLOATER[] = "emoji_picker"; +constexpr S32 HELPER_FLOATER_OFFSET_X = 0; +constexpr S32 HELPER_FLOATER_OFFSET_Y = 0; + +// ============================================================================ +// LLEmojiHelper +// + +std::string LLEmojiHelper::getToolTip(llwchar ch) const +{ +	return LLEmojiDictionary::instance().getNameFromEmoji(ch); +} + +bool LLEmojiHelper::isActive(const LLUICtrl* ctrl_p) const +{ +	return mHostHandle.get() == ctrl_p; +} + +// static +bool LLEmojiHelper::isCursorInEmojiCode(const LLWString& wtext, S32 cursorPos, S32* pShortCodePos) +{ +	// If the cursor is currently on a colon start the check one character further back +	S32 shortCodePos = (cursorPos == 0 || L':' != wtext[cursorPos - 1]) ? cursorPos : cursorPos - 1; + +	auto isPartOfShortcode = [](llwchar ch) { +		switch (ch) +		{ +			case L'-': +			case L'_': +			case L'+': +				return true; +			default: +				return LLStringOps::isAlnum(ch); +		} +	}; +	while (shortCodePos > 1 && isPartOfShortcode(wtext[shortCodePos - 1])) +	{ +		shortCodePos--; +	} + +	bool isShortCode = (L':' == wtext[shortCodePos - 1]) && (cursorPos - shortCodePos >= 2); +	if (pShortCodePos) +		*pShortCodePos = (isShortCode) ? shortCodePos - 1 : -1; +	return isShortCode; +} + +void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> cb) +{ +	// Commit immediately if the user already typed a full shortcode +	if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code)) +	{ +		cb(emojiDescrp->Character); +		hideHelper(); +		return; +	} + +	if (mHelperHandle.isDead()) +	{ +		LLFloater* pHelperFloater = LLFloaterReg::getInstance(DEFAULT_EMOJI_HELPER_FLOATER); +		mHelperHandle = pHelperFloater->getHandle(); +		mHelperCommitConn = pHelperFloater->setCommitCallback(std::bind([&](const LLSD& sdValue) { onCommitEmoji(utf8str_to_wstring(sdValue.asStringRef())[0]); }, std::placeholders::_2)); +	} +	setHostCtrl(hostctrl_p); +	mEmojiCommitCb = cb; + +	S32 floater_x, floater_y; +	if (!hostctrl_p->localPointToOtherView(local_x, local_y, &floater_x, &floater_y, gFloaterView)) +	{ +		LL_ERRS() << "Cannot show emoji helper for non-floater controls." << LL_ENDL; +		return; +	} + +	LLFloater* pHelperFloater = mHelperHandle.get(); +	LLRect rect = pHelperFloater->getRect(); +	S32 left = floater_x - HELPER_FLOATER_OFFSET_X; +	S32 top = floater_y - HELPER_FLOATER_OFFSET_Y + rect.getHeight(); +	rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight()); +	pHelperFloater->setRect(rect); +	pHelperFloater->openFloater(LLSD().with("hint", short_code)); +} + +void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p, bool strict) +{ +	mIsHideDisabled &= !strict; +	if (mIsHideDisabled || (ctrl_p && !isActive(ctrl_p))) +	{ +		return; +	} + +	setHostCtrl(nullptr); +} + +bool LLEmojiHelper::handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask) +{ +	if (mHelperHandle.isDead() || !isActive(ctrl_p)) +	{ +		return false; +	} + +	return mHelperHandle.get()->handleKey(key, mask, true); +} + +void LLEmojiHelper::onCommitEmoji(llwchar emoji) +{ +	if (!mHostHandle.isDead() && mEmojiCommitCb) +	{ +		mEmojiCommitCb(emoji); +	} +} + +void LLEmojiHelper::setHostCtrl(LLUICtrl* hostctrl_p) +{ +	const LLUICtrl* pCurHostCtrl = mHostHandle.get(); +	if (pCurHostCtrl != hostctrl_p) +	{ +		mHostCtrlFocusLostConn.disconnect(); +		mHostHandle.markDead(); +		mEmojiCommitCb = {}; + +		if (!mHelperHandle.isDead()) +		{ +			mHelperHandle.get()->closeFloater(); +		} + +		if (hostctrl_p) +		{ +			mHostHandle = hostctrl_p->getHandle(); +			mHostCtrlFocusLostConn = hostctrl_p->setFocusLostCallback(std::bind([&]() { hideHelper(getHostCtrl()); })); +		} +	} +} diff --git a/indra/llui/llemojihelper.h b/indra/llui/llemojihelper.h new file mode 100644 index 0000000000..e826ff93e6 --- /dev/null +++ b/indra/llui/llemojihelper.h @@ -0,0 +1,66 @@ +/** +* @file llemojihelper.h +* @brief Header file for LLEmojiHelper +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, 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$ +*/ + +#pragma once + +#include "llhandle.h" +#include "llsingleton.h" + +#include <boost/signals2.hpp> + +class LLFloater; +class LLUICtrl; + +class LLEmojiHelper : public LLSingleton<LLEmojiHelper> +{ +	LLSINGLETON(LLEmojiHelper) {} +	~LLEmojiHelper() override {} + +public: +	// General +	std::string getToolTip(llwchar ch) const; +	bool        isActive(const LLUICtrl* ctrl_p) const; +	static bool isCursorInEmojiCode(const LLWString& wtext, S32 cursor_pos, S32* short_code_pos_p = nullptr); +	void        showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> commit_cb); +	void        hideHelper(const LLUICtrl* ctrl_p = nullptr, bool strict = false); +	void        setIsHideDisabled(bool disabled) { mIsHideDisabled = disabled; }; + +	// Eventing +	bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask); +	void onCommitEmoji(llwchar emoji); + +protected: +	LLUICtrl* getHostCtrl() const { return mHostHandle.get(); } +	void      setHostCtrl(LLUICtrl* hostctrl_p); + +private: +	LLHandle<LLUICtrl>  mHostHandle; +	LLHandle<LLFloater> mHelperHandle; +	boost::signals2::connection mHostCtrlFocusLostConn; +	boost::signals2::connection mHelperCommitConn; +	std::function<void(llwchar)> mEmojiCommitCb; +	bool mIsHideDisabled; +}; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 87ab9d0b19..3ba56c6da1 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -182,6 +182,7 @@ LLFloater::Params::Params()  	save_visibility("save_visibility", false),  	can_dock("can_dock", false),  	show_title("show_title", true), +	auto_close("auto_close", false),  	positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE),  	header_height("header_height", 0),  	legacy_header_height("legacy_header_height", 0), @@ -254,6 +255,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)  	mCanClose(p.can_close),  	mDragOnLeft(p.can_drag_on_left),  	mResizable(p.can_resize), +	mAutoClose(p.auto_close),  	mPositioning(p.positioning),  	mMinWidth(p.min_width),  	mMinHeight(p.min_height), @@ -504,6 +506,7 @@ void LLFloater::enableResizeCtrls(bool enable, bool width, bool height)  void LLFloater::destroy()  { +	gFloaterView->onDestroyFloater(this);  	// LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before  	// it was deleted via LLMortician::updateClass(). See EXT-8458.  	LLFloaterReg::removeInstance(mInstanceName, mKey); @@ -681,7 +684,7 @@ void LLFloater::openFloater(const LLSD& key)  	if (getHost() != NULL)  	{  		getHost()->setMinimized(false); -		getHost()->setVisibleAndFrontmost(mAutoFocus); +		getHost()->setVisibleAndFrontmost(mAutoFocus && !getIsChrome());  		getHost()->showFloater(this);  	}  	else @@ -693,7 +696,7 @@ void LLFloater::openFloater(const LLSD& key)  		}  		applyControlsAndPosition(floater_to_stack);  		setMinimized(false); -		setVisibleAndFrontmost(mAutoFocus); +		setVisibleAndFrontmost(mAutoFocus && !getIsChrome());  	}  	mOpenSignal(this, key); @@ -829,6 +832,24 @@ void LLFloater::reshape(S32 width, S32 height, bool called_from_parent)  	LLPanel::reshape(width, height, called_from_parent);  } +// virtual +void LLFloater::translate(S32 x, S32 y) +{ +    LLView::translate(x, y); + +    if (!mTranslateWithDependents || mDependents.empty()) +        return; + +    for (const LLHandle<LLFloater>& handle : mDependents) +    { +        LLFloater* floater = handle.get(); +        if (floater && floater->getSnapTarget() == getHandle()) +        { +            floater->LLView::translate(x, y); +        } +    } +} +  void LLFloater::releaseFocus()  {  	LLUI::getInstance()->removePopup(this); @@ -1117,9 +1138,9 @@ bool LLFloater::canSnapTo(const LLView* other_view)  	if (other_view != getParent())  	{ -		const LLFloater* other_floaterp = dynamic_cast<const LLFloater*>(other_view);		 -		if (other_floaterp  -			&& other_floaterp->getSnapTarget() == getHandle()  +		const LLFloater* other_floaterp = dynamic_cast<const LLFloater*>(other_view); +		if (other_floaterp +			&& other_floaterp->getSnapTarget() == getHandle()  			&& mDependents.find(other_floaterp->getHandle()) != mDependents.end())  		{  			// this is a dependent that is already snapped to us, so don't snap back to it @@ -1509,30 +1530,40 @@ bool LLFloater::isFrontmost()  				&& floater_view->getFrontmost() == this);  } -void LLFloater::addDependentFloater(LLFloater* floaterp, bool reposition) +void LLFloater::addDependentFloater(LLFloater* floaterp, bool reposition, bool resize)  {  	mDependents.insert(floaterp->getHandle());  	floaterp->mDependeeHandle = getHandle();  	if (reposition)  	{ -		floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp)); +		LLRect rect = gFloaterView->findNeighboringPosition(this, floaterp); +		if (resize) +		{ +			const LLRect& base = getRect(); +			if (rect.mTop == base.mTop) +				rect.mBottom = base.mBottom; +			else if (rect.mLeft == base.mLeft) +				rect.mRight = base.mRight; +			floaterp->reshape(rect.getWidth(), rect.getHeight(), false); +		} +		floaterp->setRect(rect);  		floaterp->setSnapTarget(getHandle());  	}  	gFloaterView->adjustToFitScreen(floaterp, false, true);  	if (floaterp->isFrontmost())  	{  		// make sure to bring self and sibling floaters to front -		gFloaterView->bringToFront(floaterp); +		gFloaterView->bringToFront(floaterp, floaterp->getAutoFocus() && !getIsChrome());  	}  } -void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, bool reposition) +void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, bool reposition, bool resize)  {  	LLFloater* dependent_floaterp = dependent.get();  	if(dependent_floaterp)  	{ -		addDependentFloater(dependent_floaterp, reposition); +		addDependentFloater(dependent_floaterp, reposition, resize);  	}  } @@ -1542,6 +1573,44 @@ void LLFloater::removeDependentFloater(LLFloater* floaterp)  	floaterp->mDependeeHandle = LLHandle<LLFloater>();  } +void LLFloater::fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels) +{ +    LLRect total_rect = getRect(); + +    for (const LLHandle<LLFloater>& handle : mDependents) +    { +        LLFloater* floater = handle.get(); +        if (floater && floater->getSnapTarget() == getHandle()) +        { +            total_rect.unionWith(floater->getRect()); +        } +    } + +	S32 delta_left = left.notEmpty() ? left.mRight - total_rect.mRight : 0; +	S32 delta_bottom = bottom.notEmpty() ? bottom.mTop - total_rect.mTop : 0; +	S32 delta_right = right.notEmpty() ? right.mLeft - total_rect.mLeft : 0; + +	// move floater with dependings fully onscreen +    mTranslateWithDependents = true; +    if (translateRectIntoRect(total_rect, constraint, min_overlap_pixels)) +    { +        clearSnapTarget(); +    } +    else if (delta_left > 0 && total_rect.mTop < left.mTop && total_rect.mBottom > left.mBottom) +    { +        translate(delta_left, 0); +    } +    else if (delta_bottom > 0 && total_rect.mLeft > bottom.mLeft && total_rect.mRight < bottom.mRight) +    { +        translate(0, delta_bottom); +    } +    else if (delta_right < 0 && total_rect.mTop < right.mTop    && total_rect.mBottom > right.mBottom) +    { +        translate(delta_right, 0); +    } +    mTranslateWithDependents = false; +} +  bool LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index)  {  	if( mButtonsEnabled[index] ) @@ -1630,6 +1699,7 @@ bool LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)  	return was_minimized || LLPanel::handleDoubleClick(x, y, mask);  } +// virtual  void LLFloater::bringToFront( S32 x, S32 y )  {  	if (getVisible() && pointInView(x, y)) @@ -1644,12 +1714,20 @@ void LLFloater::bringToFront( S32 x, S32 y )  			LLFloaterView* parent = dynamic_cast<LLFloaterView*>( getParent() );  			if (parent)  			{ -				parent->bringToFront( this ); +				parent->bringToFront(this, !getIsChrome());  			}  		}  	}  } +// virtual +void LLFloater::goneFromFront() +{ +    if (mAutoClose) +    { +        closeFloater(); +    } +}  // virtual  void LLFloater::setVisibleAndFrontmost(bool take_focus,const LLSD& key) @@ -2488,13 +2566,18 @@ void LLFloaterView::bringToFront(LLFloater* child, bool give_focus, bool restore  	if (mFrontChild == child)  	{ -		if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) +		if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child))  		{  			child->setFocus(true);  		}  		return;  	} +	if (mFrontChild) +	{ +		mFrontChild->goneFromFront(); +	} +  	mFrontChild = child;  	// *TODO: make this respect floater's mAutoFocus value, instead of @@ -2852,10 +2935,17 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, bool allow_partial_out  		// floater is hosted elsewhere, so ignore  		return;  	} + +	if (floater->getDependee() && +		floater->getDependee() == floater->getSnapTarget().get()) +	{ +		// floater depends on other and snaps to it, so ignore +		return; +	} +  	LLRect::tCoordType screen_width = getSnapRect().getWidth();  	LLRect::tCoordType screen_height = getSnapRect().getHeight(); -	  	// only automatically resize non-minimized, resizable floaters  	if( floater->isResizable() && !floater->isMinimized() )  	{ @@ -2897,29 +2987,10 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, bool allow_partial_out  		}  	} -	const LLRect& floater_rect = floater->getRect(); - -	S32 delta_left = mToolbarLeftRect.notEmpty() ? mToolbarLeftRect.mRight - floater_rect.mRight : 0; -	S32 delta_bottom = mToolbarBottomRect.notEmpty() ? mToolbarBottomRect.mTop - floater_rect.mTop : 0; -	S32 delta_right = mToolbarRightRect.notEmpty() ? mToolbarRightRect.mLeft - floater_rect.mLeft : 0; +    const LLRect& constraint = snap_in_toolbars ? getSnapRect() : gFloaterView->getRect(); +    S32 min_overlap_pixels = allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX; -	// move window fully onscreen -	if (floater->translateIntoRect( snap_in_toolbars ? getSnapRect() : gFloaterView->getRect(), allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX )) -	{ -		floater->clearSnapTarget(); -	} -	else if (delta_left > 0 && floater_rect.mTop < mToolbarLeftRect.mTop && floater_rect.mBottom > mToolbarLeftRect.mBottom) -	{ -		floater->translate(delta_left, 0); -	} -	else if (delta_bottom > 0 && floater_rect.mLeft > mToolbarBottomRect.mLeft && floater_rect.mRight < mToolbarBottomRect.mRight) -	{ -		floater->translate(0, delta_bottom); -	} -	else if (delta_right < 0 && floater_rect.mTop < mToolbarRightRect.mTop	&& floater_rect.mBottom > mToolbarRightRect.mBottom) -	{ -		floater->translate(delta_right, 0); -	} +	floater->fitWithDependentsOnScreen(mToolbarLeftRect, mToolbarBottomRect, mToolbarRightRect, constraint, min_overlap_pixels);  }  void LLFloaterView::draw() @@ -3006,6 +3077,9 @@ LLFloater *LLFloaterView::getBackmost() const  void LLFloaterView::syncFloaterTabOrder()  { +	if (mFrontChild && !mFrontChild->isDead() && mFrontChild->getIsChrome()) +		return; +  	// look for a visible modal dialog, starting from first  	LLModalDialog* modal_dialog = NULL;  	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) @@ -3041,7 +3115,34 @@ void LLFloaterView::syncFloaterTabOrder()  			LLFloater* floaterp = dynamic_cast<LLFloater*>(*child_it);  			if (gFocusMgr.childHasKeyboardFocus(floaterp))  			{ -				bringToFront(floaterp, false); +                if (mFrontChild != floaterp) +                { +                    // Grab a list of the top floaters that want to stay on top of the focused floater +					std::list<LLFloater*> listTop; +					if (mFrontChild && !mFrontChild->canFocusStealFrontmost()) +                    { +                        for (LLView* childp : *getChildList()) +                        { +							LLFloater* child_floaterp = static_cast<LLFloater*>(childp); +                            if (child_floaterp->canFocusStealFrontmost()) +                                break; +							listTop.push_back(child_floaterp); +                        } +                    } + +                    bringToFront(floaterp, false); + +                    // Restore top floaters +					if (!listTop.empty()) +					{ +						for (LLView* childp : listTop) +						{ +							sendChildToFront(childp); +						} +						mFrontChild = listTop.back(); +					} +                } +  				break;  			}  		} @@ -3134,6 +3235,14 @@ void LLFloaterView::setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LL  	}  } +void LLFloaterView::onDestroyFloater(LLFloater* floater) +{ +    if (mFrontChild == floater) +    { +        mFrontChild = nullptr; +    } +} +  void LLFloater::setInstanceName(const std::string& name)  {  	if (name != mInstanceName) @@ -3226,6 +3335,7 @@ void LLFloater::initFromParams(const LLFloater::Params& p)  	mDefaultRelativeY = p.rel_y;  	mPositioning = p.positioning; +	mAutoClose = p.auto_close;  	mSaveRect = p.save_rect;  	if (p.save_visibility) diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 50edffbb8d..814bebeb95 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -113,8 +113,6 @@ struct LLCoordFloater : LLCoord<LL_COORD_FLOATER>  	bool operator!=(const LLCoordFloater& other) const { return !(*this == other); }  	void setFloater(LLFloater& floater); - -	  };  class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater> @@ -165,7 +163,8 @@ public:  								save_visibility,  								save_dock_state,  								can_dock, -								show_title; +								show_title, +								auto_close;  		Optional<LLFloaterEnums::EOpenPositioning>	positioning; @@ -238,6 +237,7 @@ public:  	virtual void	closeHostedFloater();  	/*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true); +	/*virtual*/ void translate(S32 x, S32 y);  	// Release keyboard and mouse focus  	void			releaseFocus(); @@ -256,10 +256,11 @@ public:  	std::string		getShortTitle() const;  	virtual void	setMinimized(bool b);  	void			moveResizeHandlesToFront(); -	void			addDependentFloater(LLFloater* dependent, bool reposition = true); -	void			addDependentFloater(LLHandle<LLFloater> dependent_handle, bool reposition = true); +	void			addDependentFloater(LLFloater* dependent, bool reposition = true, bool resize = false); +	void			addDependentFloater(LLHandle<LLFloater> dependent_handle, bool reposition = true, bool resize = false);  	LLFloater*		getDependee() { return (LLFloater*)mDependeeHandle.get(); } -	void		removeDependentFloater(LLFloater* dependent); +	void			removeDependentFloater(LLFloater* dependent); +	void			fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels);  	bool			isMinimized() const				{ return mMinimized; }  	/// isShown() differs from getVisible() in that isShown() also considers  	/// isMinimized(). isShown() is true only if visible and not minimized. @@ -314,8 +315,11 @@ public:  	/*virtual*/ void setVisible(bool visible); // do not override  	/*virtual*/ void onVisibilityChange ( bool new_visibility ); // do not override +	bool            canFocusStealFrontmost() const { return mFocusStealsFrontmost; } +	void            setFocusStealsFrontmost(bool wants_frontmost) { mFocusStealsFrontmost = wants_frontmost; } +  	void			setFrontmost(bool take_focus = true, bool restore = true); -     virtual void	setVisibleAndFrontmost(bool take_focus=true, const LLSD& key = LLSD()); +     virtual void	setVisibleAndFrontmost(bool take_focus = true, const LLSD& key = LLSD());  	// Defaults to false.  	virtual bool	canSaveAs() const { return false; } @@ -387,6 +391,7 @@ protected:  	void		 	setInstanceName(const std::string& name);  	virtual void	bringToFront(S32 x, S32 y); +	virtual void	goneFromFront();  	void			setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized  	const LLRect&	getExpandedRect() const { return mExpandedRect; } @@ -482,8 +487,10 @@ private:  	bool			mCanTearOff;  	bool			mCanMinimize;  	bool			mCanClose; +    bool            mFocusStealsFrontmost = true;	// false if we don't want the currently focused floater to cover this floater without user interaction  	bool			mDragOnLeft;  	bool			mResizable; +	bool			mAutoClose;  	LLFloaterEnums::EOpenPositioning	mPositioning;  	LLCoordFloater	mPosition; @@ -503,6 +510,7 @@ private:  	typedef std::set<LLHandle<LLFloater> > handle_set_t;  	typedef std::set<LLHandle<LLFloater> >::iterator handle_set_iter_t;  	handle_set_t	mDependents; +	bool			mTranslateWithDependents { false };  	bool			mButtonsEnabled[BUTTON_COUNT];  	F32				mButtonScale; @@ -599,6 +607,7 @@ public:  	LLFloater* getFrontmostClosableFloater();   	void setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect); +	void onDestroyFloater(LLFloater* floater);  private:  	void hiddenFloaterClosed(LLFloater* floater); diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 5956ae8b36..76d926f922 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -890,7 +890,7 @@ void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y      //      font->renderUTF8(mLabel, 0, x, y, color,          LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -        S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, true); +        S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/true);  }  void LLFolderViewItem::draw() @@ -999,7 +999,7 @@ void LLFolderViewItem::draw()  	{          suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,  						  LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -						  S32_MAX, S32_MAX, &right_x, false ); +						  S32_MAX, S32_MAX, &right_x);  	}  	//--------------------------------------------------------------------------------// @@ -1011,9 +1011,9 @@ void LLFolderViewItem::draw()          {              F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length);              F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; -            font->renderUTF8( combined_string, filter_offset, match_string_left, yy, -            sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -            filter_string_length, S32_MAX, &right_x, false ); +            font->renderUTF8(combined_string, filter_offset, match_string_left, yy, +                sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +                filter_string_length, S32_MAX, &right_x);          }          else          { @@ -1022,8 +1022,9 @@ void LLFolderViewItem::draw()              {                  F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length);                  F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; -                font->renderUTF8( mLabel, filter_offset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x, false ); +                font->renderUTF8(mLabel, filter_offset, match_string_left, yy, +                    sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +                    label_filter_length, S32_MAX, &right_x);              }              S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; @@ -1032,7 +1033,9 @@ void LLFolderViewItem::draw()                  S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());                  F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length);                  F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; -                suffix_font->renderUTF8( mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x, false ); +                suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, +                    LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +                    suffix_filter_length, S32_MAX, &right_x);              }          } diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 6664117bcd..505216d0ff 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -89,6 +89,7 @@ LLLineEditor::Params::Params()  	background_image_disabled("background_image_disabled"),  	background_image_focused("background_image_focused"),  	bg_image_always_focused("bg_image_always_focused", false), +	show_label_focused("show_label_focused", false),  	select_on_focus("select_on_focus", false),  	revert_on_esc("revert_on_esc", true),  	spellcheck("spellcheck", false), @@ -152,6 +153,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)  	mBgImageDisabled( p.background_image_disabled ),  	mBgImageFocused( p.background_image_focused ),  	mShowImageFocused( p.bg_image_always_focused ), +	mShowLabelFocused( p.show_label_focused ),  	mUseBgColor(p.use_bg_color),  	mHaveHistory(false),  	mReplaceNewlinesWithSpaces( true ), @@ -1737,6 +1739,20 @@ void LLLineEditor::drawBackground()  	}  } +//virtual  +const std::string LLLineEditor::getToolTip() const +{ +    if (sDebugUnicode) +    { +        std::string text = getText(); +        std::string tooltip = utf8str_showBytesUTF8(text); +        return tooltip; +    } + +    return LLUICtrl::getToolTip(); +} + +//virtual   void LLLineEditor::draw()  {  	F32 alpha = getDrawContext().mAlpha; @@ -2069,7 +2085,7 @@ void LLLineEditor::draw()  		//draw label if no text is provided  		//but we should draw it in a different color  		//to give indication that it is not text you typed in -		if (0 == mText.length() && mReadOnly) +		if (0 == mText.length() && (mReadOnly || mShowLabelFocused))  		{  			mGLFont->render(mLabel.getWString(), 0,  							mTextLeftEdge, (F32)text_bottom, @@ -2105,7 +2121,7 @@ void LLLineEditor::draw()  							LLFontGL::NO_SHADOW,  							S32_MAX,  							mTextRightEdge - ll_round(rendered_pixels_right), -							&rendered_pixels_right, false); +							&rendered_pixels_right);  		}  		// Draw children (border)  		LLView::draw(); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 1ca300ec91..afc98af3ab 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -91,6 +91,7 @@ public:  										commit_on_focus_lost,  										ignore_tab,  										bg_image_always_focused, +										show_label_focused,  										is_password,  										use_bg_color; @@ -118,54 +119,55 @@ protected:  	friend class LLUICtrlFactory;  	friend class LLFloaterEditUI;  	void showContextMenu(S32 x, S32 y); +  public:  	virtual ~LLLineEditor();  	// mousehandler overrides -	/*virtual*/ bool	handleMouseDown(S32 x, S32 y, MASK mask); -	/*virtual*/ bool	handleMouseUp(S32 x, S32 y, MASK mask); -	/*virtual*/ bool	handleHover(S32 x, S32 y, MASK mask); -	/*virtual*/ bool	handleDoubleClick(S32 x,S32 y,MASK mask); -	/*virtual*/ bool	handleMiddleMouseDown(S32 x,S32 y,MASK mask); -	/*virtual*/ bool	handleRightMouseDown(S32 x, S32 y, MASK mask); -	/*virtual*/ bool	handleKeyHere(KEY key, MASK mask ); -	/*virtual*/ bool	handleUnicodeCharHere(llwchar uni_char); -	/*virtual*/ void	onMouseCaptureLost(); +	/*virtual*/ bool	handleMouseDown(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool	handleMouseUp(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool	handleHover(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool	handleDoubleClick(S32 x,S32 y,MASK mask) override; +	/*virtual*/ bool	handleMiddleMouseDown(S32 x,S32 y,MASK mask) override; +	/*virtual*/ bool	handleRightMouseDown(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool	handleKeyHere(KEY key, MASK mask) override; +	/*virtual*/ bool	handleUnicodeCharHere(llwchar uni_char) override; +	/*virtual*/ void	onMouseCaptureLost() override;  	// LLEditMenuHandler overrides -	virtual void	cut(); -	virtual bool	canCut() const; -	virtual void	copy(); -	virtual bool	canCopy() const; -	virtual void	paste(); -	virtual bool	canPaste() const; +	/*virtual*/ void	cut() override; +	/*virtual*/ bool	canCut() const override; +	/*virtual*/ void	copy() override; +	/*virtual*/ bool	canCopy() const override; +	/*virtual*/ void	paste() override; +	/*virtual*/ bool	canPaste() const override;  	virtual void	updatePrimary();  	virtual void	copyPrimary();   	virtual void	pastePrimary();  	virtual bool	canPastePrimary() const; -	virtual void	doDelete(); -	virtual bool	canDoDelete() const; +	/*virtual*/ void	doDelete() override; +	/*virtual*/ bool	canDoDelete() const override; -	virtual void	selectAll(); -	virtual bool	canSelectAll() const; +	/*virtual*/ void	selectAll() override; +	/*virtual*/ bool	canSelectAll() const override; -	virtual void	deselect(); -	virtual bool	canDeselect() const; +	/*virtual*/ void	deselect() override; +	/*virtual*/ bool	canDeselect() const override;  	// LLSpellCheckMenuHandler overrides -	/*virtual*/ bool	getSpellCheck() const; +	/*virtual*/ bool	getSpellCheck() const override; -	/*virtual*/ const std::string& getSuggestion(U32 index) const; -	/*virtual*/ U32		getSuggestionCount() const; -	/*virtual*/ void	replaceWithSuggestion(U32 index); +	/*virtual*/ const std::string& getSuggestion(U32 index) const override; +	/*virtual*/ U32		getSuggestionCount() const override; +	/*virtual*/ void	replaceWithSuggestion(U32 index) override; -	/*virtual*/ void	addToDictionary(); -	/*virtual*/ bool	canAddToDictionary() const; +	/*virtual*/ void	addToDictionary() override; +	/*virtual*/ bool	canAddToDictionary() const override; -	/*virtual*/ void	addToIgnore(); -	/*virtual*/ bool	canAddToIgnore() const; +	/*virtual*/ void	addToIgnore() override; +	/*virtual*/ bool	canAddToIgnore() const override;  	// Spell checking helper functions  	std::string			getMisspelledWord(U32 pos) const; @@ -173,27 +175,28 @@ public:  	void				onSpellCheckSettingsChange();  	// view overrides -	virtual void	draw(); -	virtual void	reshape(S32 width,S32 height,bool called_from_parent=true); -	virtual void	onFocusReceived(); -	virtual void	onFocusLost(); -	virtual void	setEnabled(bool enabled); +	/*virtual*/ const std::string getToolTip() const override; +	/*virtual*/ void	draw() override; +	/*virtual*/ void	reshape(S32 width, S32 height, bool called_from_parent = true) override; +	/*virtual*/ void	onFocusReceived() override; +	/*virtual*/ void	onFocusLost() override; +	/*virtual*/ void	setEnabled(bool enabled) override;  	// UI control overrides -	virtual void	clear(); -	virtual void	onTabInto(); -	virtual void	setFocus( bool b ); -	virtual void 	setRect(const LLRect& rect); -	virtual bool	acceptsTextInput() const; -	virtual void	onCommit(); -	virtual bool	isDirty() const;	// Returns true if user changed value at all -	virtual void	resetDirty();		// Clear dirty state +	/*virtual*/ void	clear() override; +	/*virtual*/ void	onTabInto() override; +	/*virtual*/ void	setFocus(bool b) override; +	/*virtual*/ void 	setRect(const LLRect& rect) override; +	/*virtual*/ bool	acceptsTextInput() const override; +	/*virtual*/ void	onCommit() override; +	/*virtual*/ bool	isDirty() const override;	// Returns true if user changed value at all +	/*virtual*/ void	resetDirty() override;		// Clear dirty state  	// assumes UTF8 text -	virtual void	setValue(const LLSD& value ); -	virtual LLSD	getValue() const; -	virtual bool	setTextArg( const std::string& key, const LLStringExplicit& text ); -	virtual bool	setLabelArg( const std::string& key, const LLStringExplicit& text ); +	/*virtual*/ void	setValue(const LLSD& value) override; +	/*virtual*/ LLSD	getValue() const override; +	/*virtual*/ bool	setTextArg(const std::string& key, const LLStringExplicit& text) override; +	/*virtual*/ bool	setLabelArg(const std::string& key, const LLStringExplicit& text) override;  	void			setLabel(const LLStringExplicit &new_label) { mLabel = new_label; }  	const std::string& 	getLabel()	{ return mLabel.getString(); } @@ -215,7 +218,7 @@ public:  	// Selects characters 'start' to 'end'.  	void			setSelection(S32 start, S32 end); -	virtual void	getSelectionRange(S32 *position, S32 *length) const; +	/*virtual*/ void	getSelectionRange(S32 *position, S32 *length) const override;  	void			setCommitOnFocusLost( bool b )	{ mCommitOnFocusLost = b; }  	void			setRevertOnEsc( bool b )		{ mRevertOnEsc = b; } @@ -314,14 +317,14 @@ public:  	void			updateAllowingLanguageInput();  	bool			hasPreeditString() const;  	// Implementation (overrides) of LLPreeditor -	virtual void	resetPreedit(); -	virtual void	updatePreedit(const LLWString &preedit_string, -						const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position); -	virtual void	markAsPreedit(S32 position, S32 length); -	virtual void	getPreeditRange(S32 *position, S32 *length) const; -	virtual bool	getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; -	virtual S32		getPreeditFontSize() const; -	virtual LLWString getPreeditString() const { return getWText(); } +	/*virtual*/ void	resetPreedit() override; +	/*virtual*/ void	updatePreedit(const LLWString &preedit_string, +						const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) override; +	/*virtual*/ void	markAsPreedit(S32 position, S32 length) override; +	/*virtual*/ void	getPreeditRange(S32 *position, S32 *length) const override; +	/*virtual*/ bool	getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const override; +	/*virtual*/ S32		getPreeditFontSize() const override; +	/*virtual*/ LLWString getPreeditString() const override { return getWText(); }      void			setText(const LLStringExplicit &new_text, bool use_size_limit); @@ -398,6 +401,7 @@ protected:  	bool		mReadOnly;  	bool 		mShowImageFocused; +	bool 		mShowLabelFocused;  	bool		mUseBgColor; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 76da0755af..f53979e544 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -573,6 +573,11 @@ void LLMenuItemGL::onVisibilityChange(bool new_visibility)  //  // This class represents a separator.  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LLMenuItemSeparatorGL::Params::Params() +    : on_visible("on_visible") +{ +} +  LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :  	LLMenuItemGL( p )  { diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index a52941ab73..48d6d7114c 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -235,10 +235,10 @@ public:  	struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>  	{          Optional<EnableCallbackParam > on_visible; -        Params() : on_visible("on_visible") -        {} +        Params();  	}; -	LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params()); + +    LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params());  	/*virtual*/ void draw( void );  	/*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 46e1616805..5fb18d8299 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -913,7 +913,7 @@ public:  	/* virtual */ LLNotificationPtr add(const std::string& name,   						const LLSD& substitutions,   						const LLSD& payload,  -						LLNotificationFunctorRegistry::ResponseFunctor functor); +						LLNotificationFunctorRegistry::ResponseFunctor functor) override;  	LLNotificationPtr add(const LLNotification::Params& p);  	void add(const LLNotificationPtr pNotif); @@ -964,8 +964,8 @@ public:  	bool isVisibleByRules(LLNotificationPtr pNotification);  private: -	/*virtual*/ void initSingleton(); -	/*virtual*/ void cleanupSingleton(); +	/*virtual*/ void initSingleton() override; +	/*virtual*/ void cleanupSingleton() override;  	void loadPersistentNotifications(); diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index f520ba2fb8..615df6977b 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -475,13 +475,15 @@ void LLScrollbar::reshape(S32 width, S32 height, bool called_from_parent)  	{  		up_button->reshape(up_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness));  		down_button->reshape(down_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness)); -		up_button->setOrigin(up_button->getRect().mLeft, getRect().getHeight() - up_button->getRect().getHeight()); +		up_button->setOrigin(0, getRect().getHeight() - up_button->getRect().getHeight()); +		down_button->setOrigin(0, 0);  	}  	else  	{  		up_button->reshape(llmin(getRect().getWidth() / 2, mThickness), up_button->getRect().getHeight());  		down_button->reshape(llmin(getRect().getWidth() / 2, mThickness), down_button->getRect().getHeight()); -		down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), down_button->getRect().mBottom); +		up_button->setOrigin(0, 0); +		down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), 0);  	}  	updateThumbRect();  } diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 09e8fd1f84..5aba44c2b5 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -70,6 +70,7 @@ LLScrollContainer::Params::Params()  	bg_color("color"),  	border_visible("border_visible"),  	hide_scrollbar("hide_scrollbar"), +	ignore_arrow_keys("ignore_arrow_keys"),  	min_auto_scroll_rate("min_auto_scroll_rate", 100),  	max_auto_scroll_rate("max_auto_scroll_rate", 1000),  	max_auto_scroll_zone("max_auto_scroll_zone", 16), @@ -86,6 +87,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)  	mBackgroundColor(p.bg_color()),  	mIsOpaque(p.is_opaque),  	mHideScrollbar(p.hide_scrollbar), +	mIgnoreArrowKeys(p.ignore_arrow_keys),  	mReserveScrollCorner(p.reserve_scroll_corner),  	mMinAutoScrollRate(p.min_auto_scroll_rate),  	mMaxAutoScrollRate(p.max_auto_scroll_rate), @@ -204,10 +206,29 @@ void LLScrollContainer::reshape(S32 width, S32 height,  	}  } +// virtual  bool LLScrollContainer::handleKeyHere(KEY key, MASK mask)  { +    if (mIgnoreArrowKeys) +    { +        switch(key) +        { +        case KEY_LEFT: +        case KEY_RIGHT: +        case KEY_UP: +        case KEY_DOWN: +        case KEY_PAGE_UP: +        case KEY_PAGE_DOWN: +        case KEY_HOME: +        case KEY_END: +            return false; +        default: +            break; +        } +    } +  	// allow scrolled view to handle keystrokes in case it delegated keyboard focus -	// to the scroll container.   +	// to the scroll container.  	// NOTE: this should not recurse indefinitely as handleKeyHere  	// should not propagate to parent controls, so mScrolledView should *not*  	// call LLScrollContainer::handleKeyHere in turn diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index 13f69cb83b..a9191714e8 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -63,7 +63,8 @@ public:  		Optional<bool>		is_opaque,  							reserve_scroll_corner,  							border_visible, -							hide_scrollbar; +							hide_scrollbar, +							ignore_arrow_keys;  		Optional<F32>		min_auto_scroll_rate,  							max_auto_scroll_rate;  		Optional<U32>		max_auto_scroll_zone; @@ -149,6 +150,7 @@ private:  	F32			mMaxAutoScrollRate;  	U32			mMaxAutoScrollZone;  	bool		mHideScrollbar; +	bool		mIgnoreArrowKeys;  }; diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index e16ba9627a..b282378fe1 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -37,53 +37,44 @@ static LLDefaultChildRegistry::Register<LLScrollingPanelList> r("scrolling_panel  // This could probably be integrated with LLScrollContainer -SJB +LLScrollingPanelList::Params::Params() +	: is_horizontal("is_horizontal") +	, padding("padding") +	, spacing("spacing") +{ +} + +LLScrollingPanelList::LLScrollingPanelList(const Params& p) +	: LLUICtrl(p) +	, mIsHorizontal(p.is_horizontal) +	, mPadding(p.padding.isProvided() ? p.padding : DEFAULT_PADDING) +	, mSpacing(p.spacing.isProvided() ? p.spacing : DEFAULT_SPACING) +{ +} +  void LLScrollingPanelList::clearPanels()  {  	deleteAllChildren();  	mPanelList.clear(); - -	LLRect rc = getRect(); -	rc.setLeftTopAndSize(rc.mLeft, rc.mTop, 1, 1); -	setRect(rc); - -	notifySizeChanged(rc.getHeight()); +	rearrange();  } -S32 LLScrollingPanelList::addPanel( LLScrollingPanel* panel ) +S32 LLScrollingPanelList::addPanel(LLScrollingPanel* panel, bool back)  { -	addChildInBack( panel ); -	mPanelList.push_front( panel ); - -	// Resize this view -	S32 total_height = 0; -	S32 max_width = 0; -	S32 cur_gap = 0; -	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); -		 iter != mPanelList.end(); ++iter) +	if (back)  	{ -		LLScrollingPanel *childp = *iter; -		total_height += childp->getRect().getHeight() + cur_gap; -		max_width = llmax( max_width, childp->getRect().getWidth() ); -		cur_gap = GAP_BETWEEN_PANELS; +		addChild(panel); +		mPanelList.push_back(panel);  	} - 	LLRect rc = getRect(); - 	rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height); - 	setRect(rc); - -	notifySizeChanged(rc.getHeight()); - -	// Reposition each of the child views -	S32 cur_y = total_height; -	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); -		 iter != mPanelList.end(); ++iter) +	else  	{ -		LLScrollingPanel *childp = *iter; -		cur_y -= childp->getRect().getHeight(); -		childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom); -		cur_y -= GAP_BETWEEN_PANELS; +		addChildInBack(panel); +		mPanelList.push_front(panel);  	} -	return total_height; +	rearrange(); + +	return mIsHorizontal ? getRect().getWidth() : getRect().getHeight();  }  void LLScrollingPanelList::removePanel(LLScrollingPanel* panel)  @@ -100,7 +91,7 @@ void LLScrollingPanelList::removePanel(LLScrollingPanel* panel)  				break;  			}  		} -		if(iter != mPanelList.end()) +		if (iter != mPanelList.end())  		{  			removePanel(index);  		} @@ -120,62 +111,104 @@ void LLScrollingPanelList::removePanel( U32 panel_index )  		mPanelList.erase( mPanelList.begin() + panel_index );  	} -	const S32 GAP_BETWEEN_PANELS = 6; +	rearrange(); +} -	// Resize this view -	S32 total_height = 0; -	S32 max_width = 0; -	S32 cur_gap = 0; -	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); +void LLScrollingPanelList::updatePanels(bool allow_modify) +{ +    for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();  		 iter != mPanelList.end(); ++iter) -	{ +    {  		LLScrollingPanel *childp = *iter; -		total_height += childp->getRect().getHeight() + cur_gap; -		max_width = llmax( max_width, childp->getRect().getWidth() ); -		cur_gap = GAP_BETWEEN_PANELS; +		childp->updatePanel(allow_modify); +    } +} + +void LLScrollingPanelList::rearrange() +{ +	// Resize this view +	S32 new_width, new_height; +	if (!mPanelList.empty()) +	{ +		new_width = new_height = mPadding * 2; +		for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); +			iter != mPanelList.end(); ++iter) +		{ +			LLScrollingPanel* childp = *iter; +			const LLRect& rect = childp->getRect(); +			if (mIsHorizontal) +			{ +				new_width += rect.getWidth() + mSpacing; +				new_height = llmax(new_height, rect.getHeight()); +			} +			else +			{ +				new_height += rect.getHeight() + mSpacing; +				new_width = llmax(new_width, rect.getWidth()); +			} +		} + +		if (mIsHorizontal) +		{ +			new_width -= mSpacing; +		} +		else +		{ +			new_height -= mSpacing; +		}  	} +	else +	{ +		new_width = new_height = 1; +	} +  	LLRect rc = getRect(); -	rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height); -	setRect(rc); +	if (mIsHorizontal || !followsRight()) +	{ +		rc.mRight = rc.mLeft + new_width; +	} +	if (!mIsHorizontal || !followsBottom()) +	{ +		rc.mBottom = rc.mTop - new_height; +	} -	notifySizeChanged(rc.getHeight()); +	if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom) +	{ +		setRect(rc); +		notifySizeChanged(); +	}  	// Reposition each of the child views -	S32 cur_y = total_height; +	S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding;  	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); -		 iter != mPanelList.end(); ++iter) +		iter != mPanelList.end(); ++iter)  	{ -		LLScrollingPanel *childp = *iter; -		cur_y -= childp->getRect().getHeight(); -		childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom); -		cur_y -= GAP_BETWEEN_PANELS; +		LLScrollingPanel* childp = *iter; +		const LLRect& rect = childp->getRect(); +		if (mIsHorizontal) +		{ +			childp->translate(pos - rect.mLeft, rc.getHeight() - mPadding - rect.mTop); +			pos += rect.getWidth() + mSpacing; +		} +		else +		{ +			childp->translate(mPadding - rect.mLeft, pos - rect.mTop); +			pos -= rect.getHeight() + mSpacing; +		}  	}  } -void LLScrollingPanelList::updatePanels(bool allow_modify) -{ -    for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); -		 iter != mPanelList.end(); ++iter) -    { -		LLScrollingPanel *childp = *iter; -		childp->updatePanel(allow_modify); -    } -} -  void LLScrollingPanelList::updatePanelVisiblilty()  {  	// Determine visibility of children. -	S32 BORDER_WIDTH = 2;  // HACK -	LLRect parent_local_rect = getParent()->getRect(); -	parent_local_rect.stretch( -BORDER_WIDTH ); -	  	LLRect parent_screen_rect; -	getParent()->localPointToScreen(  -		BORDER_WIDTH, 0,  +	getParent()->localPointToScreen( +		mPadding, mPadding,  		&parent_screen_rect.mLeft, &parent_screen_rect.mBottom ); -	getParent()->localPointToScreen(  -		parent_local_rect.getWidth() - BORDER_WIDTH, parent_local_rect.getHeight() - BORDER_WIDTH, +	getParent()->localPointToScreen( +		getParent()->getRect().getWidth() - mPadding, +		getParent()->getRect().getHeight() - mPadding,  		&parent_screen_rect.mRight, &parent_screen_rect.mTop );  	for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); @@ -207,11 +240,12 @@ void LLScrollingPanelList::draw()  	LLUICtrl::draw();  } -void LLScrollingPanelList::notifySizeChanged(S32 height) +void LLScrollingPanelList::notifySizeChanged()  {  	LLSD info;  	info["action"] = "size_changes"; -	info["height"] = height; +	info["height"] = getRect().getHeight(); +	info["width"] = getRect().getWidth();  	notifyParent(info);  } diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h index 964fa1ba40..c812d23bc7 100644 --- a/indra/llui/llscrollingpanellist.h +++ b/indra/llui/llscrollingpanellist.h @@ -45,18 +45,24 @@ public:  /* - * A set of panels that are displayed in a vertical sequence inside a scroll container. + * A set of panels that are displayed in a sequence inside a scroll container.   */  class LLScrollingPanelList : public LLUICtrl  {  public:  	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> -	{}; -	LLScrollingPanelList(const Params& p) -	:	LLUICtrl(p)  -	{} +	{ +		Optional<bool> is_horizontal; +		Optional<S32> padding; +		Optional<S32> spacing; + +		Params(); +	}; + +	LLScrollingPanelList(const Params& p); -	static const S32 GAP_BETWEEN_PANELS = 6; +	static const S32 DEFAULT_SPACING = 6; +	static const S32 DEFAULT_PADDING = 2;  	typedef std::deque<LLScrollingPanel*>	panel_list_t; @@ -65,11 +71,18 @@ public:  	virtual void		draw();  	void				clearPanels(); -	S32					addPanel( LLScrollingPanel* panel ); -	void				removePanel( LLScrollingPanel* panel ); -	void				removePanel( U32 panel_index ); +	S32					addPanel(LLScrollingPanel* panel, bool back = false); +	void				removePanel(LLScrollingPanel* panel); +	void				removePanel(U32 panel_index);  	void				updatePanels(bool allow_modify); -	const panel_list_t&	getPanelList() { return mPanelList; } +	void				rearrange(); + +	const panel_list_t&	getPanelList() const { return mPanelList; } +	bool				getIsHorizontal() const { return mIsHorizontal; } +	void				setPadding(S32 padding) { mPadding = padding; rearrange(); } +	void				setSpacing(S32 spacing) { mSpacing = spacing; rearrange(); } +	S32					getPadding() const { return mPadding; } +	S32					getSpacing() const { return mSpacing; }  private:  	void				updatePanelVisiblilty(); @@ -77,7 +90,11 @@ private:  	/**  	 * Notify parent about size change, makes sense when used inside accordion  	 */ -	void				notifySizeChanged(S32 height); +	void				notifySizeChanged(); + +	bool				mIsHorizontal; +	S32					mPadding; +	S32					mSpacing;  	panel_list_t		mPanelList;  }; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 251ac46e2f..dbfacafd00 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -411,7 +411,7 @@ void LLScrollListCtrl::clearRows()  LLScrollListItem* LLScrollListCtrl::getFirstSelected() const  {  	item_list::const_iterator iter; -	for(iter = mItemList.begin(); iter != mItemList.end(); iter++) +	for (iter = mItemList.begin(); iter != mItemList.end(); iter++)  	{  		LLScrollListItem* item  = *iter;  		if (item->getSelected()) @@ -1269,7 +1269,7 @@ bool LLScrollListCtrl::selectItemByLabel(const std::string& label, bool case_sen  	LLScrollListItem* item = getItemByLabel(label, case_sensitive, column);  	bool found = NULL != item; -	if(found) +	if (found)  	{  		selectItem(item, -1);  	} @@ -2747,7 +2747,7 @@ bool LLScrollListCtrl::setSort(S32 column_idx, bool ascending)  S32	LLScrollListCtrl::getLinesPerPage()  {  	//if mPageLines is NOT provided display all item -	if(mPageLines) +	if (mPageLines)  	{  		return mPageLines;  	} diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 1eb3e530e8..b8ae33e541 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -253,7 +253,7 @@ public:  	S32				getItemIndex( LLScrollListItem* item ) const;  	S32				getItemIndex( const LLUUID& item_id ) const; -	void setCommentText( const std::string& comment_text); +	void			setCommentText( const std::string& comment_text);  	LLScrollListItem* addSeparator(EAddPosition pos);  	// "Simple" interface: use this when you're creating a list that contains only unique strings, only @@ -263,7 +263,7 @@ public:  	bool			selectItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 );		// false if item not found  	bool			selectItemByPrefix(const std::string& target, bool case_sensitive = true, S32 column = -1);  	bool			selectItemByPrefix(const LLWString& target, bool case_sensitive = true, S32 column = -1); -	LLScrollListItem*  getItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 ); +	LLScrollListItem*	getItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 );  	const std::string	getSelectedItemLabel(S32 column = 0) const;  	LLSD			getSelectedValue(); @@ -322,7 +322,7 @@ public:  	virtual S32		getScrollPos() const;  	virtual void	setScrollPos( S32 pos ); -	S32 getSearchColumn(); +	S32				getSearchColumn();  	void			setSearchColumn(S32 column) { mSearchColumn = column; }  	S32				getColumnIndexFromOffset(S32 x);  	S32				getColumnOffsetFromIndex(S32 index); @@ -371,13 +371,13 @@ public:  	// Used "internally" by the scroll bar.  	void			onScrollChange( S32 new_pos, LLScrollbar* src ); -	static void onClickColumn(void *userdata); +	static void		onClickColumn(void *userdata); -	virtual void updateColumns(bool force_update = false); -	S32 calcMaxContentWidth(); -	bool updateColumnWidths(); +	virtual void	updateColumns(bool force_update = false); +	S32				calcMaxContentWidth(); +	bool			updateColumnWidths(); -	void setHeadingHeight(S32 heading_height); +	void			setHeadingHeight(S32 heading_height);  	/**  	 * Sets  max visible  lines without scroolbar, if this value equals to 0,  	 * then display all items. @@ -398,9 +398,9 @@ public:  	virtual void	deselect();  	virtual bool	canDeselect() const; -	void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; } -	void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width); -	S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; } +	void			setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; } +	void			updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width); +	S32				getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }  	std::string     getSortColumnName();  	bool			getSortAscending() { return mSortColumns.empty() ? true : mSortColumns.back().second; } @@ -409,7 +409,9 @@ public:  	void			setAlternateSort() { mAlternateSort = true; } -	S32		selectMultiple( uuid_vec_t ids ); +	void			selectPrevItem(bool extend_selection = false); +	void			selectNextItem(bool extend_selection = false); +	S32				selectMultiple(uuid_vec_t ids);  	// conceptually const, but mutates mItemList  	void			updateSort() const;  	// sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example) @@ -454,8 +456,6 @@ protected:  	void			updateLineHeight();  private: -	void			selectPrevItem(bool extend_selection); -	void			selectNextItem(bool extend_selection);  	void			drawItems();  	void            updateLineHeightInsert(LLScrollListItem* item); diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index 13051998bd..6da0c69457 100644 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -185,6 +185,10 @@ void LLSearchEditor::setFocus( bool b )  void LLSearchEditor::onClearButtonClick(const LLSD& data)  {  	setText(LLStringUtil::null); +	if (mTextChangedCallback) +	{ +		mTextChangedCallback(this, getValue()); +	}  	mSearchEditor->onCommit(); // force keystroke callback  } diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h index 3da5e30955..14f9b44fe4 100644 --- a/indra/llui/llspellcheck.h +++ b/indra/llui/llspellcheck.h @@ -47,7 +47,7 @@ public:  protected:  	void addToDictFile(const std::string& dict_path, const std::string& word);  	void initHunspell(const std::string& dict_language); -	void initSingleton(); +	void initSingleton() override;  public:  	typedef std::list<std::string> dict_list_t; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 65c57fc764..61b67e346e 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -29,6 +29,8 @@  #include "lltextbase.h" +#include "llemojidictionary.h" +#include "llemojihelper.h"  #include "lllocalcliprect.h"  #include "llmenugl.h"  #include "llscrollcontainer.h" @@ -161,10 +163,12 @@ LLTextBase::Params::Params()  	line_spacing("line_spacing"),  	max_text_length("max_length", 255),  	font_shadow("font_shadow"), +	text_valign("text_valign"),  	wrap("wrap"),  	trusted_content("trusted_content", true),  	always_show_icons("always_show_icons", false),  	use_ellipses("use_ellipses", false), +	use_color("use_color", true),  	parse_urls("parse_urls", false),  	force_urls_external("force_urls_external", false),  	parse_highlights("parse_highlights", false) @@ -208,6 +212,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)  	mVPad(p.v_pad),  	mHAlign(p.font_halign),  	mVAlign(p.font_valign), +	mTextVAlign(p.text_valign.isProvided() ? p.text_valign.getValue() : p.font_valign.getValue()),  	mLineSpacingMult(p.line_spacing.multiple),  	mLineSpacingPixels(p.line_spacing.pixels),  	mClip(p.clip), @@ -222,6 +227,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)  	mPlainText ( p.plain_text ),  	mWordWrap(p.wrap),  	mUseEllipses( p.use_ellipses ), +	mUseColor(p.use_color),  	mParseHTML(p.parse_urls),  	mForceUrlsExternal(p.force_urls_external),  	mParseHighlights(p.parse_highlights), @@ -582,7 +588,7 @@ void LLTextBase::drawCursor()  				fontp = segmentp->getStyle()->getFont();  				fontp->render(text, mCursorPos, cursor_rect,   					LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha), -					LLFontGL::LEFT, mVAlign, +					LLFontGL::LEFT, mTextVAlign,  					LLFontGL::NORMAL,  					LLFontGL::NO_SHADOW,  					1); @@ -896,6 +902,28 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s  		}  	} +	// Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us) +	{ +		LLStyleSP emoji_style; +		LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL; +		for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++) +		{ +			llwchar code = wstr[text_kitty]; +			bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code); +			if (isEmoji) +			{ +				if (!emoji_style) +				{ +					emoji_style = new LLStyle(getStyleParams()); +					emoji_style->setFont(LLFontGL::getFontEmoji()); +				} + +				S32 new_seg_start = pos + text_kitty; +				insertSegment(new LLEmojiTextSegment(emoji_style, new_seg_start, new_seg_start + 1, *this)); +			} +		} +	} +  	getViewModel()->getEditableDisplay().insert(pos, wstr);  	if ( truncate() ) @@ -1079,6 +1107,7 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)  	needsReflow(reflow_start_index);  } +//virtual   bool LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)  {  	// handle triple click @@ -1133,6 +1162,7 @@ bool LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleMouseDown(x, y, mask);  } +//virtual   bool LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1152,6 +1182,7 @@ bool LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleMouseUp(x, y, mask);  } +//virtual   bool LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1163,6 +1194,7 @@ bool LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleMiddleMouseDown(x, y, mask);  } +//virtual   bool LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1174,6 +1206,7 @@ bool LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleMiddleMouseUp(x, y, mask);  } +//virtual   bool LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1185,6 +1218,7 @@ bool LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleRightMouseDown(x, y, mask);  } +//virtual   bool LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1196,6 +1230,7 @@ bool LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleRightMouseUp(x, y, mask);  } +//virtual   bool LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)  {  	//Don't start triple click timer if user have clicked on scrollbar @@ -1215,6 +1250,7 @@ bool LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleDoubleClick(x, y, mask);  } +//virtual   bool LLTextBase::handleHover(S32 x, S32 y, MASK mask)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1226,6 +1262,7 @@ bool LLTextBase::handleHover(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleHover(x, y, mask);  } +//virtual   bool LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1237,6 +1274,7 @@ bool LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)  	return LLUICtrl::handleScrollWheel(x, y, clicks);  } +//virtual   bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)  {  	LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); @@ -1248,7 +1286,20 @@ bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)  	return LLUICtrl::handleToolTip(x, y, mask);  } +//virtual  +const std::string LLTextBase::getToolTip() const +{ +    if (sDebugUnicode) +    { +        std::string text = getText(); +        std::string tooltip = utf8str_showBytesUTF8(text); +        return tooltip; +    } + +    return LLUICtrl::getToolTip(); +} +//virtual   void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent)  {  	if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape) @@ -1275,6 +1326,7 @@ void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent)  	}  } +//virtual   void LLTextBase::draw()  {  	// reflow if needed, on demand @@ -1985,21 +2037,8 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getEditableSegIterContaini  LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index)  { -  	static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment(); -	S32 text_len = 0; -	if (!useLabel()) -	{ -		text_len = getLength(); -	} -	else -	{ -		text_len = mLabel.getWString().length(); -	} - -	if (index > text_len) { return mSegments.end(); } -  	// when there are no segments, we return the end iterator, which must be checked by caller  	if (mSegments.size() <= 1) { return mSegments.begin(); } @@ -2013,18 +2052,6 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 i  {  	static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment(); -	S32 text_len = 0; -	if (!useLabel()) -	{ -		text_len = getLength(); -	} -	else -	{ -		text_len = mLabel.getWString().length(); -	} - -	if (index > text_len) { return mSegments.end(); } -  	// when there are no segments, we return the end iterator, which must be checked by caller  	if (mSegments.size() <= 1) { return mSegments.begin(); } @@ -2188,8 +2215,8 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para  		S32 start=0,end=0;  		LLUrlMatch match;  		std::string text = new_text; -		while ( LLUrlRegistry::instance().findUrl(text, match, -				boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3),isContentTrusted() || mAlwaysShowIcons)) +		while (LLUrlRegistry::instance().findUrl(text, match, +				boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3), isContentTrusted() || mAlwaysShowIcons))  		{  			start = match.getStart();  			end = match.getEnd()+1; @@ -2437,18 +2464,18 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig  			LLStyle::Params normal_style_params(style_params);  			normal_style_params.font.style("NORMAL");  			LLStyleConstSP normal_sp(new LLStyle(normal_style_params)); -			segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this )); +			segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this));  		}  		else  		{ -		segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this )); +			segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this));  		}  		insertStringNoUndo(getLength(), wide_text, &segments);  	}  	// Set the cursor and scroll position -	if( selection_start != selection_end ) +	if (selection_start != selection_end)  	{  		mSelectionStart = selection_start;  		mSelectionEnd = selection_end; @@ -2456,7 +2483,7 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig  		mIsSelecting = was_selecting;  		setCursorPos(cursor_pos);  	} -	else if( cursor_was_at_end ) +	else if (cursor_was_at_end)  	{  		setCursorPos(getLength());  	} @@ -2468,25 +2495,28 @@ void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 hig  void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)  { -	if (new_text.empty()) return;  +	if (new_text.empty()) +	{ +		return;  +	}  	std::string::size_type start = 0; -	std::string::size_type pos = new_text.find("\n",start); +	std::string::size_type pos = new_text.find("\n", start); -	while(pos!=-1) +	while (pos != std::string::npos)  	{ -		if(pos!=start) +		if (pos != start)  		{  			std::string str = std::string(new_text,start,pos-start); -			appendAndHighlightTextImpl(str,highlight_part, style_params, underline_on_hover_only); +			appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);  		}  		appendLineBreakSegment(style_params);  		start = pos+1; -		pos = new_text.find("\n",start); +		pos = new_text.find("\n", start);  	} -	std::string str = std::string(new_text,start,new_text.length()-start); -	appendAndHighlightTextImpl(str,highlight_part, style_params, underline_on_hover_only); +	std::string str = std::string(new_text, start, new_text.length() - start); +	appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);  } @@ -3318,12 +3348,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele  		font->render(text, start,   				 rect,   				 color,  -				 LLFontGL::LEFT, mEditor.mVAlign,  +				 LLFontGL::LEFT, mEditor.mTextVAlign,  				 LLFontGL::NORMAL,   				 mStyle->getShadowType(),   				 length,  				 &right_x,  -				 mEditor.getUseEllipses()); +				 mEditor.getUseEllipses(), +				 mEditor.getUseColor());  	}  	rect.mLeft = right_x; @@ -3337,12 +3368,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele  		font->render(text, start,   				 rect,  				 mStyle->getSelectedColor().get(), -				 LLFontGL::LEFT, mEditor.mVAlign,  +				 LLFontGL::LEFT, mEditor.mTextVAlign,  				 LLFontGL::NORMAL,   				 LLFontGL::NO_SHADOW,   				 length,  				 &right_x,  -				 mEditor.getUseEllipses()); +				 mEditor.getUseEllipses(), +				 mEditor.getUseColor());  	}  	rect.mLeft = right_x;  	if( selection_end < seg_end ) @@ -3354,12 +3386,13 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele  		font->render(text, start,   				 rect,   				 color,  -				 LLFontGL::LEFT, mEditor.mVAlign,  +				 LLFontGL::LEFT, mEditor.mTextVAlign,  				 LLFontGL::NORMAL,   				 mStyle->getShadowType(),   				 length,  				 &right_x,  -				 mEditor.getUseEllipses()); +				 mEditor.getUseEllipses(), +				 mEditor.getUseColor());  	}      return right_x;  } @@ -3591,6 +3624,33 @@ const S32 LLLabelTextSegment::getLength() const  }  // +// LLEmojiTextSegment +// +LLEmojiTextSegment::LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor) +	: LLNormalTextSegment(style, start, end, editor) +{ +} + +LLEmojiTextSegment::LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible) +	: LLNormalTextSegment(color, start, end, editor, is_visible) +{ +} + +bool LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ +	if (mTooltip.empty()) +	{ +		LLWString emoji = getWText().substr(getStart(), getEnd() - getStart()); +		if (!emoji.empty()) +		{ +			mTooltip = LLEmojiHelper::instance().getToolTip(emoji[0]); +		} +	} + +	return LLNormalTextSegment::handleToolTip(x, y, mask); +} + +//  // LLOnHoverChangeableTextSegment  // diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 236f97c4d0..f5adb0dd86 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -178,6 +178,18 @@ protected:  	/*virtual*/	const S32			getLength()	const;  }; +// Text segment that represents a single emoji character that has a different style (=font size) than the rest of +// the document it belongs to +class LLEmojiTextSegment : public LLNormalTextSegment +{ +public: +	LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor); +	LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true); + +	bool canEdit() const override { return false; } +	bool handleToolTip(S32 x, S32 y, MASK mask) override; +}; +  // Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment)  class LLOnHoverChangeableTextSegment : public LLNormalTextSegment  { @@ -273,7 +285,7 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;  /// as LLTextEditor and LLTextBox. It implements shared functionality  /// such as Url highlighting and opening.  /// -class LLTextBase  +class LLTextBase  :	public LLUICtrl,  	protected LLEditMenuHandler,  	public LLSpellCheckMenuHandler, @@ -316,6 +328,7 @@ public:  								plain_text,  								wrap,  								use_ellipses, +								use_color,  								parse_urls,  								force_urls_external,  								parse_highlights, @@ -335,55 +348,58 @@ public:  		Optional<LLFontGL::ShadowType>	font_shadow; +		Optional<LLFontGL::VAlign> text_valign; +  		Params();  	};  	// LLMouseHandler interface -	/*virtual*/ bool		handleMouseDown(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleMouseUp(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleMiddleMouseDown(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleMiddleMouseUp(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleRightMouseDown(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleRightMouseUp(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		handleScrollWheel(S32 x, S32 y, S32 clicks); -	/*virtual*/ bool		handleToolTip(S32 x, S32 y, MASK mask); +	/*virtual*/ bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleMouseUp(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleMiddleMouseDown(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleMiddleMouseUp(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleRightMouseDown(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleRightMouseUp(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleDoubleClick(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleHover(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleScrollWheel(S32 x, S32 y, S32 clicks) override; +	/*virtual*/ bool		handleToolTip(S32 x, S32 y, MASK mask) override;  	// LLView interface -	/*virtual*/ void		reshape(S32 width, S32 height, bool called_from_parent = true); -	/*virtual*/ void		draw(); +	/*virtual*/ const std::string getToolTip() const override; +	/*virtual*/ void		reshape(S32 width, S32 height, bool called_from_parent = true) override; +	/*virtual*/ void		draw() override;  	// LLUICtrl interface -	/*virtual*/ bool		acceptsTextInput() const { return !mReadOnly; } -	/*virtual*/ void		setColor( const LLColor4& c ); +	/*virtual*/ bool		acceptsTextInput() const override { return !mReadOnly; } +	/*virtual*/ void		setColor(const LLColor4& c) override;  	virtual     void 		setReadOnlyColor(const LLColor4 &c); -	virtual	    void		onVisibilityChange( bool new_visibility ); +	/*virtual*/ void		onVisibilityChange(bool new_visibility) override; -	/*virtual*/ void		setValue(const LLSD& value ); -	/*virtual*/ LLTextViewModel* getViewModel() const; +	/*virtual*/ void		setValue(const LLSD& value) override; +	/*virtual*/ LLTextViewModel* getViewModel() const override;  	// LLEditMenuHandler interface -	/*virtual*/ bool		canDeselect() const; -	/*virtual*/ void		deselect(); +	/*virtual*/ bool		canDeselect() const override; +	/*virtual*/ void		deselect() override; -	virtual void	onFocusReceived(); -	virtual void	onFocusLost(); +	virtual void	onFocusReceived() override; +	virtual void	onFocusLost() override;      void        setParseHTML(bool parse_html) { mParseHTML = parse_html; }  	// LLSpellCheckMenuHandler overrides -	/*virtual*/ bool		getSpellCheck() const; +	/*virtual*/ bool		getSpellCheck() const override; -	/*virtual*/ const std::string& getSuggestion(U32 index) const; -	/*virtual*/ U32			getSuggestionCount() const; -	/*virtual*/ void		replaceWithSuggestion(U32 index); +	/*virtual*/ const std::string& getSuggestion(U32 index) const override; +	/*virtual*/ U32			getSuggestionCount() const override; +	/*virtual*/ void		replaceWithSuggestion(U32 index) override; -	/*virtual*/ void		addToDictionary(); -	/*virtual*/ bool		canAddToDictionary() const; +	/*virtual*/ void		addToDictionary() override; +	/*virtual*/ bool		canAddToDictionary() const override; -	/*virtual*/ void		addToIgnore(); -	/*virtual*/ bool		canAddToIgnore() const; +	/*virtual*/ void		addToIgnore() override; +	/*virtual*/ bool		canAddToIgnore() const override;  	// Spell checking helper functions  	std::string				getMisspelledWord(U32 pos) const; @@ -394,6 +410,7 @@ public:  	// used by LLTextSegment layout code  	bool					getWordWrap() { return mWordWrap; }  	bool					getUseEllipses() { return mUseEllipses; } +	bool					getUseColor() { return mUseColor; }  	bool					truncate(); // returns true of truncation occurred  	bool					isContentTrusted() {return mTrustedContent;} @@ -416,7 +433,7 @@ public:  	void					appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());  	void					setLabel(const LLStringExplicit& label); -	virtual bool			setLabelArg(const std::string& key, const LLStringExplicit& text ); +	/*virtual*/ bool		setLabelArg(const std::string& key, const LLStringExplicit& text) override;  	const	std::string& 	getLabel()	{ return mLabel.getString(); }  	const	LLWString&		getWlabel() { return mLabel.getWString();} @@ -633,7 +650,8 @@ protected:  	S32 normalizeUri(std::string& uri);  protected: -	virtual std::string _getSearchText() const +	// virtual +	std::string _getSearchText() const override  	{  		return mLabel.getString() + getToolTip();  	} @@ -687,8 +705,9 @@ protected:  	// configuration  	S32							mHPad;				// padding on left of text  	S32							mVPad;				// padding above text -	LLFontGL::HAlign			mHAlign; -	LLFontGL::VAlign			mVAlign; +	LLFontGL::HAlign			mHAlign;			// horizontal alignment of the document in its entirety +	LLFontGL::VAlign			mVAlign;			// vertical alignment of the document in its entirety +	LLFontGL::VAlign			mTextVAlign;		// vertical alignment of a text segment within a single line of text  	F32							mLineSpacingMult;	// multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding)  	S32							mLineSpacingPixels;	// padding between lines  	bool						mBorderVisible; @@ -697,6 +716,7 @@ protected:  	bool						mParseHighlights;	// highlight user-defined keywords  	bool                		mWordWrap;  	bool						mUseEllipses; +	bool						mUseColor;  	bool						mTrackEnd;			// if true, keeps scroll position at end of document during resize  	bool						mReadOnly;  	bool						mBGVisible;			// render background? diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 2250ed5cb3..ee6ddf553e 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -43,6 +43,7 @@  #include "llmath.h"  #include "llclipboard.h" +#include "llemojihelper.h"  #include "llscrollbar.h"  #include "llstl.h"  #include "llstring.h" @@ -238,6 +239,7 @@ LLTextEditor::Params::Params()  	default_color("default_color"),      commit_on_focus_lost("commit_on_focus_lost", false),  	show_context_menu("show_context_menu"), +	show_emoji_helper("show_emoji_helper"),  	enable_tooltip_paste("enable_tooltip_paste")  {  	addSynonym(prevalidate_callback, "text_type"); @@ -258,6 +260,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :  	mTabsToNextField(p.ignore_tab),  	mPrevalidateFunc(p.prevalidate_callback()),  	mShowContextMenu(p.show_context_menu), +	mShowEmojiHelper(p.show_emoji_helper),  	mEnableTooltipPaste(p.enable_tooltip_paste),  	mPassDelete(false),  	mKeepSelectionOnReturn(false) @@ -505,6 +508,16 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,  	}  } +void LLTextEditor::setShowEmojiHelper(bool show) +{ +	if (!mShowEmojiHelper) +	{ +		LLEmojiHelper::instance().hideHelper(this); +	} + +	mShowEmojiHelper = show; +} +  bool LLTextEditor::selectionContainsLineBreaks()  {  	if (hasSelection()) @@ -668,6 +681,28 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p  	endSelection();  } +void LLTextEditor::insertEmoji(llwchar emoji) +{ +	LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL; +	auto styleParams = LLStyle::Params(); +	styleParams.font = LLFontGL::getFontEmoji(); +	auto segment = new LLEmojiTextSegment(new LLStyle(styleParams), mCursorPos, mCursorPos + 1, *this); +	insert(mCursorPos, LLWString(1, emoji), false, segment); +	setCursorPos(mCursorPos + 1); +} + +void LLTextEditor::handleEmojiCommit(llwchar emoji) +{ +	S32 shortCodePos; +	if (LLEmojiHelper::isCursorInEmojiCode(getWText(), mCursorPos, &shortCodePos)) +	{ +		remove(shortCodePos, mCursorPos - shortCodePos, true); +		setCursorPos(shortCodePos); + +		insertEmoji(emoji); +	} +} +  bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)  {  	bool	handled = false; @@ -934,6 +969,12 @@ bool LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)  S32 LLTextEditor::execute( TextCmd* cmd )  { +	if (!mReadOnly && mShowEmojiHelper) +	{ +		// Any change to our contents should always hide the helper +		LLEmojiHelper::instance().hideHelper(this); +	} +  	S32 delta = 0;  	if( cmd->execute(this, &delta) )  	{ @@ -983,7 +1024,7 @@ S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)  	// store text segments  	getSegmentsInRange(segments_to_remove, pos, pos + length, false); -	if(pos <= end_pos) +	if (pos <= end_pos)  	{  		removedChar = execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );  	} @@ -1007,11 +1048,12 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)  // a pseudo-tab (up to for spaces in a row)  void LLTextEditor::removeCharOrTab()  { -	if( !getEnabled() ) +	if (!getEnabled())  	{  		return;  	} -	if( mCursorPos > 0 ) + +	if (mCursorPos > 0)  	{  		S32 chars_to_remove = 1; @@ -1023,14 +1065,14 @@ void LLTextEditor::removeCharOrTab()  			if (offset > 0)  			{  				chars_to_remove = offset % SPACES_PER_TAB; -				if( chars_to_remove == 0 ) +				if (chars_to_remove == 0)  				{  					chars_to_remove = SPACES_PER_TAB;  				} -				for( S32 i = 0; i < chars_to_remove; i++ ) +				for (S32 i = 0; i < chars_to_remove; i++)  				{ -					if (text[ mCursorPos - i - 1] != ' ') +					if (text[mCursorPos - i - 1] != ' ')  					{  						// Fewer than a full tab's worth of spaces, so  						// just delete a single character. @@ -1044,8 +1086,10 @@ void LLTextEditor::removeCharOrTab()  		for (S32 i = 0; i < chars_to_remove; i++)  		{  			setCursorPos(mCursorPos - 1); -			remove( mCursorPos, 1, false ); +			remove(mCursorPos, 1, false);  		} + +		tryToShowEmojiHelper();  	}  	else  	{ @@ -1056,7 +1100,7 @@ void LLTextEditor::removeCharOrTab()  // Remove a single character from the text  S32 LLTextEditor::removeChar(S32 pos)  { -	return remove( pos, 1, false ); +	return remove(pos, 1, false);  }  void LLTextEditor::removeChar() @@ -1065,10 +1109,12 @@ void LLTextEditor::removeChar()  	{  		return;  	} +  	if (mCursorPos > 0)  	{  		setCursorPos(mCursorPos - 1);  		removeChar(mCursorPos); +		tryToShowEmojiHelper();  	}  	else  	{ @@ -1127,6 +1173,7 @@ void LLTextEditor::addChar(llwchar wc)  	}  	setCursorPos(mCursorPos + addChar( mCursorPos, wc )); +	tryToShowEmojiHelper();  	if (!mReadOnly && mAutoreplaceCallback != NULL)  	{ @@ -1146,6 +1193,37 @@ void LLTextEditor::addChar(llwchar wc)  	}  } +void LLTextEditor::showEmojiHelper() +{ +    if (mReadOnly || !mShowEmojiHelper) +        return; + +    const LLRect cursorRect(getLocalRectFromDocIndex(mCursorPos)); +    auto cb = [this](llwchar emoji) { insertEmoji(emoji); }; +    LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb); +} + +void LLTextEditor::tryToShowEmojiHelper() +{ +    if (mReadOnly || !mShowEmojiHelper) +        return; + +    S32 shortCodePos; +    LLWString wtext(getWText()); +    if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos)) +    { +        const LLRect cursorRect(getLocalRectFromDocIndex(shortCodePos)); +        const LLWString wpart(wtext.substr(shortCodePos, mCursorPos - shortCodePos)); +        const std::string part(wstring_to_utf8str(wpart)); +        auto cb = [this](llwchar emoji) { handleEmojiCommit(emoji); }; +        LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, part, cb); +    } +    else +    { +        LLEmojiHelper::instance().hideHelper(); +    } +} +  void LLTextEditor::addLineBreakChar(bool group_together)  {  	if( !getEnabled() ) @@ -1778,6 +1856,11 @@ bool LLTextEditor::handleKeyHere(KEY key, MASK mask )  	}  	else   	{ +		if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask)) +		{ +			return true; +		} +  		if (mEnableTooltipPaste &&  			LLToolTipMgr::instance().toolTipVisible() &&   			KEY_TAB == key) @@ -1819,6 +1902,12 @@ bool LLTextEditor::handleKeyHere(KEY key, MASK mask )  	{  		resetCursorBlink();  		needsScroll(); + +		if (mShowEmojiHelper) +		{ +			// Dismiss the helper whenever we handled a key that it didn't +			LLEmojiHelper::instance().hideHelper(this); +		}  	}  	return handled; @@ -1837,7 +1926,12 @@ bool LLTextEditor::handleUnicodeCharHere(llwchar uni_char)  	// Handle most keys only if the text editor is writeable.  	if( !mReadOnly )  	{ -		if( mAutoIndent && '}' == uni_char ) +        if (mShowEmojiHelper && uni_char < 0x80 && LLEmojiHelper::instance().handleKey(this, (KEY)uni_char, MASK_NONE)) +        { +            return true; +        } + +        if( mAutoIndent && '}' == uni_char )  		{  			unindentLineBeforeCloseBrace();  		} diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index b96a433d5d..873a028702 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -60,6 +60,7 @@ public:  								ignore_tab,  								commit_on_focus_lost,  								show_context_menu, +								show_emoji_helper,  								enable_tooltip_paste,  								auto_indent; @@ -91,6 +92,9 @@ public:  	static S32		spacesPerTab(); +	void    insertEmoji(llwchar emoji); +	void    handleEmojiCommit(llwchar emoji); +  	// mousehandler overrides  	virtual bool	handleMouseDown(S32 x, S32 y, MASK mask);  	virtual bool	handleMouseUp(S32 x, S32 y, MASK mask); @@ -202,6 +206,10 @@ public:  	void			setShowContextMenu(bool show) { mShowContextMenu = show; }  	bool			getShowContextMenu() const { return mShowContextMenu; } +	void			showEmojiHelper(); +	void			setShowEmojiHelper(bool show); +	bool			getShowEmojiHelper() const { return mShowEmojiHelper; } +  	void			setPassDelete(bool b) { mPassDelete = b; }  protected: @@ -248,6 +256,7 @@ protected:  	S32				insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment);  	S32				remove(S32 pos, S32 length, bool group_with_next_op); +	void			tryToShowEmojiHelper();  	void			focusLostHelper();  	void			updateAllowingLanguageInput();  	bool			hasPreeditString() const; @@ -318,6 +327,7 @@ private:  	bool			mAllowEmbeddedItems;  	bool			mShowContextMenu; +	bool			mShowEmojiHelper;  	bool			mEnableTooltipPaste;  	bool			mPassDelete;  	bool			mKeepSelectionOnReturn;	// disabling of removing selected text after pressing of Enter diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index a248e0941d..8d57a69c6e 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -768,25 +768,20 @@ void LLUICtrl::setIsChrome(bool is_chrome)  // virtual  bool LLUICtrl::getIsChrome() const -{  +{ +	if (mIsChrome) +		return true; +  	LLView* parent_ctrl = getParent(); -	while(parent_ctrl) +	while (parent_ctrl)  	{ -		if(parent_ctrl->isCtrl()) -		{ -			break;	 -		} +		if (parent_ctrl->isCtrl()) +			return ((LLUICtrl*)parent_ctrl)->getIsChrome(); +  		parent_ctrl = parent_ctrl->getParent();  	} -	 -	if(parent_ctrl) -	{ -		return mIsChrome || ((LLUICtrl*)parent_ctrl)->getIsChrome(); -	} -	else -	{ -		return mIsChrome ;  -	} + +	return false;   } diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index b2b64251ba..4f67a40cfb 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -264,7 +264,7 @@ public:  	class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter>  	{  		LLSINGLETON_EMPTY_CTOR(LLTextInputFilter); -		/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const  +		/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override  		{  			return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl *>(view)->acceptsTextInput(), true);  		} diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h index 07e02de6d8..b1089a3903 100644 --- a/indra/llui/lluistring.h +++ b/indra/llui/lluistring.h @@ -61,6 +61,7 @@ public:  	LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {}  	LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args);  	LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); } +	LLUIString(const LLWString& instring) : mArgs(NULL) { insert(0, instring); }  	~LLUIString() { delete mArgs; }  	void assign(const std::string& instring); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 894c8f6a32..f3ef7591e9 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -60,6 +60,7 @@ static const S32 LINE_HEIGHT = 15;  S32		LLView::sDepth = 0;  bool	LLView::sDebugRects = false; +bool	LLView::sDebugUnicode = false;  bool	LLView::sIsRectDirty = false;  LLRect	LLView::sDirtyRect;  bool	LLView::sDebugRectsShowNames = true; @@ -520,7 +521,7 @@ bool LLView::focusNext(LLView::child_list_t & result)  		{  			next = result.rbegin();  		} -		if((*next)->isCtrl()) +		if ((*next)->isCtrl() && ((LLUICtrl*)*next)->hasTabStop())  		{  			LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);  			ctrl->setFocus(true); @@ -1024,7 +1025,7 @@ bool LLView::handleUnicodeChar(llwchar uni_char, bool called_from_parent)  			handled = handleUnicodeCharHere(uni_char);  			if (handled && LLView::sDebugKeys)  			{ -				LL_INFOS() << "Unicode key handled by " << getName() << LL_ENDL; +				LL_INFOS() << "Unicode key " << wchar_utf8_preview(uni_char) << " is handled by " << getName() << LL_ENDL;  			}  		}  	} @@ -1336,8 +1337,7 @@ void LLView::drawDebugRect()  			std::string debug_text = llformat("%s (%d x %d)", getName().c_str(),  										debug_rect.getWidth(), debug_rect.getHeight());  			LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, -												LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -												S32_MAX, S32_MAX, NULL, false); +					LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);  		}  	}  	LLUI::popMatrix(); @@ -1753,23 +1753,26 @@ LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S3  	const S32 KEEP_ONSCREEN_PIXELS_WIDTH = llmin(min_overlap_pixels, input.getWidth());  	const S32 KEEP_ONSCREEN_PIXELS_HEIGHT = llmin(min_overlap_pixels, input.getHeight()); -	if( input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft ) -	{ -		delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH); -	} -	else if( input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight ) +	if (KEEP_ONSCREEN_PIXELS_WIDTH <= constraint.getWidth() && +		KEEP_ONSCREEN_PIXELS_HEIGHT <= constraint.getHeight())  	{ -		delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH); -	} +		if (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft) +		{ +			delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH); +		} +		else if (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight) +		{ +			delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH); +		} -	if( input.mTop > constraint.mTop ) -	{ -		delta.mY = constraint.mTop - input.mTop; -	} -	else -	if( input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom ) -	{ -		delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT); +		if (input.mTop > constraint.mTop) +		{ +			delta.mY = constraint.mTop - input.mTop; +		} +		else if (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom) +		{ +			delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT); +		}  	}  	return delta; @@ -1780,13 +1783,19 @@ LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S3  // (Why top and left?  That's where the drag bars are for floaters.)  bool LLView::translateIntoRect(const LLRect& constraint, S32 min_overlap_pixels)  { -	LLCoordGL translation = getNeededTranslation(getRect(), constraint, min_overlap_pixels); +    return translateRectIntoRect(getRect(), constraint, min_overlap_pixels); +} + +bool LLView::translateRectIntoRect(const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels) +{ +	LLCoordGL translation = getNeededTranslation(rect, constraint, min_overlap_pixels);  	if (translation.mX != 0 || translation.mY != 0)  	{  		translate(translation.mX, translation.mY);  		return true;  	} +  	return false;  } @@ -1966,7 +1975,7 @@ private:  class SortByTabOrder : public LLQuerySorter, public LLSingleton<SortByTabOrder>  {  	LLSINGLETON_EMPTY_CTOR(SortByTabOrder); -	/*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const  +	/*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const override  	{  		children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup()));  	} @@ -1990,7 +1999,7 @@ const LLViewQuery & LLView::getTabOrderQuery()  class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>  {  	LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter); -	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const  +	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override  	{  		return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());  	} diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 8ac23f9f88..1e35f0092d 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -111,7 +111,7 @@ public:  		Alternative<std::string>	string;  		Alternative<U32>			flags; -        Follows(); +		Follows();  	};  	struct Params : public LLInitParam::Block<Params> @@ -369,6 +369,7 @@ public:  	virtual void	translate( S32 x, S32 y );  	void			setOrigin( S32 x, S32 y )	{ mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); }  	bool			translateIntoRect( const LLRect& constraint, S32 min_overlap_pixels = S32_MAX); +	bool			translateRectIntoRect( const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels = S32_MAX);  	bool			translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels = S32_MAX);  	void			centerWithin(const LLRect& bounds); @@ -658,8 +659,11 @@ public:  	// Draw debug rectangles around widgets to help with alignment and spacing  	static bool	sDebugRects; -    static bool sIsRectDirty; -    static LLRect sDirtyRect; +	// Show hexadecimal byte values of unicode symbols in a tooltip +	static bool	sDebugUnicode; + +	static bool sIsRectDirty; +	static LLRect sDirtyRect;  	// Draw widget names and sizes when drawing debug rectangles, turning this  	// off is useful to make the rectangles themselves easier to see. @@ -702,20 +706,16 @@ template <class T> T* LLView::getChild(const std::string& name, bool recurse) co  		if (!result)  		{  			result = LLUICtrlFactory::getDefaultWidget<T>(name); - -			if (result) +			if (!result)  			{ -				// *NOTE: You cannot call mFoo = getChild<LLFoo>("bar") -				// in a floater or panel constructor.  The widgets will not -				// be ready.  Instead, put it in postBuild(). -				LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL; -			} -			else -			{ -				LL_WARNS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL; -				return NULL; +				LL_ERRS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL;  			} +			// *NOTE: You cannot call mFoo = getChild<LLFoo>("bar") +			// in a floater or panel constructor.  The widgets will not +			// be ready.  Instead, put it in postBuild(). +			LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL; +  			getDefaultWidgetContainer().addChild(result);  		}  	} diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h index 780f74f03c..c7d1ffac20 100644 --- a/indra/llui/llviewquery.h +++ b/indra/llui/llviewquery.h @@ -55,37 +55,37 @@ public:  class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter>  {  	LLSINGLETON_EMPTY_CTOR(LLLeavesFilter); -	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;  };  class LLRootsFilter : public LLQueryFilter, public LLSingleton<LLRootsFilter>  {  	LLSINGLETON_EMPTY_CTOR(LLRootsFilter); -	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;  };  class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter>  {  	LLSINGLETON_EMPTY_CTOR(LLVisibleFilter); -	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;  };  class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter>  {  	LLSINGLETON_EMPTY_CTOR(LLEnabledFilter); -	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;  };  class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter>  {  	LLSINGLETON_EMPTY_CTOR(LLTabStopFilter); -	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;  };  class LLCtrlFilter : public LLQueryFilter, public LLSingleton<LLCtrlFilter>  {  	LLSINGLETON_EMPTY_CTOR(LLCtrlFilter); -	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const; +	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;  };  template <class T> diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm index 586e00b5e4..0bd4e506a2 100644 --- a/indra/llwindow/llopenglview-objc.mm +++ b/indra/llwindow/llopenglview-objc.mm @@ -733,23 +733,52 @@ attributedStringInfo getSegments(NSAttributedString *str)  - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange  { -	if (!mHasMarkedText) +	// SL-19801 Special workaround for system emoji picker +	if ([aString length] == 2)  	{ -		for (NSInteger i = 0; i < [aString length]; i++) -		{ -			callUnicodeCallback([aString characterAtIndex:i], mModifiers); -		} -	} else { -        resetPreedit(); -		// We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text. -		// But just in case... -		 -		for (NSInteger i = 0; i < [aString length]; i++) -		{ -			handleUnicodeCharacter([aString characterAtIndex:i]); -		} -		mHasMarkedText = FALSE; +        @try +        { +            uint32_t b0 = [aString characterAtIndex:0]; +            uint32_t b1 = [aString characterAtIndex:1]; +            if (((b0 & 0xF000) == 0xD000) && ((b1 & 0xF000) == 0xD000)) +            { +                uint32_t b = 0x10000 | ((b0 & 0x3F) << 10) | (b1 & 0x3FF); +                callUnicodeCallback(b, 0); +                return; +            } +        } +        @catch(NSException * e) +        { +            // One of the characters is an attribute string? +            NSLog(@"Encountered an unsupported attributed character. Exception: %@ String: %@", e.name, aString); +            return; +        }  	} +     +    @try +    { +        if (!mHasMarkedText) +        { +            for (NSInteger i = 0; i < [aString length]; i++) +            { +                callUnicodeCallback([aString characterAtIndex:i], mModifiers); +            } +        } else { +            resetPreedit(); +            // We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text. +            // But just in case... +             +            for (NSInteger i = 0; i < [aString length]; i++) +            { +                handleUnicodeCharacter([aString characterAtIndex:i]); +            } +            mHasMarkedText = FALSE; +        } +    } +    @catch(NSException * e) +    { +        NSLog(@"Failed to process an attributed string. Exception: %@ String: %@", e.name, aString); +    }  }  - (void) insertNewline:(id)sender diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 156acb191c..58aa04829b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -17,6 +17,7 @@ include(DBusGlib)  include(DragDrop)  include(EXPAT)  include(Hunspell) +include(ICU4C)  include(JPEGEncoderBasic)  include(JsonCpp)  include(LLAppearance) @@ -205,6 +206,7 @@ set(viewer_SOURCE_FILES      llfloaterdisplayname.cpp      llfloatereditenvironmentbase.cpp      llfloatereditextdaycycle.cpp +    llfloateremojipicker.cpp      llfloaterenvironmentadjust.cpp      llfloaterevent.cpp      llfloaterexperiencepicker.cpp @@ -415,6 +417,7 @@ set(viewer_SOURCE_FILES      llpaneleditsky.cpp      llpaneleditwater.cpp      llpaneleditwearable.cpp +    llpanelemojicomplete.cpp      llpanelenvironment.cpp      llpanelexperiencelisteditor.cpp      llpanelexperiencelog.cpp @@ -852,6 +855,7 @@ set(viewer_HEADER_FILES      llfloaterdisplayname.h      llfloatereditenvironmentbase.h      llfloatereditextdaycycle.h +    llfloateremojipicker.h      llfloaterenvironmentadjust.h      llfloaterevent.h      llfloaterexperiencepicker.h @@ -1054,6 +1058,7 @@ set(viewer_HEADER_FILES      llpaneleditsky.h      llpaneleditwater.h      llpaneleditwearable.h +    llpanelemojicomplete.h      llpanelenvironment.h      llpanelexperiencelisteditor.h      llpanelexperiencelog.h @@ -1907,6 +1912,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}          ${LLPHYSICSEXTENSIONS_LIBRARIES}          ll::bugsplat          ll::tracy +        ll::icu4c          )  if( TARGET ll::intel_memops ) @@ -1920,6 +1926,28 @@ endif()  set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH      "Path to artwork files.") +message("Copying fonts") +file(GLOB FONT_FILE_GLOB_LIST +  "${AUTOBUILD_INSTALL_DIR}/fonts/*" +) +file(COPY ${FONT_FILE_GLOB_LIST} DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}/fonts") + +# Copy over the Emoji/shortcodes mapping XML files (and create dependency +# if they are changed, CMake will run again and copy over new versions) +message("Copying Emoji/shortcode mappings") +set(emoji_mapping_src_folder ${AUTOBUILD_INSTALL_DIR}/xui) +set(emoji_mapping_dst_folder ${CMAKE_CURRENT_SOURCE_DIR}/skins/default/xui) + +# Note Turkey is missing from this set (not available in Emoji package yet) +set(country_codes "da;de;en;es;fr;it;ja;pl;pt;ru;zh") +foreach(elem ${country_codes}) +   set(emoji_mapping_src_file +      "${emoji_mapping_src_folder}/${elem}/emoji_characters.xml") +   set(emoji_mapping_dst_file +      "${emoji_mapping_dst_folder}/${elem}/emoji_characters.xml")       +   configure_file(${emoji_mapping_src_file} ${emoji_mapping_dst_file} COPYONLY) +endforeach() +  if (LINUX)    set(product SecondLife-${ARCH}-${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 1996c50447..b7f8ee41e6 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -7.1.3 +7.1.4 diff --git a/indra/newview/app_settings/emoji_groups.xml b/indra/newview/app_settings/emoji_groups.xml new file mode 100644 index 0000000000..b433927f91 --- /dev/null +++ b/indra/newview/app_settings/emoji_groups.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>all</string> +      <key>Character</key> +      <string>🔍</string> +    </map> +    <map> +      <key>Character</key> +      <string>😀</string> +      <key>Categories</key> +      <array> +        <string>smileys and emotion</string> +        <string>people and body</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🥬</string> +      <key>Categories</key> +      <array> +        <string>animals and nature</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🍔</string> +      <key>Categories</key> +      <array> +        <string>food and drink</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🛩</string> +      <key>Categories</key> +      <array> +        <string>travel and places</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>🏈</string> +      <key>Categories</key> +      <array> +        <string>activities</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>💡</string> +      <key>Categories</key> +      <array> +        <string>objects</string> +      </array> +    </map> +    <map> +      <key>Character</key> +      <string>⚠</string> +      <key>Categories</key> +      <array> +        <string>symbols</string> +      </array> +    </map> +    <map> +      <key>Name</key> +      <string>others</string> +      <key>Character</key> +      <string>🌂</string> +    </map> +    <map> +      <key>Name</key> +      <string>skip</string> +      <key>Categories</key> +      <array> +        <string>components</string> +      </array> +    </map> +  </array> +</llsd> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a787010346..1bd24128e3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11233,7 +11233,7 @@  			<key>Type</key>  			<string>String</string>  			<key>Value</key> -			<string>https://jira.secondlife.com/secure/CreateIssueDetails!init.jspa?pid=10610&issuetype=1&environment=[ENVIRONMENT]&customfield_10253=[LOCATION]</string> +			<string>https://feedback.secondlife.com/</string>  		</map>  	<key>RevokePermsOnStopAnimation</key>      <map> diff --git a/indra/newview/fonts/DejaVu-license.txt b/indra/newview/fonts/DejaVu-license.txt deleted file mode 100644 index df52c1709b..0000000000 --- a/indra/newview/fonts/DejaVu-license.txt +++ /dev/null @@ -1,187 +0,0 @@ -Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. -Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) - - -Bitstream Vera Fonts Copyright ------------------------------- - -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is -a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of the fonts accompanying this license ("Fonts") and associated -documentation files (the "Font Software"), to reproduce and distribute the -Font Software, including without limitation the rights to use, copy, merge, -publish, distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to the -following conditions: - -The above copyright and trademark notices and this permission notice shall -be included in all copies of one or more of the Font Software typefaces. - -The Font Software may be modified, altered, or added to, and in particular -the designs of glyphs or characters in the Fonts may be modified and -additional glyphs or characters may be added to the Fonts, only if the fonts -are renamed to names not containing either the words "Bitstream" or the word -"Vera". - -This License becomes null and void to the extent applicable to Fonts or Font -Software that has been modified and is distributed under the "Bitstream -Vera" names. - -The Font Software may be sold as part of a larger software package but no -copy of one or more of the Font Software typefaces may be sold by itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, -TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME -FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING -ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF -THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE -FONT SOFTWARE. - -Except as contained in this notice, the names of Gnome, the Gnome -Foundation, and Bitstream Inc., shall not be used in advertising or -otherwise to promote the sale, use or other dealings in this Font Software -without prior written authorization from the Gnome Foundation or Bitstream -Inc., respectively. For further information, contact: fonts at gnome dot -org. - -Arev Fonts Copyright ------------------------------- - -Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the fonts accompanying this license ("Fonts") and -associated documentation files (the "Font Software"), to reproduce -and distribute the modifications to the Bitstream Vera Font Software, -including without limitation the rights to use, copy, merge, publish, -distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to -the following conditions: - -The above copyright and trademark notices and this permission notice -shall be included in all copies of one or more of the Font Software -typefaces. - -The Font Software may be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may be -modified and additional glyphs or characters may be added to the -Fonts, only if the fonts are renamed to names not containing either -the words "Tavmjong Bah" or the word "Arev". - -This License becomes null and void to the extent applicable to Fonts -or Font Software that has been modified and is distributed under the  -"Tavmjong Bah Arev" names. - -The Font Software may be sold as part of a larger software package but -no copy of one or more of the Font Software typefaces may be sold by -itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL -TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. - -Except as contained in this notice, the name of Tavmjong Bah shall not -be used in advertising or otherwise to promote the sale, use or other -dealings in this Font Software without prior written authorization -from Tavmjong Bah. For further information, contact: tavmjong @ free -. fr. - -TeX Gyre DJV Math ------------------ -Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. - -Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski -(on behalf of TeX users groups) are in public domain. - -Letters imported from Euler Fraktur from AMSfonts are (c) American -Mathematical Society (see below). -Bitstream Vera Fonts Copyright -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera -is a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of the fonts accompanying this license (“Fonts”) and associated -documentation -files (the “Font Software”), to reproduce and distribute the Font Software, -including without limitation the rights to use, copy, merge, publish, -distribute, -and/or sell copies of the Font Software, and to permit persons  to whom -the Font Software is furnished to do so, subject to the following -conditions: - -The above copyright and trademark notices and this permission notice -shall be -included in all copies of one or more of the Font Software typefaces. - -The Font Software may be modified, altered, or added to, and in particular -the designs of glyphs or characters in the Fonts may be modified and -additional -glyphs or characters may be added to the Fonts, only if the fonts are -renamed -to names not containing either the words “Bitstream” or the word “Vera”. - -This License becomes null and void to the extent applicable to Fonts or -Font Software -that has been modified and is distributed under the “Bitstream Vera” -names. - -The Font Software may be sold as part of a larger software package but -no copy -of one or more of the Font Software typefaces may be sold by itself. - -THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, -TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME -FOUNDATION -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, -SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN -ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR -INABILITY TO USE -THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -Except as contained in this notice, the names of GNOME, the GNOME -Foundation, -and Bitstream Inc., shall not be used in advertising or otherwise to promote -the sale, use or other dealings in this Font Software without prior written -authorization from the GNOME Foundation or Bitstream Inc., respectively. -For further information, contact: fonts at gnome dot org. - -AMSFonts (v. 2.2) copyright - -The PostScript Type 1 implementation of the AMSFonts produced by and -previously distributed by Blue Sky Research and Y&Y, Inc. are now freely -available for general use. This has been accomplished through the -cooperation -of a consortium of scientific publishers with Blue Sky Research and Y&Y. -Members of this consortium include: - -Elsevier Science IBM Corporation Society for Industrial and Applied -Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) - -In order to assure the authenticity of these fonts, copyright will be -held by -the American Mathematical Society. This is not meant to restrict in any way -the legitimate use of the fonts, such as (but not limited to) electronic -distribution of documents containing these fonts, inclusion of these fonts -into other public domain or commercial font collections or computer -applications, use of the outline data to create derivative fonts and/or -faces, etc. However, the AMS does require that the AMS copyright notice be -removed from any derivative versions of the fonts which have been altered in -any way. In addition, to ensure the fidelity of TeX documents using Computer -Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, -has requested that any alterations which yield different font metrics be -given a different name. - -$Id$ diff --git a/indra/newview/fonts/DejaVuSans-Bold.ttf b/indra/newview/fonts/DejaVuSans-Bold.ttfBinary files differ deleted file mode 100644 index 6d65fa7dc4..0000000000 --- a/indra/newview/fonts/DejaVuSans-Bold.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf b/indra/newview/fonts/DejaVuSans-BoldOblique.ttfBinary files differ deleted file mode 100644 index 753f2d80b1..0000000000 --- a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans-Oblique.ttf b/indra/newview/fonts/DejaVuSans-Oblique.ttfBinary files differ deleted file mode 100644 index 999bac7714..0000000000 --- a/indra/newview/fonts/DejaVuSans-Oblique.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans.ttf b/indra/newview/fonts/DejaVuSans.ttfBinary files differ deleted file mode 100644 index e5f7eecce4..0000000000 --- a/indra/newview/fonts/DejaVuSans.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSansMono.ttf b/indra/newview/fonts/DejaVuSansMono.ttfBinary files differ deleted file mode 100644 index f5786022f1..0000000000 --- a/indra/newview/fonts/DejaVuSansMono.ttf +++ /dev/null diff --git a/indra/newview/llautoreplace.h b/indra/newview/llautoreplace.h index 23cc313646..a1eebf9dcb 100644 --- a/indra/newview/llautoreplace.h +++ b/indra/newview/llautoreplace.h @@ -203,7 +203,7 @@ public:      void setSettings(const LLAutoReplaceSettings& settings);  private: -    /*virtual*/ void initSingleton(); +    /*virtual*/ void initSingleton() override;      LLAutoReplaceSettings mSettings; ///< configuration information diff --git a/indra/newview/llchannelmanager.h b/indra/newview/llchannelmanager.h index 8abe350196..22ae595d66 100644 --- a/indra/newview/llchannelmanager.h +++ b/indra/newview/llchannelmanager.h @@ -46,7 +46,7 @@ class LLChannelManager : public LLSingleton<LLChannelManager>  	LLSINGLETON(LLChannelManager);  	virtual ~LLChannelManager(); -	void cleanupSingleton(); +	void cleanupSingleton() override;  public: diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 0b7cdd1db4..0c365d33c4 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -1096,6 +1096,8 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)  	editor_params.enabled = false; // read only  	editor_params.show_context_menu = "true";  	editor_params.trusted_content = false; +	editor_params.text_valign = LLFontGL::VAlign::VCENTER; +	editor_params.use_color = true;  	mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);  	mEditor->setIsFriendCallback(LLAvatarActions::isFriend);  	mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0)); @@ -1213,9 +1215,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL  	llassert(mEditor);  	if (!mEditor) -	{  		return; -	}  	bool from_me = chat.mFromID == gAgent.getID();  	mEditor->setPlainText(use_plain_text_chat_history); @@ -1225,26 +1225,16 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL  		mUnreadChatSources.insert(chat.mFromName);  		mMoreChatPanel->setVisible(true);  		std::string chatters; -		for (unread_chat_source_t::iterator it = mUnreadChatSources.begin(); -			it != mUnreadChatSources.end();) +		for (const std::string& source : mUnreadChatSources)  		{ -			chatters += *it; -			if (++it != mUnreadChatSources.end()) -			{ -				chatters += ", "; -			} +			chatters += chatters.size() ? ", " + source : source;  		}  		LLStringUtil::format_map_t args;  		args["SOURCES"] = chatters; -		if (mUnreadChatSources.size() == 1) -		{ -			mMoreChatText->setValue(LLTrans::getString("unread_chat_single", args)); -		} -		else -		{ -			mMoreChatText->setValue(LLTrans::getString("unread_chat_multiple", args)); -		} +		std::string xml_desc = mUnreadChatSources.size() == 1 ? +			"unread_chat_single" : "unread_chat_multiple"; +		mMoreChatText->setValue(LLTrans::getString(xml_desc, args));  		S32 height = mMoreChatText->getTextPixelHeight() + 5;  		mMoreChatPanel->reshape(mMoreChatPanel->getRect().getWidth(), height);  	} @@ -1292,11 +1282,11 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL  		body_message_params.font.style = "ITALIC";  	} -	if(chat.mChatType == CHAT_TYPE_WHISPER) +	if (chat.mChatType == CHAT_TYPE_WHISPER)  	{  		body_message_params.font.style = "ITALIC";  	} -	else if(chat.mChatType == CHAT_TYPE_SHOUT) +	else if (chat.mChatType == CHAT_TYPE_SHOUT)  	{  		body_message_params.font.style = "BOLD";  	} @@ -1343,10 +1333,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL  		}  		// names showing -		if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size() != 0) +		if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size())  		{  			// Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text. -			if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull()) +			if (chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull())  			{  				// for object IMs, create a secondlife:///app/objectim SLapp  				std::string url = LLViewerChat::getSenderSLURL(chat, args); @@ -1406,36 +1396,27 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL  			&& mIsLastMessageFromLog == message_from_log)  //distinguish between current and previous chat session's histories  		{  			view = getSeparator(); -			p.top_pad = mTopSeparatorPad; -			p.bottom_pad = mBottomSeparatorPad;              if (!view)              {                  // Might be wiser to make this LL_ERRS, getSeparator() should work in case of correct instalation.                  LL_WARNS() << "Failed to create separator from " << mMessageSeparatorFilename << ": can't append to history" << LL_ENDL;                  return;              } + +			p.top_pad = mTopSeparatorPad; +			p.bottom_pad = mBottomSeparatorPad;  		}  		else  		{  			view = getHeader(chat, name_params, args); -			if (mEditor->getLength() == 0) -				p.top_pad = 0; -			else -				p.top_pad = mTopHeaderPad; -            if (teleport_separator) -            { -                p.bottom_pad = mBottomSeparatorPad; -            } -            else -            { -                p.bottom_pad = mBottomHeaderPad; -            } -            if (!view) -            { -                LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL; -                return; -            } +			if (!view) +			{ +				LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL; +				return; +			} +			p.top_pad = mEditor->getLength() ? mTopHeaderPad : 0; +			p.bottom_pad = teleport_separator ? mBottomSeparatorPad : mBottomHeaderPad;  		}  		p.view = view; @@ -1508,11 +1489,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL  		}  	}  	// usual messages showing -	else if(!teleport_separator) +	else if (!teleport_separator)  	{  		std::string message = irc_me ? chat.mText.substr(3) : chat.mText; -  		//MESSAGE TEXT PROCESSING  		//*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010)  		if (use_plain_text_chat_history && !from_me && chat.mFromID.notNull()) diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h index 8658c93486..033ad2b6e9 100644 --- a/indra/newview/llconversationlog.h +++ b/indra/newview/llconversationlog.h @@ -125,11 +125,11 @@ public:  	void removeObserver(LLConversationLogObserver* observer);  	// LLIMSessionObserver triggers -	virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, bool has_offline_msg); -    virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; // Stub -	virtual void sessionRemoved(const LLUUID& session_id){}											// Stub -	virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){};								// Stub -	virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){};	// Stub +	virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, bool has_offline_msg) override; +    virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) override {}; // Stub +	virtual void sessionRemoved(const LLUUID& session_id) override{}											// Stub +	virtual void sessionVoiceOrIMStarted(const LLUUID& session_id) override{};								// Stub +	virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) override{};	// Stub  	void notifyObservers(); diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 38da03911c..5af9247285 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -88,7 +88,7 @@ public:  									mStyle->getShadowType(),   									end - start, draw_rect.getWidth(),   									&right_x,  -									mEditor.getUseEllipses()); +									mEditor.getUseEllipses(), mEditor.getUseColor());  		return right_x;  	}  	/*virtual*/ bool	canEdit() const { return false; } diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h index e5dedfbd24..35d1267a45 100644 --- a/indra/newview/llfeaturemanager.h +++ b/indra/newview/llfeaturemanager.h @@ -101,7 +101,7 @@ class LLFeatureManager : public LLFeatureList, public LLSingleton<LLFeatureManag  	~LLFeatureManager() {cleanupFeatureTables();}  	// initialize this by loading feature table and gpu table -	void initSingleton(); +	void initSingleton() override;  public: diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index 8c4e7ff627..5dd0388129 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -741,7 +741,6 @@ void LLFloaterAvatarPicker::processResponse(const LLUUID& query_id, const LLSD&  	}  } -//static  void LLFloaterAvatarPicker::editKeystroke(LLLineEditor* caller, void* user_data)  {  	getChildView("Find")->setEnabled(caller->getText().size() > 0); diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp new file mode 100644 index 0000000000..bfde125476 --- /dev/null +++ b/indra/newview/llfloateremojipicker.cpp @@ -0,0 +1,1346 @@ +/** + * @file llfloateremojipicker.cpp + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llfloateremojipicker.h" + +#include "llappviewer.h" +#include "llbutton.h" +#include "llcombobox.h" +#include "llemojidictionary.h" +#include "llemojihelper.h" +#include "llfloaterreg.h" +#include "llkeyboard.h" +#include "llscrollcontainer.h" +#include "llscrollingpanellist.h" +#include "llscrolllistctrl.h" +#include "llscrolllistitem.h" +#include "llsdserialize.h" +#include "lltextbox.h"  +#include "llviewerchat.h"  + +namespace { +// The following variables and constants are used for storing the floater state +// between different lifecycles of the floater and different sissions of the viewer + +// Floater constants +static const S32 ALL_EMOJIS_GROUP_INDEX = -2; +// https://www.compart.com/en/unicode/U+1F50D +static const S32 ALL_EMOJIS_IMAGE_INDEX = 0x1F50D; +static const S32 USED_EMOJIS_GROUP_INDEX = -1; +// https://www.compart.com/en/unicode/U+23F2 +static const S32 USED_EMOJIS_IMAGE_INDEX = 0x23F2; +// https://www.compart.com/en/unicode/U+1F6D1 +static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1; +// The following categories should follow the required alphabetic order +static const std::string RECENTLY_USED_CATEGORY = "1 recently used"; +static const std::string FREQUENTLY_USED_CATEGORY = "2 frequently used"; + +// Floater state related variables +static std::list<llwchar> sRecentlyUsed; +static std::list<std::pair<llwchar, U32>> sFrequentlyUsed; + +// State file related values +static std::string sStateFileName; +static const std::string sKeyRecentlyUsed("RecentlyUsed"); +static const std::string sKeyFrequentlyUsed("FrequentlyUsed"); +} + +class LLEmojiGridRow : public LLScrollingPanel +{ +public: +    LLEmojiGridRow(const LLPanel::Params& panel_params, +        const LLScrollingPanelList::Params& list_params) +        : LLScrollingPanel(panel_params) +        , mList(new LLScrollingPanelList(list_params)) +    { +        addChild(mList); +    } + +    virtual void updatePanel(bool allow_modify) override {} + +public: +    LLScrollingPanelList* mList; +}; + +class LLEmojiGridDivider : public LLScrollingPanel +{ +public: +    LLEmojiGridDivider(const LLPanel::Params& panel_params, std::string text) +        : LLScrollingPanel(panel_params) +        , mText(utf8string_to_wstring(text)) +    { +    } + +    virtual void draw() override +    { +        LLScrollingPanel::draw(); + +        F32 x = 4; // padding-left +        F32 y = getRect().getHeight() / 2; +        LLFontGL::getFontSansSerif()->render( +            mText,                      // wstr +            0,                          // begin_offset +            x,                          // x +            y,                          // y +            LLColor4::white,            // color +            LLFontGL::LEFT,             // halign +            LLFontGL::VCENTER,          // valign +            LLFontGL::NORMAL,           // style +            LLFontGL::DROP_SHADOW_SOFT, // shadow +            mText.size());              // max_chars +    } + +    virtual void updatePanel(bool allow_modify) override {} + +private: +    const LLWString mText; +}; + +class LLEmojiGridIcon : public LLScrollingPanel +{ +public: +    LLEmojiGridIcon( +        const LLPanel::Params& panel_params +        , const LLEmojiSearchResult& emoji) +        : LLScrollingPanel(panel_params) +        , mData(emoji) +        , mText(LLWString(1, emoji.Character)) +    { +    } + +    virtual void draw() override +    { +        LLScrollingPanel::draw(); + +        F32 x = getRect().getWidth() / 2; +        F32 y = getRect().getHeight() / 2; +        LLFontGL::getFontEmoji()->render( +            mText,                      // wstr +            0,                          // begin_offset +            x,                          // x +            y,                          // y +            LLColor4::white,            // color +            LLFontGL::HCENTER,          // halign +            LLFontGL::VCENTER,          // valign +            LLFontGL::NORMAL,           // style +            LLFontGL::DROP_SHADOW_SOFT, // shadow +            1);                         // max_chars +    } + +    virtual void updatePanel(bool allow_modify) override {} + +    const LLEmojiSearchResult& getData() const { return mData; } +    LLWString getText() const { return mText; } + +private: +    const LLEmojiSearchResult mData; +    const LLWString mText; +}; + +class LLEmojiPreviewPanel : public LLPanel +{ +public: +    LLEmojiPreviewPanel() +        : LLPanel() +    { +    } + +    void setIcon(const LLEmojiGridIcon* icon) +    { +        if (icon) +        { +            setData(icon->getData().Character, icon->getData().String, icon->getData().Begin, icon->getData().End); +        } +        else +        { +            setData(0, LLStringUtil::null, 0, 0); +        } +    } + +    void setData(llwchar emoji, std::string title, size_t begin, size_t end) +    { +        mWStr = LLWString(1, emoji); +        mEmoji = emoji; +        mTitle = title; +        mBegin = begin; +        mEnd = end; +    } + +    virtual void draw() override +    { +        LLPanel::draw(); + +        S32 clientHeight = getRect().getHeight(); +        S32 clientWidth = getRect().getWidth(); +        S32 iconWidth = clientHeight; + +        F32 centerX = 0.5f * iconWidth; +        F32 centerY = 0.5f * clientHeight; +        drawIcon(centerX, centerY - 1, iconWidth); + +        static LLColor4 defaultColor(0.75f, 0.75f, 0.75f, 1.0f); +        LLColor4 textColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", defaultColor); +        S32 max_pixels = clientWidth - iconWidth; +        drawName(iconWidth, centerY, max_pixels, textColor); +    } + +protected: +    void drawIcon(F32 x, F32 y, S32 max_pixels) +    { +        LLFontGL::getFontEmojiHuge()->render( +            mWStr,                      // wstr +            0,                          // begin_offset +            x,                          // x +            y,                          // y +            LLColor4::white,            // color +            LLFontGL::HCENTER,          // halign +            LLFontGL::VCENTER,          // valign +            LLFontGL::NORMAL,           // style +            LLFontGL::DROP_SHADOW_SOFT, // shadow +            1,                          // max_chars +            max_pixels);                // max_pixels +    } + +    void drawName(F32 x, F32 y, S32 max_pixels, LLColor4& color) +    { +        F32 x0 = x; +        F32 x1 = max_pixels; +        LLFontGL* font = LLFontGL::getFontEmoji(); +        if (mBegin) +        { +            std::string text = mTitle.substr(0, mBegin); +            font->renderUTF8( +                text,                       // text +                0,                          // begin_offset +                x0,                         // x +                y,                          // y +                color,                      // color +                LLFontGL::LEFT,             // halign +                LLFontGL::VCENTER,          // valign +                LLFontGL::NORMAL,           // style +                LLFontGL::DROP_SHADOW_SOFT, // shadow +                text.size(),                // max_chars +                x1);                        // max_pixels +            F32 dx = font->getWidthF32(text); +            x0 += dx; +            x1 -= dx; +        } +        if (x1 > 0 && mEnd > mBegin) +        { +            std::string text = mTitle.substr(mBegin, mEnd - mBegin); +            font->renderUTF8( +                text,                       // text +                0,                          // begin_offset +                x0,                         // x +                y,                          // y +                LLColor4::yellow6,          // color +                LLFontGL::LEFT,             // halign +                LLFontGL::VCENTER,          // valign +                LLFontGL::NORMAL,           // style +                LLFontGL::DROP_SHADOW_SOFT, // shadow +                text.size(),                // max_chars +                x1);                        // max_pixels +            F32 dx = font->getWidthF32(text); +            x0 += dx; +            x1 -= dx; +        } +        if (x1 > 0 && mEnd < mTitle.size()) +        { +            std::string text = mEnd ? mTitle.substr(mEnd) : mTitle; +            font->renderUTF8( +                text,                       // text +                0,                          // begin_offset +                x0,                         // x +                y,                          // y +                color,                      // color +                LLFontGL::LEFT,             // halign +                LLFontGL::VCENTER,          // valign +                LLFontGL::NORMAL,           // style +                LLFontGL::DROP_SHADOW_SOFT, // shadow +                text.size(),                // max_chars +                x1);                        // max_pixels +        } +    } + +private: +    llwchar mEmoji; +    LLWString mWStr; +    std::string mTitle; +    size_t mBegin; +    size_t mEnd; +}; + +LLFloaterEmojiPicker::LLFloaterEmojiPicker(const LLSD& key) +: super(key) +{ +    // This floater should hover on top of our dependent (with the dependent having the focus) +    setFocusStealsFrontmost(false); +    setBackgroundVisible(false); +    setAutoFocus(false); + +    loadState(); +} + +bool LLFloaterEmojiPicker::postBuild() +{ +    mGroups = getChild<LLPanel>("Groups"); +    mBadge = getChild<LLPanel>("Badge"); +    mEmojiScroll = getChild<LLScrollContainer>("EmojiGridContainer"); +    mEmojiGrid = getChild<LLScrollingPanelList>("EmojiGrid"); +    mDummy = getChild<LLTextBox>("Dummy"); + +    mPreview = new LLEmojiPreviewPanel(); +    mPreview->setVisible(false); +    addChild(mPreview); + +    return LLFloater::postBuild(); +} + +void LLFloaterEmojiPicker::onOpen(const LLSD& key) +{ +    mHint = key["hint"].asString(); + +    LLEmojiHelper::instance().setIsHideDisabled(mHint.empty()); +    mFilterPattern = mHint; + +    initialize(); + +    gFloaterView->adjustToFitScreen(this, false); +} + +void LLFloaterEmojiPicker::dirtyRect() +{ +    super::dirtyRect(); + +    if (!mPreview) +        return; + +    const S32 HPADDING = 4; +    const S32 VOFFSET = 12; +    LLRect rect(HPADDING, mDummy->getRect().mTop + 6, getRect().getWidth() - HPADDING, VOFFSET); +    if (mPreview->getRect() != rect) +    { +        mPreview->setRect(rect); +    } + +    if (mEmojiScroll && mEmojiGrid) +    { +        S32 outer_width = mEmojiScroll->getRect().getWidth(); +        S32 inner_width = mEmojiGrid->getRect().getWidth(); +        if (outer_width != inner_width) +        { +            resizeGroupButtons(); +            fillEmojis(true); +        } +    } +} + +void LLFloaterEmojiPicker::initialize() +{ +    S32 groupIndex = mSelectedGroupIndex && mSelectedGroupIndex <= mFilteredEmojiGroups.size() ? +        mFilteredEmojiGroups[mSelectedGroupIndex - 1] : ALL_EMOJIS_GROUP_INDEX; + +    fillGroups(); + +    if (mFilteredEmojis.empty()) +    { +        if (!mHint.empty()) +        { +            hideFloater(); +            return; +        } + +        mGroups->setVisible(false); +        mFocusedIconRow = -1; +        mFocusedIconCol = -1; +        mFocusedIcon = nullptr; +        mHoveredIcon = nullptr; +        mEmojiScroll->goToTop(); +        mEmojiGrid->clearPanels(); + +        if (mFilterPattern.empty()) +        { +            showPreview(false); +        } +        else +        { +            const std::string prompt("No emoji found for "); +            std::string title(prompt + '"' + mFilterPattern.substr(1) + '"'); +            mPreview->setData(EMPTY_LIST_IMAGE_INDEX, title, prompt.size() + 1, title.size() - 1); +            showPreview(true); +        } +        return; +    } + +    mGroups->setVisible(true); +    mPreview->setIcon(nullptr); +    showPreview(true); + +    mSelectedGroupIndex = groupIndex == ALL_EMOJIS_GROUP_INDEX ? 0 : +        (1 + std::distance(mFilteredEmojiGroups.begin(), +            std::find(mFilteredEmojiGroups.begin(), mFilteredEmojiGroups.end(), groupIndex))) % +        (1 + mFilteredEmojiGroups.size()); + +    mGroupButtons[mSelectedGroupIndex]->setToggleState(true); +    mGroupButtons[mSelectedGroupIndex]->setUseFontColor(true); + +    fillEmojis(); +} + +void LLFloaterEmojiPicker::fillGroups() +{ +    // Do not use deleteAllChildren() because mBadge shouldn't be removed +    for (LLButton* button : mGroupButtons) +    { +        mGroups->removeChild(button); +    } +    mFilteredEmojiGroups.clear(); +    mFilteredEmojis.clear(); +    mGroupButtons.clear(); + +    LLButton::Params params; +    params.font = LLFontGL::getFontEmoji(); + +    LLRect rect; +    rect.mTop = mGroups->getRect().getHeight(); +    rect.mBottom = mBadge->getRect().getHeight(); + +    // Create button for "All categories" +    createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX); + +    // Create group and button for "Recently used" and/or "Frequently used" +    if (!sRecentlyUsed.empty() || !sFrequentlyUsed.empty()) +    { +        std::map<std::string, std::vector<LLEmojiSearchResult>> cats; +        fillCategoryRecentlyUsed(cats); +        fillCategoryFrequentlyUsed(cats); + +        if (!cats.empty()) +        { +            mFilteredEmojiGroups.push_back(USED_EMOJIS_GROUP_INDEX); +            mFilteredEmojis.emplace_back(cats); +            createGroupButton(params, rect, USED_EMOJIS_IMAGE_INDEX); +        } +    } + +    const std::vector<LLEmojiGroup>& groups = LLEmojiDictionary::instance().getGroups(); + +    // List all categories in the dictionary +    for (U32 i = 0; i < groups.size(); ++i) +    { +        std::map<std::string, std::vector<LLEmojiSearchResult>> cats; + +        fillGroupEmojis(cats, i); + +        if (!cats.empty()) +        { +            mFilteredEmojiGroups.push_back(i); +            mFilteredEmojis.emplace_back(cats); +            createGroupButton(params, rect, groups[i].Character); +        } +    } + +    resizeGroupButtons(); +} + +void LLFloaterEmojiPicker::fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats) +{ +    if (sRecentlyUsed.empty()) +        return; + +    std::vector<LLEmojiSearchResult> emojis; + +    // In case of empty mFilterPattern we'd use sRecentlyUsed directly +    if (!mFilterPattern.empty()) +    { +        // List all emojis in "Recently used" +        const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); +        std::size_t begin, end; +        for (llwchar emoji : sRecentlyUsed) +        { +            auto e2d = emoji2descr.find(emoji); +            if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) +            { +                const std::string shortcode(e2d->second->ShortCodes.front()); +                if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern)) +                { +                    emojis.emplace_back(emoji, shortcode, begin, end); +                } +            } +        } +        if (emojis.empty()) +            return; +    } + +    cats.emplace(std::make_pair(RECENTLY_USED_CATEGORY, emojis)); +} + +void LLFloaterEmojiPicker::fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats) +{ +    if (sFrequentlyUsed.empty()) +        return; + +    std::vector<LLEmojiSearchResult> emojis; + +    // In case of empty mFilterPattern we'd use sFrequentlyUsed directly +    if (!mFilterPattern.empty()) +    { +        // List all emojis in "Frequently used" +        const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); +        std::size_t begin, end; +        for (const auto& emoji : sFrequentlyUsed) +        { +            auto e2d = emoji2descr.find(emoji.first); +            if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) +            { +                const std::string shortcode(e2d->second->ShortCodes.front()); +                if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern)) +                { +                    emojis.emplace_back(emoji.first, shortcode, begin, end); +                } +            } +        } +        if (emojis.empty()) +            return; +    } + +    cats.emplace(std::make_pair(FREQUENTLY_USED_CATEGORY, emojis)); +} + +void LLFloaterEmojiPicker::fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index) +{ +    const std::vector<LLEmojiGroup>& groups = LLEmojiDictionary::instance().getGroups(); +    const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs(); + +    for (const std::string& category : groups[index].Categories) +    { +        const LLEmojiDictionary::cat2descrs_map_t::const_iterator& c2d = category2Descr.find(category); +        if (c2d == category2Descr.end()) +            continue; + +        std::vector<LLEmojiSearchResult> emojis; + +        // In case of empty mFilterPattern we'd use category2Descr directly +        if (!mFilterPattern.empty()) +        { +            // List all emojis in category +            std::size_t begin, end; +            for (const LLEmojiDescriptor* descr : c2d->second) +            { +                if (!descr->ShortCodes.empty()) +                { +                    const std::string shortcode(descr->ShortCodes.front()); +                    if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern)) +                    { +                        emojis.emplace_back(descr->Character, shortcode, begin, end); +                    } +                } +            } +            if (emojis.empty()) +                continue; +        } + +        cats.emplace(std::make_pair(category, emojis)); +    } +} + +void LLFloaterEmojiPicker::createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji) +{ +    LLButton* button = LLUICtrlFactory::create<LLButton>(params); +    button->setClickedCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonClick(ctrl); }); +    button->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseEnter(ctrl); }); +    button->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseLeave(ctrl); }); + +    button->setRect(rect); +    button->setTabStop(false); +    button->setLabel(LLUIString(LLWString(1, emoji))); +    button->setUseFontColor(false); + +    mGroupButtons.push_back(button); +    mGroups->addChild(button); +} + +void LLFloaterEmojiPicker::resizeGroupButtons() +{ +    U32 groupCount = (U32)mGroupButtons.size(); +    if (!groupCount) +        return; + +    S32 totalWidth = mGroups->getRect().getWidth(); +    S32 badgeWidth = totalWidth / groupCount; +    S32 leftOffset = (totalWidth - badgeWidth * groupCount) / 2; + +    for (U32 i = 0; i < groupCount; ++i) +    { +        LLRect rect = mGroupButtons[i]->getRect(); +        rect.mLeft = leftOffset + badgeWidth * i; +        rect.mRight = rect.mLeft + badgeWidth; +        mGroupButtons[i]->setRect(rect); +    } + +    LLRect rect = mBadge->getRect(); +    rect.mLeft = leftOffset + badgeWidth * mSelectedGroupIndex; +    rect.mRight = rect.mLeft + badgeWidth; +    mBadge->setRect(rect); +} + +void LLFloaterEmojiPicker::selectEmojiGroup(U32 index) +{ +    if (index == mSelectedGroupIndex || index >= mGroupButtons.size()) +        return; + +    if (mSelectedGroupIndex < mGroupButtons.size()) +    { +        mGroupButtons[mSelectedGroupIndex]->setUseFontColor(false); +        mGroupButtons[mSelectedGroupIndex]->setToggleState(false); +    } + +    mSelectedGroupIndex = index; +    mGroupButtons[mSelectedGroupIndex]->setToggleState(true); +    mGroupButtons[mSelectedGroupIndex]->setUseFontColor(true); + +    LLButton* button = mGroupButtons[mSelectedGroupIndex]; +    LLRect rect = mBadge->getRect(); +    rect.mLeft = button->getRect().mLeft; +    rect.mRight = button->getRect().mRight; +    mBadge->setRect(rect); + +    fillEmojis(); +} + +void LLFloaterEmojiPicker::fillEmojis(bool fromResize) +{ +    S32 scrollbar_size = mEmojiScroll->getSize(); +    if (scrollbar_size < 0) +    { +        static LLUICachedControl<S32> scrollbar_size_control("UIScrollbarSize", 0); +        scrollbar_size = scrollbar_size_control; +    } + +    const S32 scroll_width = mEmojiScroll->getRect().getWidth(); +    const S32 client_width = scroll_width - scrollbar_size - mEmojiScroll->getBorderWidth() * 2; +    const S32 grid_padding = mEmojiGrid->getPadding(); +    const S32 icon_spacing = mEmojiGrid->getSpacing(); +    const S32 row_width = client_width - grid_padding * 2; +    const S32 icon_size = 28; // icon width and height +    const S32 max_icons = llmax(1, (row_width + icon_spacing) / (icon_size + icon_spacing)); + +    // Optimization: don't rearrange for different widths with the same maxIcons +    if (fromResize && (max_icons == mRecentMaxIcons)) +        return; + +    mRecentMaxIcons = max_icons; + +    mFocusedIconRow = 0; +    mFocusedIconCol = 0; +    mFocusedIcon = nullptr; +    mHoveredIcon = nullptr; +    mEmojiScroll->goToTop(); +    mEmojiGrid->clearPanels(); +    mPreview->setIcon(nullptr); + +    if (mEmojiGrid->getRect().getWidth() != client_width) +    { +        LLRect rect = mEmojiGrid->getRect(); +        rect.mRight = rect.mLeft + client_width; +        mEmojiGrid->setRect(rect); +    } + +    LLPanel::Params row_panel_params; +    row_panel_params.rect = LLRect(0, icon_size, row_width, 0); + +    LLScrollingPanelList::Params row_list_params; +    row_list_params.rect = row_panel_params.rect; +    row_list_params.is_horizontal = true; +    row_list_params.padding = 0; +    row_list_params.spacing = icon_spacing; + +    LLPanel::Params icon_params; +    LLRect icon_rect(0, icon_size, icon_size, 0); + +    static LLColor4 default_color(0.75f, 0.75f, 0.75f, 1.0f); +    LLColor4 bg_color = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", default_color); + +    if (!mSelectedGroupIndex) +    { +        // List all groups +        for (const auto& group : mFilteredEmojis) +        { +            // List all categories in the group +            for (const auto& category : group) +            { +                // List all emojis in the category +                fillEmojisCategory(category.second, category.first, row_panel_params, +                    row_list_params, icon_params, icon_rect, max_icons, bg_color); +            } +        } +    } +    else +    { +        // List all categories in the selected group +        const auto& group = mFilteredEmojis[mSelectedGroupIndex - 1]; +        for (const auto& category : group) +        { +            // List all emojis in the category +            fillEmojisCategory(category.second, category.first, row_panel_params, +                row_list_params, icon_params, icon_rect, max_icons, bg_color); +        } +    } + +    if (mEmojiGrid->getPanelList().empty()) +    { +        showPreview(false); +        mFocusedIconRow = -1; +        mFocusedIconCol = -1; +        if (!mHint.empty()) +        { +            hideFloater(); +        } +    } +    else +    { +        showPreview(true); +        mFocusedIconRow = 0; +        mFocusedIconCol = 0; +        moveFocusedIconNext(); +    } +} + +void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector<LLEmojiSearchResult>& emojis, +    const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params, +    const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg) +{ +    // Place the category title +    std::string title = +        category == RECENTLY_USED_CATEGORY ? getString("title_for_recently_used") : +        category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") : +        isupper(category.front()) ? category : LLStringUtil::capitalize(category); +    LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, title); +    mEmojiGrid->addPanel(div, true); + +    int icon_index = 0; +    LLEmojiGridRow* row = nullptr; + +    if (mFilterPattern.empty()) +    { +        const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); +        LLEmojiSearchResult emoji { 0, "", 0, 0 }; +        if (category == RECENTLY_USED_CATEGORY) +        { +            for (llwchar code : sRecentlyUsed) +            { +                const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code); +                if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) +                { +                    emoji.Character = code; +                    emoji.String = e2d->second->ShortCodes.front(); +                    createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params, +                        icon_rect, max_icons, bg, row, icon_index); +                } +            } +        } +        else if (category == FREQUENTLY_USED_CATEGORY) +        { +            for (const auto& code : sFrequentlyUsed) +            { +                const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code.first); +                if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty()) +                { +                    emoji.Character = code.first; +                    emoji.String = e2d->second->ShortCodes.front(); +                    createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params, +                        icon_rect, max_icons, bg, row, icon_index); +                } +            } +        } +        else +        { +            const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs(); +            const LLEmojiDictionary::cat2descrs_map_t::const_iterator& c2d = category2Descr.find(category); +            if (c2d != category2Descr.end()) +            { +                for (const LLEmojiDescriptor* descr : c2d->second) +                { +                    emoji.Character = descr->Character; +                    emoji.String = descr->ShortCodes.front(); +                    createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params, +                        icon_rect, max_icons, bg, row, icon_index); +                } +            } +        } +    } +    else +    { +        for (const LLEmojiSearchResult& emoji : emojis) +        { +            createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params, +                icon_rect, max_icons, bg, row, icon_index); +        } +    } +} + +void LLFloaterEmojiPicker::createEmojiIcon(const LLEmojiSearchResult& emoji, +    const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params, +    const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg, +    LLEmojiGridRow*& row, int& icon_index) +{ +    // Place a new row each (max_icons) icons +    if (!(icon_index % max_icons)) +    { +        row = new LLEmojiGridRow(row_panel_params, *(const LLScrollingPanelList::Params*)&row_list_params); +        mEmojiGrid->addPanel(row, true); +    } + +    // Place a new icon to the current row +    LLEmojiGridIcon* icon = new LLEmojiGridIcon(icon_params, emoji); +    icon->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseEnter(ctrl); }); +    icon->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseLeave(ctrl); }); +    icon->setMouseDownCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseDown(ctrl); }); +    icon->setMouseUpCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseUp(ctrl); }); +    icon->setBackgroundColor(bg); +    icon->setBackgroundOpaque(1); +    icon->setRect(icon_rect); +    row->mList->addPanel(icon, true); + +    icon_index++; +} + +void LLFloaterEmojiPicker::showPreview(bool show) +{ +    //mPreview->setIcon(nullptr); +    mDummy->setVisible(show ? false : true); +    mPreview->setVisible(show ? true : false); +} + +void LLFloaterEmojiPicker::onGroupButtonClick(LLUICtrl* ctrl) +{ +    if (LLButton* button = dynamic_cast<LLButton*>(ctrl)) +    { +        if (button == mGroupButtons[mSelectedGroupIndex] || button->getToggleState()) +            return; + +        auto it = std::find(mGroupButtons.begin(), mGroupButtons.end(), button); +        if (it == mGroupButtons.end()) +            return; + +        selectEmojiGroup(it - mGroupButtons.begin()); +    } +} + +void LLFloaterEmojiPicker::onGroupButtonMouseEnter(LLUICtrl* ctrl) +{ +    if (LLButton* button = dynamic_cast<LLButton*>(ctrl)) +    { +        button->setUseFontColor(true); +    } +} + +void LLFloaterEmojiPicker::onGroupButtonMouseLeave(LLUICtrl* ctrl) +{ +    if (LLButton* button = dynamic_cast<LLButton*>(ctrl)) +    { +        button->setUseFontColor(button->getToggleState()); +    } +} + +void LLFloaterEmojiPicker::onEmojiMouseEnter(LLUICtrl* ctrl) +{ +    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) +    { +        if (mFocusedIcon && mFocusedIcon != icon && mFocusedIcon->isBackgroundVisible()) +        { +            unselectGridIcon(mFocusedIcon); +        } + +        if (mHoveredIcon && mHoveredIcon != icon) +        { +            unselectGridIcon(mHoveredIcon); +        } + +        selectGridIcon(icon); + +        mHoveredIcon = icon; +    } +} + +void LLFloaterEmojiPicker::onEmojiMouseLeave(LLUICtrl* ctrl) +{ +    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) +    { +        if (icon == mHoveredIcon) +        { +            if (icon != mFocusedIcon) +            { +                unselectGridIcon(icon); +            } +            mHoveredIcon = nullptr; +        } + +        if (!mHoveredIcon && mFocusedIcon && !mFocusedIcon->isBackgroundVisible()) +        { +            selectGridIcon(mFocusedIcon); +        } +    } +} + +void LLFloaterEmojiPicker::onEmojiMouseDown(LLUICtrl* ctrl) +{ +    if (getSoundFlags() & MOUSE_DOWN) +    { +        make_ui_sound("UISndClick"); +    } +} + +void LLFloaterEmojiPicker::onEmojiMouseUp(LLUICtrl* ctrl) +{ +    if (getSoundFlags() & MOUSE_UP) +    { +        make_ui_sound("UISndClickRelease"); +    } + +    if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) +    { +        LLSD value(wstring_to_utf8str(icon->getText())); +        setValue(value); + +        onCommit(); + +        if (!mHint.empty() || !(gKeyboard->currentMask(true) & MASK_SHIFT)) +        { +            hideFloater(); +        } +    } +} + +void LLFloaterEmojiPicker::selectFocusedIcon() +{ +    if (mFocusedIcon && mFocusedIcon != mHoveredIcon) +    { +        unselectGridIcon(mFocusedIcon); +    } + +    // Both mFocusedIconRow and mFocusedIconCol should be already verified +    LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(mEmojiGrid->getPanelList()[mFocusedIconRow]); +    mFocusedIcon = row ? dynamic_cast<LLEmojiGridIcon*>(row->mList->getPanelList()[mFocusedIconCol]) : nullptr; + +    if (mFocusedIcon && !mHoveredIcon) +    { +        selectGridIcon(mFocusedIcon); +    } +} + +bool LLFloaterEmojiPicker::moveFocusedIconUp() +{ +    for (S32 i = mFocusedIconRow - 1; i >= 0; --i) +    { +        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i]; +        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel); +        if (row && row->mList->getPanelList().size() > mFocusedIconCol) +        { +            mEmojiScroll->scrollToShowRect(row->getBoundingRect()); +            mFocusedIconRow = i; +            selectFocusedIcon(); +            return true; +        } +    } + +    return false; +} + +bool LLFloaterEmojiPicker::moveFocusedIconDown() +{ +    S32 rowCount = mEmojiGrid->getPanelList().size(); +    for (S32 i = mFocusedIconRow + 1; i < rowCount; ++i) +    { +        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i]; +        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel); +        if (row && row->mList->getPanelList().size() > mFocusedIconCol) +        { +            mEmojiScroll->scrollToShowRect(row->getBoundingRect()); +            mFocusedIconRow = i; +            selectFocusedIcon(); +            return true; +        } +    } + +    return false; +} + +bool LLFloaterEmojiPicker::moveFocusedIconPrev() +{ +    if (mHoveredIcon) +        return false; + +    if (mFocusedIconCol > 0) +    { +        mFocusedIconCol--; +        selectFocusedIcon(); +        return true; +    } + +    for (S32 i = mFocusedIconRow - 1; i >= 0; --i) +    { +        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i]; +        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel); +        if (row && row->mList->getPanelList().size()) +        { +            mEmojiScroll->scrollToShowRect(row->getBoundingRect()); +            mFocusedIconCol = row->mList->getPanelList().size() - 1; +            mFocusedIconRow = i; +            selectFocusedIcon(); +            return true; +        } +    } + +    return false; +} + +bool LLFloaterEmojiPicker::moveFocusedIconNext() +{ +    if (mHoveredIcon) +        return false; + +    LLScrollingPanel* panel = mEmojiGrid->getPanelList()[mFocusedIconRow]; +    LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel); +    S32 colCount = row ? row->mList->getPanelList().size() : 0; +    if (mFocusedIconCol < colCount - 1) +    { +        mFocusedIconCol++; +        selectFocusedIcon(); +        return true; +    } + +    S32 rowCount = mEmojiGrid->getPanelList().size(); +    for (S32 i = mFocusedIconRow + 1; i < rowCount; ++i) +    { +        LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i]; +        LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel); +        if (row && row->mList->getPanelList().size()) +        { +            mEmojiScroll->scrollToShowRect(row->getBoundingRect()); +            mFocusedIconCol = 0; +            mFocusedIconRow = i; +            selectFocusedIcon(); +            return true; +        } +    } + +    return false; +} + +void LLFloaterEmojiPicker::selectGridIcon(LLEmojiGridIcon* icon) +{ +    icon->setBackgroundVisible(true); +    mPreview->setIcon(icon); +} + +void LLFloaterEmojiPicker::unselectGridIcon(LLEmojiGridIcon* icon) +{ +    icon->setBackgroundVisible(false); +    mPreview->setIcon(nullptr); +} + +// virtual +bool LLFloaterEmojiPicker::handleKey(KEY key, MASK mask, bool called_from_parent) +{ +    if (mask == MASK_NONE) +    { +        switch (key) +        { +        case KEY_UP: +            moveFocusedIconUp(); +            return true; +        case KEY_DOWN: +            moveFocusedIconDown(); +            return true; +        case KEY_LEFT: +            moveFocusedIconPrev(); +            return true; +        case KEY_RIGHT: +            moveFocusedIconNext(); +            return true; +        case KEY_ESCAPE: +            hideFloater(); +            return true; +        } +    } + +    if (mask == MASK_ALT) +    { +        switch (key) +        { +        case KEY_LEFT: +            selectEmojiGroup((mSelectedGroupIndex + mFilteredEmojis.size()) % mGroupButtons.size()); +            return true; +        case KEY_RIGHT: +            selectEmojiGroup((mSelectedGroupIndex + 1) % mGroupButtons.size()); +            return true; +        } +    } + +    if (key == KEY_RETURN) +    { +        U64 time = totalTime(); +        // <Shift+Return> comes twice for unknown reason +        if (mFocusedIcon && (time - mRecentReturnPressedMs > 100000)) // Min interval 0.1 sec. +        { +            onEmojiMouseDown(mFocusedIcon); +            onEmojiMouseUp(mFocusedIcon); +        } +        mRecentReturnPressedMs = time; +        return true; +    } + +    if (mHint.empty()) +    { +        if (key >= 0x20 && key < 0x80) +        { +            if (!mEmojiGrid->getPanelList().empty()) +            { +                if (mFilterPattern.empty()) +                { +                    mFilterPattern = ":"; +                } +                mFilterPattern += (char)key; +                initialize(); +            } +            return true; +        } +        else if (key == KEY_BACKSPACE) +        { +            if (!mFilterPattern.empty()) +            { +                mFilterPattern.pop_back(); +                if (mFilterPattern == ":") +                { +                    mFilterPattern.clear(); +                } +                initialize(); +            } +            return true; +        } +    } + +    return super::handleKey(key, mask, called_from_parent); +} + +// virtual +void LLFloaterEmojiPicker::goneFromFront() +{ +    hideFloater(); +} + +void LLFloaterEmojiPicker::hideFloater() const +{ +    LLEmojiHelper::instance().hideHelper(nullptr, true); +} + +// static +std::list<llwchar>& LLFloaterEmojiPicker::getRecentlyUsed() +{ +    loadState(); +    return sRecentlyUsed; +} + +// static +void LLFloaterEmojiPicker::onEmojiUsed(llwchar emoji) +{ +    // Update sRecentlyUsed +    auto itr = std::find(sRecentlyUsed.begin(), sRecentlyUsed.end(), emoji); +    if (itr == sRecentlyUsed.end()) +    { +        sRecentlyUsed.push_front(emoji); +    } +    else if (itr != sRecentlyUsed.begin()) +    { +        sRecentlyUsed.erase(itr); +        sRecentlyUsed.push_front(emoji); +    } + +    // Increment and reorder sFrequentlyUsed +    auto itf = sFrequentlyUsed.begin(); +    while (itf != sFrequentlyUsed.end()) +    { +        if (itf->first == emoji) +        { +            itf->second++; +            while (itf != sFrequentlyUsed.begin()) +            { +                auto prior = itf; +                prior--; +                if (prior->second > itf->second) +                    break; +                prior->swap(*itf); +                itf = prior; +            } +            break; +        } +        itf++; +    } +    // Append new if not found +    if (itf == sFrequentlyUsed.end()) +    { +        // Insert before others with count == 1 +        while (itf != sFrequentlyUsed.begin()) +        { +            auto prior = itf; +            prior--; +            if (prior->second > 1) +                break; +            itf = prior; +        } +        sFrequentlyUsed.insert(itf, std::make_pair(emoji, 1)); +    } +} + +// static +void LLFloaterEmojiPicker::loadState() +{ +    if (!sStateFileName.empty()) +        return; // Already loaded + +    sStateFileName = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "emoji_floater_state.xml"); + +    llifstream file; +    file.open(sStateFileName.c_str()); +    if (!file.is_open()) +    { +        LL_WARNS() << "Emoji floater state file is missing or inaccessible: " << sStateFileName << LL_ENDL; +        return; +    } + +    LLSD state; +    LLSDSerialize::fromXML(state, file); +    if (state.isUndefined()) +    { +        LL_WARNS() << "Emoji floater state file is missing or ill-formed: " << sStateFileName << LL_ENDL; +        return; +    } + +    // Load and parse sRecentlyUsed +    std::string recentlyUsed = state[sKeyRecentlyUsed]; +    std::vector<std::string> rtokens = LLStringUtil::getTokens(recentlyUsed, ","); +    int maxCountR = 20; +    for (const std::string& token : rtokens) +    { +        llwchar emoji = (llwchar)atoi(token.c_str()); +        if (std::find(sRecentlyUsed.begin(), sRecentlyUsed.end(), emoji) == sRecentlyUsed.end()) +        { +            sRecentlyUsed.push_back(emoji); +            if (!--maxCountR) +                break; +        } +    } + +    // Load and parse sFrequentlyUsed +    std::string frequentlyUsed = state[sKeyFrequentlyUsed]; +    std::vector<std::string> ftokens = LLStringUtil::getTokens(frequentlyUsed, ","); +    int maxCountF = 20; +    for (const std::string& token : ftokens) +    { +        std::vector<std::string> pair = LLStringUtil::getTokens(token, ":"); +        if (pair.size() == 2) +        { +            llwchar emoji = (llwchar)atoi(pair[0].c_str()); +            if (emoji) +            { +                U32 count = atoi(pair[1].c_str()); +                auto it = std::find_if(sFrequentlyUsed.begin(), sFrequentlyUsed.end(), +                    [emoji](std::pair<llwchar, U32>& it) { return it.first == emoji; }); +                if (it != sFrequentlyUsed.end()) +                { +                    it->second += count; +                } +                else +                { +                    sFrequentlyUsed.push_back(std::make_pair(emoji, count)); +                    if (!--maxCountF) +                        break; +                } +            } +        } +    } + +    // Normalize by minimum +    if (!sFrequentlyUsed.empty()) +    { +        U32 delta = sFrequentlyUsed.back().second - 1; +        for (auto& it : sFrequentlyUsed) +        { +            it.second = std::max((U32)0, it.second - delta); +        } +    } +} + +// static +void LLFloaterEmojiPicker::saveState() +{ +    if (sStateFileName.empty()) +        return; // Not loaded + +    if (LLAppViewer::instance()->isSecondInstance()) +        return; // Not allowed + +    LLSD state = LLSD::emptyMap(); + +    if (!sRecentlyUsed.empty()) +    { +        U32 maxCount = 20; +        std::string recentlyUsed; +        for (llwchar emoji : sRecentlyUsed) +        { +            if (!recentlyUsed.empty()) +                recentlyUsed += ","; +            char buffer[32]; +            sprintf(buffer, "%u", (U32)emoji); +            recentlyUsed += buffer; +            if (!--maxCount) +                break; +        } +        state[sKeyRecentlyUsed] = recentlyUsed; +    } + +    if (!sFrequentlyUsed.empty()) +    { +        U32 maxCount = 20; +        std::string frequentlyUsed; +        for (auto& it : sFrequentlyUsed) +        { +            if (!frequentlyUsed.empty()) +                frequentlyUsed += ","; +            char buffer[32]; +            sprintf(buffer, "%u:%u", (U32)it.first, (U32)it.second); +            frequentlyUsed += buffer; +            if (!--maxCount) +                break; +        } +        state[sKeyFrequentlyUsed] = frequentlyUsed; +    } + +    llofstream stream(sStateFileName.c_str()); +    LLSDSerialize::toPrettyXML(state, stream); +} diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h new file mode 100644 index 0000000000..c35616e054 --- /dev/null +++ b/indra/newview/llfloateremojipicker.h @@ -0,0 +1,122 @@ +/** + * @file llfloateremojipicker.h + * @brief Header file for llfloateremojipicker + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 LLFLOATEREMOJIPICKER_H +#define LLFLOATEREMOJIPICKER_H + +#include "llfloater.h" + +class LLEmojiGridRow; +class LLEmojiGridIcon; +struct LLEmojiDescriptor; +struct LLEmojiSearchResult; + +class LLFloaterEmojiPicker : public LLFloater +{ +    using super = LLFloater; + +public: +    // The callback function will be called with an emoji char. +    typedef boost::function<void (llwchar)> pick_callback_t; +    typedef boost::function<void ()> close_callback_t; + +    LLFloaterEmojiPicker(const LLSD& key); + +    virtual	bool postBuild() override; +    virtual void dirtyRect() override; +    virtual void goneFromFront() override; + +    void hideFloater() const; + +    static std::list<llwchar>& getRecentlyUsed(); +    static void onEmojiUsed(llwchar emoji); + +    static void loadState(); +    static void saveState(); + +private: +    void initialize(); +    void fillGroups(); +    void fillCategoryRecentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats); +    void fillCategoryFrequentlyUsed(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats); +    void fillGroupEmojis(std::map<std::string, std::vector<LLEmojiSearchResult>>& cats, U32 index); +    void createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji); +    void resizeGroupButtons(); +    void selectEmojiGroup(U32 index); +    void fillEmojis(bool fromResize = false); +    void fillEmojisCategory(const std::vector<LLEmojiSearchResult>& emojis, +        const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params, +        const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg); +    void createEmojiIcon(const LLEmojiSearchResult& emoji, +        const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params, +        const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg, +        LLEmojiGridRow*& row, int& icon_index); +    void showPreview(bool show); + +    void onGroupButtonClick(LLUICtrl* ctrl); +    void onGroupButtonMouseEnter(LLUICtrl* ctrl); +    void onGroupButtonMouseLeave(LLUICtrl* ctrl); +    void onEmojiMouseEnter(LLUICtrl* ctrl); +    void onEmojiMouseLeave(LLUICtrl* ctrl); +    void onEmojiMouseDown(LLUICtrl* ctrl); +    void onEmojiMouseUp(LLUICtrl* ctrl); + +    void selectFocusedIcon(); +    bool moveFocusedIconUp(); +    bool moveFocusedIconDown(); +    bool moveFocusedIconPrev(); +    bool moveFocusedIconNext(); + +    void selectGridIcon(LLEmojiGridIcon* icon); +    void unselectGridIcon(LLEmojiGridIcon* icon); + +    void onOpen(const LLSD& key) override; +    virtual bool handleKey(KEY key, MASK mask, bool called_from_parent) override; + +    class LLPanel* mGroups { nullptr }; +    class LLPanel* mBadge { nullptr }; +    class LLScrollContainer* mEmojiScroll { nullptr }; +    class LLScrollingPanelList* mEmojiGrid { nullptr }; +    class LLEmojiPreviewPanel* mPreview { nullptr }; +    class LLTextBox* mDummy { nullptr }; + +    std::vector<S32> mFilteredEmojiGroups; +    std::vector<std::map<std::string, std::vector<LLEmojiSearchResult>>> mFilteredEmojis; +    std::vector<class LLButton*> mGroupButtons; + +    std::string mHint; +    std::string mFilterPattern; +    U32 mSelectedGroupIndex { 0 }; +    S32 mRecentMaxIcons { 0 }; +    S32 mFocusedIconRow { 0 }; +    S32 mFocusedIconCol { 0 }; +    LLEmojiGridIcon* mFocusedIcon { nullptr }; +    LLEmojiGridIcon* mHoveredIcon { nullptr }; + +    U64 mRecentReturnPressedMs { 0 }; +}; + +#endif diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 3bd45f083d..341e938765 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -130,11 +130,12 @@ bool LLFloaterIMNearbyChat::postBuild()  	mInputEditor->setKeystrokeCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxKeystroke, this));  	mInputEditor->setFocusLostCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusLost, this));  	mInputEditor->setFocusReceivedCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusReceived, this)); -	mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle")); +	std::string nearbyChatTitle(LLTrans::getString("NearbyChatTitle")); +	mInputEditor->setLabel(nearbyChatTitle);  	// Title must be defined BEFORE call to addConversationListItem() because  	// it is used to show the item's name in the conversations list -	setTitle(LLTrans::getString("NearbyChatTitle")); +	setTitle(nearbyChatTitle);  	// obsolete, but may be needed for backward compatibility?  	gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", LLControlVariable::PERSIST_NONDFT); @@ -590,6 +591,8 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type )  			S32 channel = 0;  			stripChannelNumber(text, &channel); +			updateUsedEmojis(text); +  			std::string utf8text = wstring_to_utf8str(text);  			// Try to trigger a gesture, if not chat to a script.  			std::string utf8_revised_text; diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index 62bb1d77a6..af35d5fd17 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -249,6 +249,8 @@ void LLFloaterIMSession::sendMsgFromInputEditor()  			LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.  			if(!text.empty())  			{ +				updateUsedEmojis(text); +  				// Truncate and convert to UTF8 for transport  				std::string utf8_text = wstring_to_utf8str(text); diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index fd2d3095b3..9b1fc96706 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -33,18 +33,21 @@  #include "llagentcamera.h"  #include "llavataractions.h"  #include "llavatariconctrl.h" -#include "llgroupiconctrl.h"  #include "llchatentry.h"  #include "llchathistory.h"  #include "llchiclet.h"  #include "llchicletbar.h"  #include "lldraghandle.h" +#include "llemojidictionary.h"  #include "llfloaterreg.h" +#include "llfloateremojipicker.h"  #include "llfloaterimsession.h"  #include "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container +#include "llfloaterimnearbychat.h" +#include "llgroupiconctrl.h"  #include "lllayoutstack.h" +#include "llpanelemojicomplete.h"  #include "lltoolbarview.h" -#include "llfloaterimnearbychat.h"  const F32 REFRESH_INTERVAL = 1.0f;  const std::string ICN_GROUP("group_chat_icon"); @@ -56,7 +59,7 @@ void cb_group_do_nothing()  }  LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id) -:	LLTransientDockableFloater(NULL, false, session_id), +:	super(NULL, false, session_id),  	mIsP2PChat(false),  	mExpandCollapseBtn(NULL),  	mTearOffBtn(NULL), @@ -75,7 +78,7 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id)  	mInputPanels(NULL),  	mChatLayoutPanelHeight(0)  { -    setAutoFocus(false); +	setAutoFocus(false);  	mSession = LLIMModel::getInstance()->findIMSession(mSessionID);  	mCommitCallbackRegistrar.add("IMSession.Menu.Action", @@ -88,12 +91,12 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id)  			boost::bind(&LLFloaterIMSessionTab::onIMShowModesMenuItemEnable,  this, _2));  	// Right click menu handling -    mEnableCallbackRegistrar.add("Avatar.CheckItem",  boost::bind(&LLFloaterIMSessionTab::checkContextMenuItem,	this, _2)); -    mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2)); -    mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2)); -    mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&cb_group_do_nothing)); +	mEnableCallbackRegistrar.add("Avatar.CheckItem",  boost::bind(&LLFloaterIMSessionTab::checkContextMenuItem,	this, _2)); +	mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2)); +	mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2)); +	mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&cb_group_do_nothing)); -    mMinFloaterHeight = getMinHeight(); +	mMinFloaterHeight = getMinHeight();  }  LLFloaterIMSessionTab::~LLFloaterIMSessionTab() @@ -121,7 +124,7 @@ LLFloaterIMSessionTab::~LLFloaterIMSessionTab()      }  } -//static +// static  LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uuid)  {  	LLFloaterIMSessionTab* conv; @@ -138,7 +141,7 @@ LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uui  	return conv;  }; -//static +// static  LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid)  {  	LLFloaterIMSessionTab* conv; @@ -154,14 +157,16 @@ LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid  	}  	return conv; +  }; +// virtual  void LLFloaterIMSessionTab::setVisible(bool visible)  { -	if(visible && !mHasVisibleBeenInitialized) +	if (visible && !mHasVisibleBeenInitialized)  	{  		mHasVisibleBeenInitialized = true; -		if(!gAgentCamera.cameraMouselook()) +		if (!gAgentCamera.cameraMouselook())  		{  			LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container")->setVisible(true);  		} @@ -175,27 +180,26 @@ void LLFloaterIMSessionTab::setVisible(bool visible)  		mInputButtonPanel->setVisible(isTornOff());  	} -	LLTransientDockableFloater::setVisible(visible); +	super::setVisible(visible);  } -/*virtual*/ +// virtual  void LLFloaterIMSessionTab::setFocus(bool focus)  { -	LLTransientDockableFloater::setFocus(focus); +	super::setFocus(focus); -    //Redirect focus to input editor -    if (focus) +	// Redirect focus to input editor +	if (focus)  	{ -    	updateMessages(); +		updateMessages(); -        if (mInputEditor) -        { -    	    mInputEditor->setFocus(true); -        } +		if (mInputEditor) +		{ +			mInputEditor->setFocus(true); +		}  	}  } -  void LLFloaterIMSessionTab::addToHost(const LLUUID& session_id)  {  	if ((session_id.notNull() && !gIMMgr->hasSession(session_id)) @@ -240,42 +244,60 @@ void LLFloaterIMSessionTab::assignResizeLimits()  {  	bool is_participants_pane_collapsed = mParticipantListPanel->isCollapsed(); -    // disable a layoutstack's functionality when participant list panel is collapsed +	// disable a layoutstack's functionality when participant list panel is collapsed  	mRightPartPanel->setIgnoreReshape(is_participants_pane_collapsed); -    S32 participants_pane_target_width = is_participants_pane_collapsed? -    		0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing()); +	S32 participants_pane_target_width = is_participants_pane_collapsed? +			0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing()); -    S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth; +	S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth;  	setResizeLimits(new_min_width, getMinHeight());  	this->mParticipantListAndHistoryStack->updateLayout();  } +// virtual  bool LLFloaterIMSessionTab::postBuild()  {  	bool result;  	mBodyStack = getChild<LLLayoutStack>("main_stack"); -    mParticipantListAndHistoryStack = getChild<LLLayoutStack>("im_panels"); +	mParticipantListAndHistoryStack = getChild<LLLayoutStack>("im_panels");  	mCloseBtn = getChild<LLButton>("close_btn"); -	mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); +	mCloseBtn->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickClose(this); });  	mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); -	mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onSlide, this)); +	mExpandCollapseBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onSlide(this); });  	mExpandCollapseLineBtn = getChild<LLButton>("minz_btn"); -	mExpandCollapseLineBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onCollapseToLine, this)); +	mExpandCollapseLineBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onCollapseToLine(this); });  	mTearOffBtn = getChild<LLButton>("tear_off_btn");  	mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this)); +	mEmojiRecentPanelToggleBtn = getChild<LLButton>("emoji_recent_panel_toggle_btn"); +	mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(); }); + +	mEmojiRecentPanel = getChild<LLLayoutPanel>("emoji_recent_layout_panel"); +	mEmojiRecentPanel->setVisible(false); + +	mEmojiRecentEmptyText = getChild<LLTextBox>("emoji_recent_empty_text"); +	mEmojiRecentEmptyText->setToolTip(mEmojiRecentEmptyText->getText()); +	mEmojiRecentEmptyText->setVisible(false); + +	mEmojiRecentIconsCtrl = getChild<LLPanelEmojiComplete>("emoji_recent_icons_ctrl"); +	mEmojiRecentIconsCtrl->setCommitCallback([this](LLUICtrl*, const LLSD& value) { onRecentEmojiPicked(value); }); +	mEmojiRecentIconsCtrl->setVisible(false); + +	mEmojiPickerShowBtn = getChild<LLButton>("emoji_picker_show_btn"); +	mEmojiPickerShowBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnClicked(); }); +  	mGearBtn = getChild<LLButton>("gear_btn"); -    mAddBtn = getChild<LLButton>("add_btn"); +	mAddBtn = getChild<LLButton>("add_btn");  	mVoiceButton = getChild<LLButton>("voice_call_btn"); -     +  	mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel");  	mRightPartPanel = getChild<LLLayoutPanel>("right_part_holder"); @@ -327,17 +349,17 @@ bool LLFloaterIMSessionTab::postBuild()  	// Create the root using an ad-hoc base item  	LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel); -    LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>()); -    p.rect = LLRect(0, 0, getRect().getWidth(), 0); -    p.parent_panel = mParticipantListPanel; -    p.listener = base_item; -    p.view_model = &mConversationViewModel; -    p.root = NULL; -    p.use_ellipses = true; -    p.options_menu = "menu_conversation.xml"; -    p.name = "root"; +	LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>()); +	p.rect = LLRect(0, 0, getRect().getWidth(), 0); +	p.parent_panel = mParticipantListPanel; +	p.listener = base_item; +	p.view_model = &mConversationViewModel; +	p.root = NULL; +	p.use_ellipses = true; +	p.options_menu = "menu_conversation.xml"; +	p.name = "root";  	mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p); -    mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); +	mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);  	mConversationsRoot->setEnableRegistrar(&mEnableCallbackRegistrar);  	// Attach that root to the scroller  	mScroller->addChild(mConversationsRoot); @@ -377,6 +399,7 @@ LLParticipantList* LLFloaterIMSessionTab::getParticipantList()  	return dynamic_cast<LLParticipantList*>(LLFloaterIMContainer::getInstance()->getSessionModel(mSessionID));  } +// virtual  void LLFloaterIMSessionTab::draw()  {  	if (mRefreshTimer->hasExpired()) @@ -401,23 +424,24 @@ void LLFloaterIMSessionTab::draw()  		mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL);  	} -	LLTransientDockableFloater::draw(); +	super::draw();  }  void LLFloaterIMSessionTab::enableDisableCallBtn()  { -    if (LLVoiceClient::instanceExists() && mVoiceButton) -    { -        mVoiceButton->setEnabled( -            mSessionID.notNull() -            && mSession -            && mSession->mSessionInitialized -            && LLVoiceClient::getInstance()->voiceEnabled() -            && LLVoiceClient::getInstance()->isVoiceWorking() -            && mSession->mCallBackEnabled); -    } +	if (LLVoiceClient::instanceExists() && mVoiceButton) +	{ +		mVoiceButton->setEnabled( +			mSessionID.notNull() +			&& mSession +			&& mSession->mSessionInitialized +			&& LLVoiceClient::getInstance()->voiceEnabled() +			&& LLVoiceClient::getInstance()->isVoiceWorking() +			&& mSession->mCallBackEnabled); +	}  } +// virtual  void LLFloaterIMSessionTab::onFocusReceived()  {  	setBackgroundOpaque(true); @@ -427,13 +451,14 @@ void LLFloaterIMSessionTab::onFocusReceived()  		LLIMModel::instance().sendNoUnreadMessages(mSessionID);  	} -	LLTransientDockableFloater::onFocusReceived(); +	super::onFocusReceived();  } +// virtual  void LLFloaterIMSessionTab::onFocusLost()  {  	setBackgroundOpaque(false); -	LLTransientDockableFloater::onFocusLost(); +	super::onFocusLost();  }  void LLFloaterIMSessionTab::onInputEditorClicked() @@ -446,52 +471,129 @@ void LLFloaterIMSessionTab::onInputEditorClicked()  	gToolBarView->flashCommand(LLCommandId("chat"), false);  } +void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked() +{ +	bool show = mEmojiRecentPanel->getVisible() ? false : true; +    if (show) +    { +        initEmojiRecentPanel(); +    } + +    mEmojiRecentPanel->setVisible(show); +    mInputEditor->setFocus(true); +} + +void LLFloaterIMSessionTab::onEmojiPickerShowBtnClicked() +{ +    mInputEditor->setFocus(true); +    mInputEditor->showEmojiHelper(); +} + +void LLFloaterIMSessionTab::initEmojiRecentPanel() +{ +    std::list<llwchar>& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed(); +    if (recentlyUsed.empty()) +    { +        mEmojiRecentEmptyText->setVisible(true); +        mEmojiRecentIconsCtrl->setVisible(false); +    } +    else +    { +        LLWString emojis; +        for (llwchar emoji : recentlyUsed) +        { +            emojis += emoji; +        } +        mEmojiRecentIconsCtrl->setEmojis(emojis); +        mEmojiRecentEmptyText->setVisible(false); +        mEmojiRecentIconsCtrl->setVisible(true); +    } +} + +void LLFloaterIMSessionTab::onRecentEmojiPicked(const LLSD& value) +{ +	LLSD::String str = value.asString(); +	if (str.size()) +	{ +		LLWString wstr = utf8string_to_wstring(str); +		if (wstr.size()) +		{ +			llwchar emoji = wstr[0]; +			mInputEditor->insertEmoji(emoji); +		} +	} +} + +void LLFloaterIMSessionTab::closeFloater(bool app_quitting) +{ +	LLFloaterEmojiPicker::saveState(); +	super::closeFloater(app_quitting); +} +  std::string LLFloaterIMSessionTab::appendTime()  { -	time_t utc_time; -	utc_time = time_corrected(); -	std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" -		+LLTrans::getString("TimeMin")+"]"; +	std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:" +						  "[" + LLTrans::getString("TimeMin") + "]";  	LLSD substitution; - -	substitution["datetime"] = (S32) utc_time; -	LLStringUtil::format (timeStr, substitution); +	substitution["datetime"] = (S32)time_corrected(); +	LLStringUtil::format(timeStr, substitution);  	return timeStr;  } -void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD &args) +void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD& args)  { +	if (chat.mMuted || !mChatHistory) +		return;  	// Update the participant activity time  	LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance();  	if (im_box)  	{ -		im_box->setTimeNow(mSessionID,chat.mFromID); +		im_box->setTimeNow(mSessionID, chat.mFromID);  	} -  	LLChat& tmp_chat = const_cast<LLChat&>(chat); -	if(tmp_chat.mTimeStr.empty()) +	if (tmp_chat.mTimeStr.empty())  		tmp_chat.mTimeStr = appendTime(); -	if (!chat.mMuted) -	{ -		tmp_chat.mFromName = chat.mFromName; -		LLSD chat_args; -		if (args) chat_args = args; -		chat_args["use_plain_text_chat_history"] = -				gSavedSettings.getBOOL("PlainTextChatHistory"); -		chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); -		chat_args["show_names_for_p2p_conv"] = -				!mIsP2PChat || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); - -		if (mChatHistory) -		{ -			mChatHistory->appendMessage(chat, chat_args); -		} -	} +	tmp_chat.mFromName = chat.mFromName; + +	LLSD chat_args = args; +	chat_args["use_plain_text_chat_history"] = +			gSavedSettings.getBOOL("PlainTextChatHistory"); +	chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); +	chat_args["show_names_for_p2p_conv"] = !mIsP2PChat || +			gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + +	mChatHistory->appendMessage(chat, chat_args); +} + +void LLFloaterIMSessionTab::updateUsedEmojis(LLWString text) +{ +    LLEmojiDictionary* dictionary = LLEmojiDictionary::getInstance(); +    llassert_always(dictionary); + +    bool emojiSent = false; +    for (llwchar& c : text) +    { +        if (dictionary->isEmoji(c)) +        { +            LLFloaterEmojiPicker::onEmojiUsed(c); +            emojiSent = true; +        } +    } + +    if (!emojiSent) +        return; + +    LLFloaterEmojiPicker::saveState(); + +    if (mEmojiRecentPanel->getVisible()) +    { +        initEmojiRecentPanel(); +    }  }  static LLTrace::BlockTimerStatHandle FTM_BUILD_CONVERSATION_VIEW_PARTICIPANT("Build Conversation View"); @@ -521,10 +623,10 @@ void LLFloaterIMSessionTab::buildConversationViewParticipant()  	while (current_participant_model != end_participant_model)  	{  		LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model); -        if (participant_model) -        { -            addConversationViewParticipant(participant_model); -        } +		if (participant_model) +		{ +			addConversationViewParticipant(participant_model); +		}  		current_participant_model++;  	}  } @@ -544,10 +646,10 @@ void LLFloaterIMSessionTab::addConversationViewParticipant(LLConversationItem* p  	// If not already present, create the participant view and attach it to the root, otherwise, just refresh it  	if (widget)  	{ -        if (update_view) -        { -            updateConversationViewParticipant(uuid); // overkill? -        } +		if (update_view) +		{ +			updateConversationViewParticipant(uuid); // overkill? +		}  	}  	else  	{ @@ -618,11 +720,11 @@ void LLFloaterIMSessionTab::refreshConversation()  		{  			participants_uuids.push_back(widget_it->first);  		} -        if (widget_it->second->getViewModelItem()) -        { -            widget_it->second->refresh(); -            widget_it->second->setVisible(true); -        } +		if (widget_it->second->getViewModelItem()) +		{ +			widget_it->second->refresh(); +			widget_it->second->setVisible(true); +		}  		++widget_it;  	}  	if (is_ad_hoc || mIsP2PChat) @@ -678,7 +780,7 @@ void LLFloaterIMSessionTab::refreshConversation()  // Copied from LLFloaterIMContainer::createConversationViewParticipant(). Refactor opportunity!  LLConversationViewParticipant* LLFloaterIMSessionTab::createConversationViewParticipant(LLConversationItem* item)  { -    LLRect panel_rect = mParticipantListPanel->getRect(); +	LLRect panel_rect = mParticipantListPanel->getRect();  	LLConversationViewParticipant::Params params;  	params.name = item->getDisplayName(); @@ -806,7 +908,7 @@ void LLFloaterIMSessionTab::hideAllStandardButtons()  void LLFloaterIMSessionTab::updateHeaderAndToolbar()  {  	// prevent start conversation before its container -    LLFloaterIMContainer::getInstance(); +	LLFloaterIMContainer::getInstance();  	bool is_not_torn_off = !checkIfTornOff();  	if (is_not_torn_off) @@ -823,12 +925,12 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar()  			&& !mIsP2PChat;  	mParticipantListAndHistoryStack->collapsePanel(mParticipantListPanel, !is_participant_list_visible); -    mParticipantListPanel->setVisible(is_participant_list_visible); +	mParticipantListPanel->setVisible(is_participant_list_visible);  	// Display collapse image (<<) if the floater is hosted  	// or if it is torn off but has an open control panel.  	bool is_expanded = is_not_torn_off || is_participant_list_visible; -     +	  	mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon"));  	mExpandCollapseBtn->setToolTip(  			is_not_torn_off? @@ -857,10 +959,10 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar()  void LLFloaterIMSessionTab::forceReshape()  { -    LLRect floater_rect = getRect(); -    reshape(llmax(floater_rect.getWidth(), this->getMinWidth()), -    		llmax(floater_rect.getHeight(), this->getMinHeight()), -    		true); +	LLRect floater_rect = getRect(); +	reshape(llmax(floater_rect.getWidth(), this->getMinWidth()), +			llmax(floater_rect.getHeight(), this->getMinHeight()), +			true);  } @@ -886,7 +988,7 @@ void LLFloaterIMSessionTab::processChatHistoryStyleUpdate(bool clean_messages/*  	LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");  	if (nearby_chat)  	{ -             nearby_chat->reloadMessages(clean_messages); +			 nearby_chat->reloadMessages(clean_messages);  	}  } @@ -932,15 +1034,15 @@ void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self)  	{  		if (!self->mIsP2PChat)  		{ -            // The state must toggle the collapsed state of the panel -           should_be_expanded = self->mParticipantListPanel->isCollapsed(); +			// The state must toggle the collapsed state of the panel +			should_be_expanded = self->mParticipantListPanel->isCollapsed();  			// Update the expand/collapse flag of the participant list panel and save it -            gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded); -            self->mIsParticipantListExpanded = should_be_expanded; -             -            // Refresh for immediate feedback -            self->refreshConversation(); +			gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded); +			self->mIsParticipantListExpanded = should_be_expanded; + +			// Refresh for immediate feedback +			self->refreshConversation();  		}  	} @@ -977,12 +1079,12 @@ void LLFloaterIMSessionTab::reshapeFloater(bool collapse)  			+ mChatLayoutPanel->getRect().getHeight() - mChatLayoutPanelHeight + 2;  		floater_rect.mTop -= height; -        setResizeLimits(getMinWidth(), floater_rect.getHeight()); +		setResizeLimits(getMinWidth(), floater_rect.getHeight());  	}  	else  	{  		floater_rect.mTop = floater_rect.mBottom + mFloaterHeight; -        setResizeLimits(getMinWidth(), mMinFloaterHeight); +		setResizeLimits(getMinWidth(), mMinFloaterHeight);  	}  	enableResizeCtrls(true, true, !collapse); @@ -1007,7 +1109,7 @@ void LLFloaterIMSessionTab::restoreFloater()  		setShape(floater_rect, true);  		mBodyStack->updateLayout();  		mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon")); -        setResizeLimits(getMinWidth(), mMinFloaterHeight); +		setResizeLimits(getMinWidth(), mMinFloaterHeight);  		setMessagePaneExpanded(true);  		saveCollapsedState();  		mInputEditor->enableSingleLineMode(false); @@ -1035,8 +1137,8 @@ void LLFloaterIMSessionTab::onTearOffClicked()  {  	restoreFloater();  	setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE); -    mSaveRect = isTornOff(); -    initRectControl(); +	mSaveRect = isTornOff(); +	initRectControl();  	LLFloater::onClickTearOff(this);  	LLFloaterIMContainer* container = LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container"); @@ -1125,8 +1227,8 @@ bool LLFloaterIMSessionTab::checkIfTornOff()  void LLFloaterIMSessionTab::doToSelected(const LLSD& userdata)  {  	// Get the list of selected items in the tab -    std::string command = userdata.asString(); -    uuid_vec_t selected_uuids; +	std::string command = userdata.asString(); +	uuid_vec_t selected_uuids;  	getSelectedUUIDs(selected_uuids);  	// Perform the command (IM, profile, etc...) on the list using the general conversation container method @@ -1138,8 +1240,8 @@ void LLFloaterIMSessionTab::doToSelected(const LLSD& userdata)  bool LLFloaterIMSessionTab::enableContextMenuItem(const LLSD& userdata)  {  	// Get the list of selected items in the tab -    std::string command = userdata.asString(); -    uuid_vec_t selected_uuids; +	std::string command = userdata.asString(); +	uuid_vec_t selected_uuids;  	getSelectedUUIDs(selected_uuids);  	// Perform the item enable test on the list using the general conversation container method @@ -1150,8 +1252,8 @@ bool LLFloaterIMSessionTab::enableContextMenuItem(const LLSD& userdata)  bool LLFloaterIMSessionTab::checkContextMenuItem(const LLSD& userdata)  {  	// Get the list of selected items in the tab -    std::string command = userdata.asString(); -    uuid_vec_t selected_uuids; +	std::string command = userdata.asString(); +	uuid_vec_t selected_uuids;  	getSelectedUUIDs(selected_uuids);  	// Perform the item check on the list using the general conversation container method @@ -1161,19 +1263,19 @@ bool LLFloaterIMSessionTab::checkContextMenuItem(const LLSD& userdata)  void LLFloaterIMSessionTab::getSelectedUUIDs(uuid_vec_t& selected_uuids)  { -    const std::set<LLFolderViewItem*> selected_items = mConversationsRoot->getSelectionList(); +	const std::set<LLFolderViewItem*> selected_items = mConversationsRoot->getSelectionList(); -    std::set<LLFolderViewItem*>::const_iterator it = selected_items.begin(); -    const std::set<LLFolderViewItem*>::const_iterator it_end = selected_items.end(); +	std::set<LLFolderViewItem*>::const_iterator it = selected_items.begin(); +	const std::set<LLFolderViewItem*>::const_iterator it_end = selected_items.end(); -    for (; it != it_end; ++it) -    { -        LLConversationItem* conversation_item = static_cast<LLConversationItem *>((*it)->getViewModelItem()); -        if (conversation_item) -        { -            selected_uuids.push_back(conversation_item->getUUID()); -        } -    } +	for (; it != it_end; ++it) +	{ +		LLConversationItem* conversation_item = static_cast<LLConversationItem *>((*it)->getViewModelItem()); +		if (conversation_item) +		{ +			selected_uuids.push_back(conversation_item->getUUID()); +		} +	}  }  LLConversationItem* LLFloaterIMSessionTab::getCurSelectedViewModelItem() @@ -1181,8 +1283,8 @@ LLConversationItem* LLFloaterIMSessionTab::getCurSelectedViewModelItem()  	LLConversationItem *conversationItem = NULL;  	if(mConversationsRoot &&  -        mConversationsRoot->getCurSelectedItem() &&  -        mConversationsRoot->getCurSelectedItem()->getViewModelItem()) +		mConversationsRoot->getCurSelectedItem() &&  +		mConversationsRoot->getCurSelectedItem()->getViewModelItem())  	{  		conversationItem = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem()) ;  	} diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index a54c463470..8a024611ff 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -41,10 +41,12 @@  class LLPanelChatControlPanel;  class LLChatEntry;  class LLChatHistory; +class LLPanelEmojiComplete;  class LLFloaterIMSessionTab  	: public LLTransientDockableFloater  { +	using super = LLTransientDockableFloater;  public:  	LOG_CLASS(LLFloaterIMSessionTab); @@ -68,9 +70,8 @@ public:  	bool isHostAttached() {return mIsHostAttached;}  	void setHostAttached(bool is_attached) {mIsHostAttached = is_attached;} -    static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid); -    static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid); - +	static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid); +	static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid);  	bool isNearbyChat() {return mIsNearbyChat;} @@ -80,6 +81,7 @@ public:  	/*virtual*/ void draw();  	/*virtual*/ void setVisible(bool visible);  	/*virtual*/ void setFocus(bool focus); +	/*virtual*/ void closeFloater(bool app_quitting = false);  	// Handle the left hand participant list widgets  	void addConversationViewParticipant(LLConversationItem* item, bool update_view = true); @@ -136,15 +138,17 @@ protected:  	virtual void enableDisableCallBtn();  	// process focus events to set a currently active session -	/* virtual */ void onFocusLost();  	/* virtual */ void onFocusReceived(); +	/* virtual */ void onFocusLost();  	// prepare chat's params and out one message to chatHistory -	void appendMessage(const LLChat& chat, const LLSD &args = 0); +	void appendMessage(const LLChat& chat, const LLSD& args = LLSD());  	std::string appendTime();  	void assignResizeLimits(); +	void updateUsedEmojis(LLWString text); +  	S32  mFloaterExtraWidth;  	bool mIsNearbyChat; @@ -152,8 +156,7 @@ protected:  	bool mMessagePaneExpanded;  	bool mIsParticipantListExpanded; -    S32 mMinFloaterHeight; - +	S32 mMinFloaterHeight;  	LLIMModel::LLIMSession* mSession; @@ -168,32 +171,37 @@ protected:  	LLLayoutPanel* mContentPanel;  	LLLayoutPanel* mToolbarPanel;  	LLLayoutPanel* mInputButtonPanel; +	LLLayoutPanel* mEmojiRecentPanel; +	LLTextBox* mEmojiRecentEmptyText; +	LLPanelEmojiComplete* mEmojiRecentIconsCtrl;  	LLParticipantList* getParticipantList();  	conversations_widgets_map mConversationsWidgets;  	LLConversationViewModel mConversationViewModel;  	LLFolderView* mConversationsRoot;  	LLScrollContainer* mScroller; -    LLChatHistory* mChatHistory; +	LLChatHistory* mChatHistory;  	LLChatEntry* mInputEditor; -	LLLayoutPanel * mChatLayoutPanel; -	LLLayoutStack * mInputPanels; +	LLLayoutPanel* mChatLayoutPanel; +	LLLayoutStack* mInputPanels;  	LLButton* mExpandCollapseLineBtn;  	LLButton* mExpandCollapseBtn;  	LLButton* mTearOffBtn; +	LLButton* mEmojiRecentPanelToggleBtn; +	LLButton* mEmojiPickerShowBtn;  	LLButton* mCloseBtn;  	LLButton* mGearBtn;  	LLButton* mAddBtn; -    LLButton* mVoiceButton; +	LLButton* mVoiceButton;  private:  	// Handling selection and contextual menu -    void doToSelected(const LLSD& userdata); -    bool enableContextMenuItem(const LLSD& userdata); -    bool checkContextMenuItem(const LLSD& userdata); +	void doToSelected(const LLSD& userdata); +	bool enableContextMenuItem(const LLSD& userdata); +	bool checkContextMenuItem(const LLSD& userdata); -    void getSelectedUUIDs(uuid_vec_t& selected_uuids); +	void getSelectedUUIDs(uuid_vec_t& selected_uuids);  	/// Refreshes the floater at a constant rate.  	virtual void refresh() = 0; @@ -207,9 +215,14 @@ private:  	void onInputEditorClicked(); +	void onEmojiRecentPanelToggleBtnClicked(); +	void onEmojiPickerShowBtnClicked(); +	void initEmojiRecentPanel(); +	void onRecentEmojiPicked(const LLSD& value); +  	bool checkIfTornOff(); -    bool mIsHostAttached; -    bool mHasVisibleBeenInitialized; +	bool mIsHostAttached; +	bool mHasVisibleBeenInitialized;  	LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index 43a19ab027..90e44d49dd 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -1601,7 +1601,7 @@ void LLOverlapPanel::draw()  		LLUI::translate(5,getRect().getHeight()-20);	// translate to top-5,left-5  		LLView::sDrawPreviewHighlights = false;  		LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text, 0, 0, 0, text_color, -				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false); +				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);  	}  	else  	{ @@ -1619,7 +1619,7 @@ void LLOverlapPanel::draw()  			std::string current_selection = std::string(current_selection_text + LLView::sPreviewClickedElement->getName() + " (no elements overlap)");  			S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(current_selection) + 10;  			LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection, 0, 0, 0, text_color, -					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false); +					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);  			// widen panel enough to fit this text  			LLRect rect = getRect();  			setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop)); @@ -1685,7 +1685,7 @@ void LLOverlapPanel::draw()  		// draw currently-selected element at top of overlappers  		LLUI::translate(0,-mSpacing);  		LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text + LLView::sPreviewClickedElement->getName(), 0, 0, 0, text_color, -				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false); +				LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);  		LLUI::translate(0,-mSpacing-LLView::sPreviewClickedElement->getRect().getHeight());	// skip spacing distance + height  		LLView::sPreviewClickedElement->draw(); @@ -1700,7 +1700,7 @@ void LLOverlapPanel::draw()  			// draw name  			LLUI::translate(0,-mSpacing);  			LLFontGL::getFontSansSerifSmall()->renderUTF8(overlapper_text + viewp->getName(), 0, 0, 0, text_color, -					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false); +					LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);  			// draw element  			LLUI::translate(0,-mSpacing-viewp->getRect().getHeight());	// skip spacing distance + height diff --git a/indra/newview/llfriendcard.h b/indra/newview/llfriendcard.h index f5679d7d85..ef0dda7949 100644 --- a/indra/newview/llfriendcard.h +++ b/indra/newview/llfriendcard.h @@ -55,7 +55,7 @@ public:      };  	// LLFriendObserver implementation -	void changed(U32 mask) +	void changed(U32 mask) override  	{  		onFriendListUpdate(mask);  	} diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h index 05b9417ff4..7a7bc2938f 100644 --- a/indra/newview/llgesturemgr.h +++ b/indra/newview/llgesturemgr.h @@ -135,7 +135,7 @@ public:  	void notifyObservers();  	// Overriding so we can update active gesture names and notify observers  -	void changed(U32 mask);  +	void changed(U32 mask) override;  	bool matchPrefix(const std::string& in_str, std::string* out_str); @@ -150,7 +150,7 @@ protected:  	void runStep(LLMultiGesture* gesture, LLGestureStep* step);  	// LLInventoryCompletionObserver trigger -	void done(); +	void done() override;  	// Used by loadGesture  	static void onLoadComplete(const LLUUID& asset_uuid, diff --git a/indra/newview/llhudrender.cpp b/indra/newview/llhudrender.cpp index b48bfe223f..a1e8560a6e 100644 --- a/indra/newview/llhudrender.cpp +++ b/indra/newview/llhudrender.cpp @@ -138,7 +138,7 @@ void hud_render_text(const LLWString &wstr, const LLVector3 &pos_agent,  	LLUI::translate((F32) winX*1.0f/LLFontGL::sScaleX, (F32) winY*1.0f/(LLFontGL::sScaleY), -(((F32) winZ*2.f)-1.f));  	F32 right_x; -	font.render(wstr, 0, 0, 1, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, shadow, wstr.length(), 1000, &right_x); +	font.render(wstr, 0, 0, 1, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, shadow, wstr.length(), 1000, &right_x, /*use_ellipses*/false, /*use_color*/true);  	LLUI::popMatrix();  	gGL.popMatrix(); diff --git a/indra/newview/llimagefiltersmanager.h b/indra/newview/llimagefiltersmanager.h index d06212d85a..05d1806da4 100644 --- a/indra/newview/llimagefiltersmanager.h +++ b/indra/newview/llimagefiltersmanager.h @@ -45,7 +45,7 @@ private:  	void loadAllFilters();  	void loadFiltersFromDir(const std::string& dir); -	/*virtual*/ void initSingleton(); +	/*virtual*/ void initSingleton() override;  	// List of filters : first is the user friendly localized name, second is the xml file name      std::map<std::string,std::string> mFiltersList; diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 74f6de3a98..7763e79ecf 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -537,7 +537,7 @@ public:  	static void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction, bool ended_by_agent);  private: -	void initSingleton(); +	void initSingleton() override;  	void onVoiceChannelChangedInt(const LLUUID &session_id);  	void onVoiceChannelStateChangedInt(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction, bool ended_by_agent); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 7e8b24b7e5..aa3d45b7d2 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -2435,11 +2435,17 @@ void LLInventoryGallery::startDrag()  {      std::vector<EDragAndDropType> types;      uuid_vec_t ids; +    LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_AGENT;      for (LLUUID& selected_id : mSelectedItemIDs)      {          const LLInventoryItem* item = gInventory.getItem(selected_id);          if (item)          { +            if (item->getPermissions().getOwner() == ALEXANDRIA_LINDEN_ID) +            { +                src = LLToolDragAndDrop::SOURCE_LIBRARY; +            } +              EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(item->getType());              types.push_back(type);              ids.push_back(selected_id); @@ -2449,12 +2455,17 @@ void LLInventoryGallery::startDrag()          if (cat && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())              && !LLFolderType::lookupIsProtectedType((cat)->getPreferredType()))          { +            if (cat->getOwnerID() == ALEXANDRIA_LINDEN_ID) +            { +                src = LLToolDragAndDrop::SOURCE_LIBRARY; +            } +              EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType());              types.push_back(type);              ids.push_back(selected_id);          }      } -    LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, LLToolDragAndDrop::SOURCE_AGENT); +    LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, src);  }  bool LLInventoryGallery::areViewsInitialized() diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 09c21b42cd..2798bd78ba 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -73,7 +73,7 @@ class LLMuteList : public LLSingleton<LLMuteList>  {  	LLSINGLETON(LLMuteList);  	~LLMuteList(); -	/*virtual*/ void cleanupSingleton(); +	/*virtual*/ void cleanupSingleton() override;  public:  	// reasons for auto-unmuting a resident  	enum EAutoReason  diff --git a/indra/newview/llnavigationbar.h b/indra/newview/llnavigationbar.h index 759aeab08e..d3f415c248 100755 --- a/indra/newview/llnavigationbar.h +++ b/indra/newview/llnavigationbar.h @@ -92,10 +92,10 @@ class LLNavigationBar  public: -	/*virtual*/ void	draw(); -	/*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask); -	/*virtual*/ bool	postBuild(); -	/*virtual*/ void	setVisible(bool visible); +	/*virtual*/ void	draw() override; +	/*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool	postBuild() override; +	/*virtual*/ void	setVisible(bool visible) override;  	void handleLoginComplete();  	void clearHistoryCache(); diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h index 2f136d48e8..56f2ceb8b1 100644 --- a/indra/newview/lloutfitobserver.h +++ b/indra/newview/lloutfitobserver.h @@ -40,7 +40,7 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO  public: -	virtual void changed(U32 mask); +	virtual void changed(U32 mask) override;  	void notifyOutfitLockChanged() { mOutfitLockChanged();  } diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp new file mode 100644 index 0000000000..c794ca44ec --- /dev/null +++ b/indra/newview/llpanelemojicomplete.cpp @@ -0,0 +1,579 @@ +/** +* @file llpanelemojicomplete.h +* @brief Header file for LLPanelEmojiComplete +* +* $LicenseInfo:firstyear=2012&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, 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 "llemojidictionary.h" +#include "llemojihelper.h" +#include "llpanelemojicomplete.h" +#include "llscrollbar.h" +#include "lluictrlfactory.h" + +constexpr U32 MIN_MOUSE_MOVE_DELTA = 4; +constexpr U32 MIN_SHORT_CODE_WIDTH = 100; +constexpr U32 DEF_PADDING = 8; + +// ============================================================================ +// LLPanelEmojiComplete +// + +static LLDefaultChildRegistry::Register<LLPanelEmojiComplete> r("emoji_complete"); + +LLPanelEmojiComplete::Params::Params() +    : autosize("autosize") +    , noscroll("noscroll") +    , vertical("vertical") +    , max_visible("max_visible") +    , padding("padding", DEF_PADDING) +    , selected_image("selected_image") +{ +} + +LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) +    : LLUICtrl(p) +    , mAutoSize(p.autosize) +    , mNoScroll(p.noscroll) +    , mVertical(p.vertical) +    , mMaxVisible(p.max_visible) +    , mPadding(p.padding) +    , mSelectedImage(p.selected_image) +    , mIconFont(LLFontGL::getFontEmojiHuge()) +    , mTextFont(LLFontGL::getFontSansSerifBig()) +    , mScrollbar(nullptr) +{ +    if (mVertical) +    { +        LLScrollbar::Params sbparams; +        sbparams.orientation(LLScrollbar::VERTICAL); +        sbparams.change_callback([this](S32 index, LLScrollbar*) { onScrollbarChange(index); }); +        mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams); +        addChild(mScrollbar); +    } +} + +LLPanelEmojiComplete::~LLPanelEmojiComplete() +{ +} + +void LLPanelEmojiComplete::draw() +{ +    LLUICtrl::draw(); + +    if (!mTotalEmojis) +        return; + +    const size_t firstVisibleIdx = mScrollPos; +    const size_t lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mTotalEmojis); + +    if (mCurSelected >= firstVisibleIdx && mCurSelected < lastVisibleIdx) +    { +        S32 x, y, width, height; +        if (mVertical) +        { +            x = mRenderRect.mLeft; +            y = mRenderRect.mTop - (mCurSelected - firstVisibleIdx + 1) * mEmojiHeight; +            width = mRenderRect.getWidth(); +            height = mEmojiHeight; +        } +        else +        { +            x = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth; +            y = mRenderRect.mBottom; +            width = mEmojiWidth; +            height = mRenderRect.getHeight(); +        } +        mSelectedImage->draw(x, y, width, height); +    } + +    F32 iconCenterX = mRenderRect.mLeft + (F32)mEmojiWidth / 2; +    F32 iconCenterY = mRenderRect.mTop - (F32)mEmojiHeight / 2; +    F32 textLeft = mVertical ? mRenderRect.mLeft + mEmojiWidth + mPadding : 0; +    F32 textWidth = mVertical ? getRect().getWidth() - textLeft - mPadding : 0; + +    for (U32 curIdx = firstVisibleIdx; curIdx < lastVisibleIdx; curIdx++) +    { +        LLWString text(1, mEmojis[curIdx].Character); +        mIconFont->render(text, 0, iconCenterX, iconCenterY, +            LLColor4::white, LLFontGL::HCENTER, LLFontGL::VCENTER, LLFontGL::NORMAL, +            LLFontGL::DROP_SHADOW_SOFT, 1); +        if (mVertical) +        { +            const std::string& shortCode = mEmojis[curIdx].String; +            F32 x0 = textLeft; +            F32 x1 = textWidth; +            if (mEmojis[curIdx].Begin) +            { +                std::string text = shortCode.substr(0, mEmojis[curIdx].Begin); +                mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white, +                    LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +                    text.size(), x1); +                x0 += mTextFont->getWidthF32(text); +                x1 = textLeft + textWidth - x0; +            } +            if (x1 > 0 && mEmojis[curIdx].End > mEmojis[curIdx].Begin) +            { +                std::string text = shortCode.substr(mEmojis[curIdx].Begin, mEmojis[curIdx].End - mEmojis[curIdx].Begin); +                mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::yellow6, +                    LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +                    text.size(), x1); +                x0 += mTextFont->getWidthF32(text); +                x1 = textLeft + textWidth - x0; +            } +            if (x1 > 0 && mEmojis[curIdx].End < shortCode.size()) +            { +                std::string text = shortCode.substr(mEmojis[curIdx].End); +                mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white, +                    LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +                    text.size(), x1); +            } +            iconCenterY -= mEmojiHeight; +        } +        else +        { +            iconCenterX += mEmojiWidth; +        } +    } +} + +bool LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask) +{ +    if (mScrollbar && mScrollbar->getVisible() && childrenHandleHover(x, y, mask)) +        return true; + +    LLVector2 curHover(x, y); +    if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA) +    { +        size_t index = posToIndex(x, y); +        if (index < mTotalEmojis) +            mCurSelected = index; +        mLastHover = curHover; +    } + +    return true; +} + +bool LLPanelEmojiComplete::handleKey(KEY key, MASK mask, bool called_from_parent) +{ +    bool handled = false; +    if (mTotalEmojis && MASK_NONE == mask) +    { +        switch (key) +        { +        case KEY_HOME: +            select(0); +            handled = true; +            break; + +        case KEY_END: +            select(mTotalEmojis - 1); +            handled = true; +            break; + +        case KEY_PAGE_DOWN: +            select(mCurSelected + mVisibleEmojis - 1); +            handled = true; +            break; + +        case KEY_PAGE_UP: +            select(mCurSelected - llmin(mCurSelected, mVisibleEmojis + 1)); +            handled = true; +            break; + +        case KEY_LEFT: +        case KEY_UP: +            selectPrevious(); +            handled = true; +            break; + +        case KEY_RIGHT: +        case KEY_DOWN: +            selectNext(); +            handled = true; +            break; + +        case KEY_RETURN: +            onCommit(); +            handled = true; +            break; +        } +    } + +    if (handled) +    { +        return true; +    } + +    return LLUICtrl::handleKey(key, mask, called_from_parent); +} + +bool LLPanelEmojiComplete::handleMouseDown(S32 x, S32 y, MASK mask) +{ +    if (mScrollbar && mScrollbar->getVisible() && childrenHandleMouseDown(x, y, mask)) +        return true; + +    mCurSelected = posToIndex(x, y); +    mLastHover = LLVector2(x, y); + +    return true; +} + +bool LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask) +{ +    if (mScrollbar && mScrollbar->getVisible() && childrenHandleMouseUp(x, y, mask)) +        return true; + +    mCurSelected = posToIndex(x, y); +    onCommit(); + +    return true; +} + +bool LLPanelEmojiComplete::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ +    if (mNoScroll) +        return false; + +    if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(x, y, clicks)) +    { +        mCurSelected = posToIndex(x, y); +        return true; +    } + +    if (mTotalEmojis > mVisibleEmojis) +    { +        // In case of wheel up (clicks < 0) we shouldn't subtract more than value of mScrollPos +        // Example: if mScrollPos = 0, clicks = -1 then (mScrollPos + clicks) becomes SIZE_MAX +        // As a result of llclamp<size_t>() mScrollPos becomes (mTotalEmojis - mVisibleEmojis) +        S32 newScrollPos = llmax(0, (S32)mScrollPos + clicks); +        mScrollPos = llclamp<size_t>((size_t)newScrollPos, 0, mTotalEmojis - mVisibleEmojis); +        mCurSelected = posToIndex(x, y); +        return true; +    } + +    return false; +} + +void LLPanelEmojiComplete::onCommit() +{ +    if (mCurSelected < mTotalEmojis) +    { +        LLSD value(wstring_to_utf8str(LLWString(1, mEmojis[mCurSelected].Character))); +        setValue(value); +        LLUICtrl::onCommit(); +    } +} + +void LLPanelEmojiComplete::reshape(S32 width, S32 height, bool called_from_parent) +{ +    LLUICtrl::reshape(width, height, called_from_parent); +    if (mAutoSize) +    { +        updateConstraints(); +    } +    else +    { +        onEmojisChanged(); +    } +} + +void LLPanelEmojiComplete::setEmojis(const LLWString& emojis) +{ +    mEmojis.clear(); + +    auto& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); +    for (const llwchar& emoji : emojis) +    { +        std::string shortCode; +        if (mVertical) +        { +            auto it = emoji2descr.find(emoji); +            if (it != emoji2descr.end() && !it->second->ShortCodes.empty()) +            { +                shortCode = it->second->ShortCodes.front(); +            } +        } +        mEmojis.emplace_back(emoji, shortCode, 0, 0); +    } + +    mTotalEmojis = mEmojis.size(); +    mCurSelected = 0; + +    onEmojisChanged(); +} + +void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) +{ +    llwchar curEmoji = mCurSelected < mTotalEmojis ? mEmojis[mCurSelected].Character : 0; + +    LLEmojiDictionary::instance().findByShortCode(mEmojis, hint); +    mTotalEmojis = mEmojis.size(); + +    mCurSelected = 0; +    for (size_t i = 1; i < mTotalEmojis; ++i) +    { +        if (mEmojis[i].Character == curEmoji) +        { +            mCurSelected = i; +            break; +        } +    } + +    onEmojisChanged(); +} + +U32 LLPanelEmojiComplete::getMaxShortCodeWidth() const +{ +    U32 max_width = 0; +    for (const LLEmojiSearchResult& result : mEmojis) +    { +        S32 width = mTextFont->getWidth(result.String); +        if (width > max_width) +        { +            max_width = width; +        } +    } +    return max_width; +} + +void LLPanelEmojiComplete::onEmojisChanged() +{ +    if (mAutoSize) +    { +        S32 width, height; +        mVisibleEmojis = llmin(mTotalEmojis, mMaxVisible); +        if (mVertical) +        { +            U32 maxShortCodeWidth = getMaxShortCodeWidth(); +            U32 shortCodeWidth = llmax(maxShortCodeWidth, MIN_SHORT_CODE_WIDTH); +            width = mEmojiWidth + shortCodeWidth + mPadding * 2; +            if (!mNoScroll && mVisibleEmojis < mTotalEmojis) +            { +                width += mScrollbar->getThickness(); +            } +            height = mVisibleEmojis * mEmojiHeight; +        } +        else +        { +            width = mVisibleEmojis * mEmojiWidth; +            height = getRect().getHeight(); +        } +        LLUICtrl::reshape(width, height, false); +    } +    else +    { +        mVisibleEmojis = mVertical ? +            mEmojiHeight ? getRect().getHeight() / mEmojiHeight : 0 : +            mEmojiWidth ? getRect().getWidth() / mEmojiWidth : 0; +    } + +    updateConstraints(); +} + +void LLPanelEmojiComplete::onScrollbarChange(S32 index) +{ +    mScrollPos = llclamp<size_t>(index, 0, mTotalEmojis - mVisibleEmojis); +} + +size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const +{ +    if (mRenderRect.pointInRect(x, y)) +    { +        U32 pos = mVertical ? (U32)(mRenderRect.mTop - y) / mEmojiHeight : x / mEmojiWidth; +        return llmin(mScrollPos + pos, mTotalEmojis - 1); +    } +    return std::string::npos; +} + +void LLPanelEmojiComplete::select(size_t emoji_idx) +{ +    mCurSelected = llclamp<size_t>(emoji_idx, 0, mTotalEmojis - 1); + +    updateScrollPos(); +} + +void LLPanelEmojiComplete::selectNext() +{ +    if (!mTotalEmojis) +        return; + +    mCurSelected = (mCurSelected < mTotalEmojis - 1) ? mCurSelected + 1 : 0; + +    updateScrollPos(); +} + +void LLPanelEmojiComplete::selectPrevious() +{ +    if (!mTotalEmojis) +        return; + +    mCurSelected = (mCurSelected && mCurSelected < mTotalEmojis) ? mCurSelected - 1 : mTotalEmojis - 1; + +    updateScrollPos(); +} + +void LLPanelEmojiComplete::updateConstraints() +{ +    mRenderRect = getLocalRect(); + +    mEmojiWidth = mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2; +    if (mVertical) +    { +        mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; +        if (!mNoScroll && mVisibleEmojis < mTotalEmojis) +        { +            mRenderRect.mRight -= mScrollbar->getThickness(); +            mScrollbar->setDocSize(mTotalEmojis); +            mScrollbar->setPageSize(mVisibleEmojis); +            mScrollbar->setOrigin(mRenderRect.mRight, 0); +            mScrollbar->reshape(mScrollbar->getThickness(), mRenderRect.mTop, true); +            mScrollbar->setVisible(true); +        } +        else +        { +            mScrollbar->setVisible(false); +        } +    } +    else +    { +        mEmojiHeight = mRenderRect.getHeight(); +        mRenderRect.stretch((mRenderRect.getWidth() - mVisibleEmojis * mEmojiWidth) / -2, 0); +    } + +    updateScrollPos(); +} + +void LLPanelEmojiComplete::updateScrollPos() +{ +    if (mNoScroll || 0 == mTotalEmojis || mTotalEmojis < mVisibleEmojis || 0 == mCurSelected) +    { +        mScrollPos = 0; +        if (mCurSelected >= mVisibleEmojis) +        { +            mCurSelected = mVisibleEmojis ? mVisibleEmojis - 1 : 0; +        } +    } +    else if (mTotalEmojis - 1 == mCurSelected) +    { +        mScrollPos = mTotalEmojis - mVisibleEmojis; +    } +    else +    { +        mScrollPos = mCurSelected - ((float)mCurSelected / (mTotalEmojis - 2) * (mVisibleEmojis - 2)); +    } + +    if (mScrollbar && mScrollbar->getVisible()) +    { +        mScrollbar->setDocPos(mScrollPos); +    } +} + +// ============================================================================ +// LLFloaterEmojiComplete +// + +LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey) +    : LLFloater(sdKey) +{ +    // This floater should hover on top of our dependent (with the dependent having the focus) +    setFocusStealsFrontmost(false); +    setAutoFocus(false); +    setBackgroundVisible(false); +    setIsChrome(true); +} + +bool LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, bool called_from_parent) +{ +    bool handled = false; +    if (MASK_NONE == mask) +    { +        switch (key) +        { +            case KEY_ESCAPE: +                LLEmojiHelper::instance().hideHelper(); +                handled = true; +                break; +        } +    } + +    if (handled) +        return true; + +    return LLFloater::handleKey(key, mask, called_from_parent); +} + +void LLFloaterEmojiComplete::onOpen(const LLSD& key) +{ +    mEmojiCtrl->setEmojiHint(key["hint"].asString()); +    if (0 == mEmojiCtrl->getEmojiCount()) +    { +        LLEmojiHelper::instance().hideHelper(); +        return; +    } + +    if (mEmojiCtrl->isAutoSize()) +    { +        LLRect outer_rect = getRect(); +        const LLRect& inner_rect = mEmojiCtrl->getRect(); +        outer_rect.mTop = outer_rect.mBottom + inner_rect.mBottom * 2 + inner_rect.getHeight(); +        outer_rect.mRight = outer_rect.mLeft + inner_rect.mLeft * 2 + inner_rect.getWidth(); +        setRect(outer_rect); +    } + +    gFloaterView->adjustToFitScreen(this, false); +} + +bool LLFloaterEmojiComplete::postBuild() +{ +    mEmojiCtrl = findChild<LLPanelEmojiComplete>("emoji_complete_ctrl"); +    mEmojiCtrl->setCommitCallback( +        [this](LLUICtrl* ctrl, const LLSD& param) +        { +            setValue(param); +            onCommit(); +        }); + +    mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth(); +    mEmojiCtrlVert = getRect().getHeight() - mEmojiCtrl->getRect().getHeight(); + +    return LLFloater::postBuild(); +} + +void LLFloaterEmojiComplete::reshape(S32 width, S32 height, bool called_from_parent) +{ +    if (called_from_parent) +    { +        LLFloater::reshape(width, height, called_from_parent); +    } +    else +    { +        LLRect outer(getRect()), inner(mEmojiCtrl->getRect()); +        outer.mRight = outer.mLeft + inner.getWidth() + mEmojiCtrlHorz; +        outer.mTop = outer.mBottom + inner.getHeight() + mEmojiCtrlVert; +        setRect(outer); +    } +} + +// ============================================================================ diff --git a/indra/newview/llpanelemojicomplete.h b/indra/newview/llpanelemojicomplete.h new file mode 100644 index 0000000000..3dfcc98a39 --- /dev/null +++ b/indra/newview/llpanelemojicomplete.h @@ -0,0 +1,132 @@ +/** +* @file llpanelemojicomplete.h +* @brief Header file for LLPanelEmojiComplete +* +* $LicenseInfo:firstyear=2014&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2014, 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$ +*/ + +#pragma once + +#include "llemojidictionary.h" +#include "llfloater.h" +#include "lluictrl.h" + +class LLScrollbar; + +// ============================================================================ +// LLPanelEmojiComplete +// + +class LLPanelEmojiComplete : public LLUICtrl +{ +    friend class LLUICtrlFactory; +public: +    struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> +    { +        Optional<bool>       autosize; +        Optional<bool>       noscroll; +        Optional<bool>       vertical; +        Optional<S32>        max_visible, +                             padding; + +        Optional<LLUIImage*> selected_image; + +        Params(); +    }; + +protected: +    LLPanelEmojiComplete(const LLPanelEmojiComplete::Params&); + +public: +    virtual ~LLPanelEmojiComplete(); + +    void draw() override; +    bool handleHover(S32 x, S32 y, MASK mask) override; +    bool handleKey(KEY key, MASK mask, bool called_from_parent) override; +    bool handleMouseDown(S32 x, S32 y, MASK mask) override; +    bool handleMouseUp(S32 x, S32 y, MASK mask) override; +    bool handleScrollWheel(S32 x, S32 y, S32 clicks) override; +    void onCommit() override; +    void reshape(S32 width, S32 height, bool called_from_parent) override; + +public: +    size_t getEmojiCount() const { return mEmojis.size(); } +    void setEmojis(const LLWString& emojis); +    void setEmojiHint(const std::string& hint); +    bool isAutoSize() const { return mAutoSize; } +    U32 getMaxShortCodeWidth() const; + +protected: +    void onEmojisChanged(); +    void onScrollbarChange(S32 index); +    size_t posToIndex(S32 x, S32 y) const; +    void select(size_t emoji_idx); +    void selectNext(); +    void selectPrevious(); +    void updateConstraints(); +    void updateScrollPos(); + +protected: +    const bool      mAutoSize; +    const bool      mNoScroll; +    const bool      mVertical; +    const size_t    mMaxVisible; +    const S32       mPadding; +    const LLUIImagePtr mSelectedImage; +    const LLFontGL* mIconFont; +    const LLFontGL* mTextFont; + +    std::vector<LLEmojiSearchResult> mEmojis; +    LLScrollbar*    mScrollbar; +    LLRect          mRenderRect; +    U16             mEmojiWidth = 0; +    U16             mEmojiHeight = 0; +    size_t          mTotalEmojis = 0; +    size_t          mVisibleEmojis = 0; +    size_t          mFirstVisible = 0; +    size_t          mScrollPos = 0; +    size_t          mCurSelected = 0; +    LLVector2       mLastHover; +}; + +// ============================================================================ +// LLFloaterEmojiComplete +// + +class LLFloaterEmojiComplete : public LLFloater +{ +public: +    LLFloaterEmojiComplete(const LLSD& sdKey); + +public: +    bool handleKey(KEY key, MASK mask, bool called_from_parent) override; +    void onOpen(const LLSD& key) override; +    bool postBuild() override; +    void reshape(S32 width, S32 height, bool called_from_parent) override; + +protected: +    LLPanelEmojiComplete* mEmojiCtrl = nullptr; +    S32 mEmojiCtrlHorz = 0; +    S32 mEmojiCtrlVert = 0; +}; + +// ============================================================================ diff --git a/indra/newview/llpathfindingpathtool.h b/indra/newview/llpathfindingpathtool.h index 720314df02..f9b3e170f4 100644 --- a/indra/newview/llpathfindingpathtool.h +++ b/indra/newview/llpathfindingpathtool.h @@ -66,17 +66,17 @@ public:  	typedef boost::signals2::signal<void (void)> path_event_signal_t;  	typedef boost::signals2::connection          path_event_slot_t; -	virtual bool      handleMouseDown(S32 pX, S32 pY, MASK pMask); -	virtual bool      handleMouseUp(S32 pX, S32 pY, MASK pMask); -	virtual bool      handleMiddleMouseDown(S32 pX, S32 pY, MASK pMask); -	virtual bool      handleMiddleMouseUp(S32 pX, S32 pY, MASK pMask); -	virtual bool      handleRightMouseDown(S32 pX, S32 pY, MASK pMask); -	virtual bool      handleRightMouseUp(S32 pX, S32 pY, MASK pMask); -	virtual bool      handleDoubleClick(S32 x, S32 y, MASK mask); +	virtual bool      handleMouseDown(S32 pX, S32 pY, MASK pMask) override; +	virtual bool      handleMouseUp(S32 pX, S32 pY, MASK pMask) override; +	virtual bool      handleMiddleMouseDown(S32 pX, S32 pY, MASK pMask) override; +	virtual bool      handleMiddleMouseUp(S32 pX, S32 pY, MASK pMask) override; +	virtual bool      handleRightMouseDown(S32 pX, S32 pY, MASK pMask) override; +	virtual bool      handleRightMouseUp(S32 pX, S32 pY, MASK pMask) override; +	virtual bool      handleDoubleClick(S32 x, S32 y, MASK mask) override; -	virtual bool      handleHover(S32 pX, S32 pY, MASK pMask); +	virtual bool      handleHover(S32 pX, S32 pY, MASK pMask) override; -	virtual bool      handleKey(KEY pKey, MASK pMask); +	virtual bool      handleKey(KEY pKey, MASK pMask) override;  	EPathStatus       getPathStatus() const; diff --git a/indra/newview/llproductinforequest.h b/indra/newview/llproductinforequest.h index d1036374e8..0b94c39d11 100644 --- a/indra/newview/llproductinforequest.h +++ b/indra/newview/llproductinforequest.h @@ -46,7 +46,7 @@ public:  	std::string getDescriptionForSku(const std::string& sku);  private: -	/* virtual */ void initSingleton(); +	/* virtual */ void initSingleton() override;      void getLandDescriptionsCoro(std::string url);      LLSD mSkuDescriptions; diff --git a/indra/newview/llrecentpeople.h b/indra/newview/llrecentpeople.h index 1b322f2c0a..0c04222a9f 100644 --- a/indra/newview/llrecentpeople.h +++ b/indra/newview/llrecentpeople.h @@ -106,7 +106,7 @@ public:  	/**  	 * LLSimpleListener interface.  	 */ -	/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); +	/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) override;  	void updateAvatarsArrivalTime(uuid_vec_t& uuids);  	F32 getArrivalTimeByID(const LLUUID& id); diff --git a/indra/newview/llsceneview.cpp b/indra/newview/llsceneview.cpp index 442feec5b6..e8233faf82 100644 --- a/indra/newview/llsceneview.cpp +++ b/indra/newview/llsceneview.cpp @@ -99,7 +99,6 @@ void LLSceneView::draw()  	std::vector<F32> physics_cost[2];  	F32 total_physics[] = { 0.f, 0.f }; -  	LLViewerRegion* region = gAgent.getRegion();  	if (region)  	{ diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 6500fe77ff..99e1e83512 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -5519,9 +5519,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,  	LLSelectNode* linkset_root = NULL;  	LLViewerRegion*	last_region;  	LLViewerRegion*	current_region; - -//	S32 objects_sent = 0; -//	S32 packets_sent = 0;  	S32 objects_in_this_packet = 0;  	bool link_operation = message_name == "ObjectLink"; @@ -5653,7 +5650,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,  			(*pack_body)(node, user_data);              // do any related logging              (*log_func)(node, user_data); -//			++objects_sent;  			++objects_in_this_packet;  			// and on to the next object @@ -5671,7 +5667,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,  		{  			// otherwise send current message and start new one  			gMessageSystem->sendReliable( last_region->getHost()); -//			packets_sent++;  			objects_in_this_packet = 0;  			gMessageSystem->newMessage(message_name.c_str()); @@ -5688,7 +5683,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,  				{  					// add root instance into new message  					(*pack_body)(linkset_root, user_data); -//					++objects_sent;  					++objects_in_this_packet;  				}  			} @@ -5702,7 +5696,6 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle,  	if (gMessageSystem->getCurrentSendTotal() > 0)  	{  		gMessageSystem->sendReliable( current_region->getHost()); -//		packets_sent++;  	}  	else  	{ diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 1d15b30d6d..671a3ebc9c 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -338,7 +338,7 @@ class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLActiveSpeak  	LOG_CLASS(LLActiveSpeakerMgr);  protected: -	virtual void updateSpeakerList(); +	virtual void updateSpeakerList() override;  };  class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLLocalSpeakerMgr> @@ -347,7 +347,7 @@ class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLLocalSpeaker  	~LLLocalSpeakerMgr ();  	LOG_CLASS(LLLocalSpeakerMgr);  protected: -	virtual void updateSpeakerList(); +	virtual void updateSpeakerList() override;  };  #endif // LL_LLSPEAKERS_H diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index 1c3d1dd09b..18b335fb21 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -53,7 +53,7 @@ class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, L  	LOG_CLASS(SpeakingIndicatorManager);  protected: -    void                cleanupSingleton(); +    void                cleanupSingleton() override;  public: @@ -88,7 +88,7 @@ public:  	 * So, method does not calculate difference between these list it only switches off already   	 * switched on indicators and switches on indicators of voice channel participants  	 */ -	void onParticipantsChanged(); +	void onParticipantsChanged() override;  private:  	typedef std::set<LLUUID> speaker_ids_t; diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index 9ccb2f419f..f410217387 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -589,8 +589,7 @@ void LLGLTexMemBar::draw()  	x_right = 550.0;  	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,  											 text_color, LLFontGL::LEFT, LLFontGL::TOP, -											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, -											 &x_right, false); +											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &x_right);  	F32Kilobits bandwidth(LLAppViewer::getTextureFetch()->getTextureBandwidth());  	F32Kilobits max_bandwidth(gSavedSettings.getF32("ThrottleBandwidthKBPS")); diff --git a/indra/newview/lltoolbrush.h b/indra/newview/lltoolbrush.h index 9193dfe5f1..c4f7c50ecb 100644 --- a/indra/newview/lltoolbrush.h +++ b/indra/newview/lltoolbrush.h @@ -49,27 +49,27 @@ class LLToolBrushLand : public LLTool, public LLEditMenuHandler, public LLSingle  public:  	// x,y in window coords, 0,0 = left,bot -	virtual bool handleMouseDown( S32 x, S32 y, MASK mask ); -	virtual bool handleMouseUp( S32 x, S32 y, MASK mask ); -	virtual bool handleHover( S32 x, S32 y, MASK mask ); -	virtual void handleSelect(); -	virtual void handleDeselect(); +	virtual bool handleMouseDown( S32 x, S32 y, MASK mask ) override; +	virtual bool handleMouseUp( S32 x, S32 y, MASK mask ) override; +	virtual bool handleHover( S32 x, S32 y, MASK mask ) override; +	virtual void handleSelect() override; +	virtual void handleDeselect() override;  	// isAlwaysRendered() - return true if this is a tool that should  	// always be rendered regardless of selection. -	virtual bool isAlwaysRendered() { return true; } +	virtual bool isAlwaysRendered()  override { return true; }  	// Draw the area that will be affected. -	virtual void render(); +	virtual void render() override;  	// on Idle is where the land modification actually occurs  	static void onIdle(void* brush_tool);   -	void			onMouseCaptureLost(); +	void onMouseCaptureLost() override;  	void modifyLandInSelectionGlobal(); -	virtual void	undo(); -	virtual bool	canUndo() const	{ return true; } +	virtual void	undo() override; +	virtual bool	canUndo() const	 override { return true; }  protected:  	void brush( void ); diff --git a/indra/newview/lltoolcomp.h b/indra/newview/lltoolcomp.h index f2be90aadc..7507984d08 100644 --- a/indra/newview/lltoolcomp.h +++ b/indra/newview/lltoolcomp.h @@ -108,11 +108,11 @@ class LLToolCompInspect : public LLToolComposite, public LLSingleton<LLToolCompI  public:  	// Overridden from LLToolComposite -    virtual bool		handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask); -    virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask); -	virtual bool		handleKey(KEY key, MASK mask); -	virtual void		onMouseCaptureLost(); +    virtual bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask) override; +    virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask) override; +	virtual bool		handleKey(KEY key, MASK mask) override; +	virtual void		onMouseCaptureLost() override;  			void		keyUp(KEY key, MASK mask);  	static void pickCallback(const LLPickInfo& pick_info); @@ -133,13 +133,13 @@ class LLToolCompTranslate : public LLToolComposite, public LLSingleton<LLToolCom  public:  	// Overridden from LLToolComposite -	virtual bool		handleMouseDown(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		handleMouseUp(S32 x, S32 y, MASK mask);			// Returns to the default tool -	virtual void		render(); +	virtual bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask) override; +	virtual bool		handleHover(S32 x, S32 y, MASK mask) override; +	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask) override;			// Returns to the default tool +	virtual void		render() override; -	virtual LLTool*		getOverrideTool(MASK mask); +	virtual LLTool*		getOverrideTool(MASK mask) override;  	static void pickCallback(const LLPickInfo& pick_info);  }; @@ -154,13 +154,13 @@ class LLToolCompScale : public LLToolComposite, public LLSingleton<LLToolCompSca  public:  	// Overridden from LLToolComposite -    virtual bool		handleMouseDown(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		handleMouseUp(S32 x, S32 y, MASK mask);			// Returns to the default tool -	virtual void		render(); +    virtual bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +    virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask) override; +    virtual bool		handleHover(S32 x, S32 y, MASK mask) override; +	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask) override;			// Returns to the default tool +	virtual void		render() override; -	virtual LLTool*		getOverrideTool(MASK mask); +	virtual LLTool*		getOverrideTool(MASK mask) override;  	static void pickCallback(const LLPickInfo& pick_info);  }; @@ -176,13 +176,13 @@ class LLToolCompRotate : public LLToolComposite, public LLSingleton<LLToolCompRo  public:  	// Overridden from LLToolComposite -    virtual bool		handleMouseDown(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		handleMouseUp(S32 x, S32 y, MASK mask); -	virtual void		render(); +    virtual bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +    virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask) override; +    virtual bool		handleHover(S32 x, S32 y, MASK mask) override; +	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask) override; +	virtual void		render() override; -	virtual LLTool*		getOverrideTool(MASK mask); +	virtual LLTool*		getOverrideTool(MASK mask) override;  	static void pickCallback(const LLPickInfo& pick_info); @@ -199,9 +199,9 @@ class LLToolCompCreate : public LLToolComposite, public LLSingleton<LLToolCompCr  public:  	// Overridden from LLToolComposite -    virtual bool		handleMouseDown(S32 x, S32 y, MASK mask); -    virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask); -	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask); +    virtual bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +    virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask) override; +	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask) override;  	static void pickCallback(const LLPickInfo& pick_info);  protected: @@ -224,16 +224,16 @@ class LLToolCompGun : public LLToolComposite, public LLSingleton<LLToolCompGun>  public:  	// Overridden from LLToolComposite -    virtual bool			handleHover(S32 x, S32 y, MASK mask); -	virtual bool			handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool			handleDoubleClick(S32 x, S32 y, MASK mask); -	virtual bool			handleRightMouseDown(S32 x, S32 y, MASK mask); -	virtual bool			handleMouseUp(S32 x, S32 y, MASK mask); -	virtual bool			handleScrollWheel(S32 x, S32 y, S32 clicks); -	virtual void			onMouseCaptureLost(); -	virtual void			handleSelect(); -	virtual void			handleDeselect(); -	virtual LLTool*			getOverrideTool(MASK mask) { return NULL; } +    virtual bool			handleHover(S32 x, S32 y, MASK mask) override; +	virtual bool			handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool			handleDoubleClick(S32 x, S32 y, MASK mask) override; +	virtual bool			handleRightMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool			handleMouseUp(S32 x, S32 y, MASK mask) override; +	virtual bool			handleScrollWheel(S32 x, S32 y, S32 clicks) override; +	virtual void			onMouseCaptureLost() override; +	virtual void			handleSelect() override; +	virtual void			handleDeselect() override; +	virtual LLTool*			getOverrideTool(MASK mask) override { return NULL; }  protected:  	LLToolGun*			mGun; diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index f63e74a7a3..060663db93 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -48,12 +48,12 @@ public:  	typedef boost::signals2::signal<void ()> enddrag_signal_t;  	// overridden from LLTool -	virtual bool	handleMouseUp(S32 x, S32 y, MASK mask); -	virtual bool	handleHover(S32 x, S32 y, MASK mask); -	virtual bool	handleKey(KEY key, MASK mask); -	virtual bool	handleToolTip(S32 x, S32 y, MASK mask); -	virtual void	onMouseCaptureLost(); -	virtual void	handleDeselect(); +	virtual bool	handleMouseUp(S32 x, S32 y, MASK mask) override; +	virtual bool	handleHover(S32 x, S32 y, MASK mask) override; +	virtual bool	handleKey(KEY key, MASK mask) override; +	virtual bool	handleToolTip(S32 x, S32 y, MASK mask) override; +	virtual void	onMouseCaptureLost() override; +	virtual void	handleDeselect() override;  	void			setDragStart( S32 x, S32 y );			// In screen space  	bool			isOverThreshold( S32 x, S32 y );		// In screen space diff --git a/indra/newview/lltoolface.h b/indra/newview/lltoolface.h index d063c9966e..4d3a51c40f 100644 --- a/indra/newview/lltoolface.h +++ b/indra/newview/lltoolface.h @@ -39,11 +39,11 @@ class LLToolFace  	virtual ~LLToolFace();  public: -	virtual bool	handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool	handleDoubleClick(S32 x, S32 y, MASK mask); -	virtual void	handleSelect(); -	virtual void	handleDeselect(); -	virtual void	render();			// draw face highlights +	virtual bool	handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool	handleDoubleClick(S32 x, S32 y, MASK mask) override; +	virtual void	handleSelect() override; +	virtual void	handleDeselect() override; +	virtual void	render() override;			// draw face highlights  	static void pickCallback(const LLPickInfo& pick_info);  }; diff --git a/indra/newview/lltoolfocus.h b/indra/newview/lltoolfocus.h index a72f5888a3..ada619703d 100644 --- a/indra/newview/lltoolfocus.h +++ b/indra/newview/lltoolfocus.h @@ -38,16 +38,16 @@ class LLToolCamera  	virtual ~LLToolCamera();  public: -	virtual bool	handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool	handleMouseUp(S32 x, S32 y, MASK mask); -	virtual bool	handleHover(S32 x, S32 y, MASK mask); +	virtual bool	handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool	handleMouseUp(S32 x, S32 y, MASK mask) override; +	virtual bool	handleHover(S32 x, S32 y, MASK mask) override; -	virtual void	onMouseCaptureLost(); +	virtual void	onMouseCaptureLost() override; -	virtual void	handleSelect(); -	virtual void	handleDeselect(); +	virtual void	handleSelect() override; +	virtual void	handleDeselect() override; -	virtual LLTool*	getOverrideTool(MASK mask) { return NULL; } +	virtual LLTool*	getOverrideTool(MASK mask) override { return NULL; }      void setClickPickPending() { mClickPickPending = true; }  	static void pickCallback(const LLPickInfo& pick_info); diff --git a/indra/newview/lltoolindividual.h b/indra/newview/lltoolindividual.h index 8c4eb70923..73ab6bd1af 100644 --- a/indra/newview/lltoolindividual.h +++ b/indra/newview/lltoolindividual.h @@ -43,11 +43,9 @@ class LLToolIndividual : public LLTool, public LLSingleton<LLToolIndividual>  	virtual ~LLToolIndividual();  public: -	virtual bool handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool handleDoubleClick(S32 x, S32 y, MASK mask); -	virtual void handleSelect(); -	//virtual void handleDeselect(); -	//virtual void render(); +	virtual bool handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool handleDoubleClick(S32 x, S32 y, MASK mask) override; +	virtual void handleSelect() override;  	static void pickCallback(const LLPickInfo& pick_info); diff --git a/indra/newview/lltoolobjpicker.h b/indra/newview/lltoolobjpicker.h index 2238aefa08..4d120009e3 100644 --- a/indra/newview/lltoolobjpicker.h +++ b/indra/newview/lltoolobjpicker.h @@ -38,16 +38,16 @@ class LLToolObjPicker : public LLTool, public LLSingleton<LLToolObjPicker>  	LLSINGLETON(LLToolObjPicker);  public: -	virtual bool		handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask); -	virtual bool		handleHover(S32 x, S32 y, MASK mask); +	virtual bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask) override; +	virtual bool		handleHover(S32 x, S32 y, MASK mask) override; -	virtual void 		handleSelect(); -	virtual void 		handleDeselect(); +	virtual void 		handleSelect() override; +	virtual void 		handleDeselect() override; -	virtual void		onMouseCaptureLost(); +	virtual void		onMouseCaptureLost() override; -	virtual void 		setExitCallback(void (*callback)(void *), void *callback_data); +	void 		setExitCallback(void (*callback)(void *), void *callback_data);  	LLUUID				getObjectID() const { return mHitObjectID; } diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index f746e35ef9..39cca0ffa7 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -42,26 +42,26 @@ class LLToolPie : public LLTool, public LLSingleton<LLToolPie>  public:  	// Virtual functions inherited from LLMouseHandler -	virtual bool		handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, bool down); -	virtual bool		handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool		handleRightMouseDown(S32 x, S32 y, MASK mask); -	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask); -	virtual bool		handleRightMouseUp(S32 x, S32 y, MASK mask); -	virtual bool		handleHover(S32 x, S32 y, MASK mask); -	virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask); +	virtual bool		handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, bool down) override; +	virtual bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool		handleRightMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool		handleMouseUp(S32 x, S32 y, MASK mask) override; +	virtual bool		handleRightMouseUp(S32 x, S32 y, MASK mask) override; +	virtual bool		handleHover(S32 x, S32 y, MASK mask) override; +	virtual bool		handleDoubleClick(S32 x, S32 y, MASK mask) override;  	bool				handleScrollWheelAny(S32 x, S32 y, S32 clicks_x, S32 clicks_y); -	virtual bool		handleScrollWheel(S32 x, S32 y, S32 clicks); -	virtual bool		handleScrollHWheel(S32 x, S32 y, S32 clicks); -	virtual bool		handleToolTip(S32 x, S32 y, MASK mask); +	virtual bool		handleScrollWheel(S32 x, S32 y, S32 clicks) override; +	virtual bool		handleScrollHWheel(S32 x, S32 y, S32 clicks) override; +	virtual bool		handleToolTip(S32 x, S32 y, MASK mask) override; -	virtual void		render(); +	virtual void		render() override; -	virtual void		stopEditing(); +	virtual void		stopEditing() override; -	virtual void		onMouseCaptureLost(); -	virtual void		handleSelect(); -	virtual void		handleDeselect(); -	virtual LLTool*		getOverrideTool(MASK mask); +	virtual void		onMouseCaptureLost() override; +	virtual void		handleSelect() override; +	virtual void		handleDeselect() override; +	virtual LLTool*		getOverrideTool(MASK mask) override;  	LLPickInfo&			getPick() { return mPick; }  	U8					getClickAction() { return mClickAction; } diff --git a/indra/newview/lltoolpipette.h b/indra/newview/lltoolpipette.h index 8fc7ae5edf..ade06ba78d 100644 --- a/indra/newview/lltoolpipette.h +++ b/indra/newview/lltoolpipette.h @@ -47,10 +47,10 @@ class LLToolPipette  	virtual ~LLToolPipette();  public: -	virtual bool	handleMouseDown(S32 x, S32 y, MASK mask); -	virtual bool	handleMouseUp(S32 x, S32 y, MASK mask); -	virtual bool	handleHover(S32 x, S32 y, MASK mask); -	virtual bool	handleToolTip(S32 x, S32 y, MASK mask); +	virtual bool	handleMouseDown(S32 x, S32 y, MASK mask) override; +	virtual bool	handleMouseUp(S32 x, S32 y, MASK mask) override; +	virtual bool	handleHover(S32 x, S32 y, MASK mask) override; +	virtual bool	handleToolTip(S32 x, S32 y, MASK mask) override;  	// Note: Don't return connection; use boost::bind + boost::signals2::trackable to disconnect slots  	typedef boost::signals2::signal<void (const LLTextureEntry& te)> signal_t; diff --git a/indra/newview/lltoolselectland.h b/indra/newview/lltoolselectland.h index 3aa86bc87d..893bf970d1 100644 --- a/indra/newview/lltoolselectland.h +++ b/indra/newview/lltoolselectland.h @@ -39,15 +39,15 @@ class LLToolSelectLand  	virtual ~LLToolSelectLand();  public: -	/*virtual*/ bool		handleMouseDown(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleDoubleClick(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleMouseUp(S32 x, S32 y, MASK mask); -	/*virtual*/ bool		handleHover(S32 x, S32 y, MASK mask); -	/*virtual*/ void		render();				// draw the select rectangle -	/*virtual*/ bool		isAlwaysRendered()		{ return true; } +	/*virtual*/ bool		handleMouseDown(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleDoubleClick(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleMouseUp(S32 x, S32 y, MASK mask) override; +	/*virtual*/ bool		handleHover(S32 x, S32 y, MASK mask) override; +	/*virtual*/ void		render() override;				// draw the select rectangle +	/*virtual*/ bool		isAlwaysRendered() override	{ return true; } -	/*virtual*/ void		handleSelect(); -	/*virtual*/ void		handleDeselect(); +	/*virtual*/ void		handleSelect() override; +	/*virtual*/ void		handleDeselect() override;  protected:  	bool			outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y); diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h index a40042380a..f82c5ffa98 100644 --- a/indra/newview/llversioninfo.h +++ b/indra/newview/llversioninfo.h @@ -47,7 +47,7 @@ class LLStoreListener;  class LLVersionInfo: public LLSingleton<LLVersionInfo>  {  	LLSINGLETON(LLVersionInfo); -	void initSingleton(); +	void initSingleton() override;  public:  	~LLVersionInfo(); diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp index 1c3c547bc1..0d2d62fd77 100644 --- a/indra/newview/llviewerchat.cpp +++ b/indra/newview/llviewerchat.cpp @@ -25,7 +25,7 @@   */  #include "llviewerprecompiledheaders.h" -#include "llviewerchat.h"  +#include "llviewerchat.h"  // newview includes  #include "llagent.h" 	// gAgent		 diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 3a08f748d6..8353d4d1d7 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -66,6 +66,7 @@  #include "llfloaterdestinations.h"  #include "llfloaterdisplayname.h"  #include "llfloatereditextdaycycle.h" +#include "llfloateremojipicker.h"  #include "llfloaterenvironmentadjust.h"  #include "llfloaterexperienceprofile.h"  #include "llfloaterexperiences.h" @@ -161,6 +162,7 @@  #include "llfloaterimnearbychat.h"  #include "llpanelblockedlist.h"  #include "llpanelprofileclassifieds.h" +#include "llpanelemojicomplete.h"  #include "llpreviewanim.h"  #include "llpreviewgesture.h"  #include "llpreviewnotecard.h" @@ -339,7 +341,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>);      LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChangeItemThumbnail>);  	LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); -    LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>); +	LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>);  	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("add_landmark", "floater_create_landmark.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCreateLandmark>); @@ -347,18 +349,20 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("delete_pref_preset", "floater_delete_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeletePrefPreset>);  	LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>); +	LLFloaterReg::add("emoji_picker", "floater_emoji_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEmojiPicker>); +	LLFloaterReg::add("emoji_complete", "floater_emoji_complete.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEmojiComplete>);  	LLFloaterReg::add("env_post_process", "floater_post_process.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPostProcess>); -    LLFloaterReg::add("env_fixed_environmentent_water", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentWater>); -    LLFloaterReg::add("env_fixed_environmentent_sky", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentSky>); +	LLFloaterReg::add("env_fixed_environmentent_water", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentWater>); +	LLFloaterReg::add("env_fixed_environmentent_sky", "floater_fixedenvironment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterFixedEnvironmentSky>); -    LLFloaterReg::add("env_adjust_snapshot", "floater_adjust_environment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEnvironmentAdjust>); +	LLFloaterReg::add("env_adjust_snapshot", "floater_adjust_environment.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEnvironmentAdjust>); -    LLFloaterReg::add("env_edit_extdaycycle", "floater_edit_ext_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditExtDayCycle>); -    LLFloaterReg::add("my_environments", "floater_my_environments.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMyEnvironment>); +	LLFloaterReg::add("env_edit_extdaycycle", "floater_edit_ext_day_cycle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEditExtDayCycle>); +	LLFloaterReg::add("my_environments", "floater_my_environments.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMyEnvironment>); -    LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>); -    LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiences>); +	LLFloaterReg::add("event", "floater_event.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterEvent>); +	LLFloaterReg::add("experiences", "floater_experiences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiences>);  	LLFloaterReg::add("experience_profile", "floater_experienceprofile.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperienceProfile>);  	LLFloaterReg::add("experience_search", "floater_experience_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterExperiencePicker>); diff --git a/indra/newview/llviewerhelp.h b/indra/newview/llviewerhelp.h index da50e07a43..bbd20bc07e 100644 --- a/indra/newview/llviewerhelp.h +++ b/indra/newview/llviewerhelp.h @@ -43,21 +43,21 @@ class LLViewerHelp : public LLHelp, public LLSingleton<LLViewerHelp>   public:  	/// display the specified help topic in the help viewer -	/*virtual*/ void showTopic(const std::string &topic); +	/*virtual*/ void showTopic(const std::string &topic) override; -	std::string getURL(const std::string& topic); +	std::string getURL(const std::string& topic) override;  	// return topic derived from viewer UI focus, else default topic  	std::string getTopicFromFocus();  	/// return default (fallback) topic name suitable for showTopic() -	/*virtual*/ std::string defaultTopic(); +	/*virtual*/ std::string defaultTopic() override;  	// return topic to use before the user logs in -	/*virtual*/ std::string preLoginTopic(); +	/*virtual*/ std::string preLoginTopic() override;  	// return topic to use for the top-level help, invoked by F1 -	/*virtual*/ std::string f1HelpTopic(); +	/*virtual*/ std::string f1HelpTopic() override;  };  #endif // header guard diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index f6d6d70b6b..5b4e1096f7 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -74,7 +74,7 @@ class LLViewerMedia: public LLSingleton<LLViewerMedia>  {  	LLSINGLETON(LLViewerMedia);  	~LLViewerMedia(); -	void initSingleton(); +	void initSingleton() override;  	LOG_CLASS(LLViewerMedia);  public: diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index 0fabb50d6a..08774c2c98 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -54,10 +54,10 @@ public:  	void setHoverFace(LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero);  	void clearHover(); -	/*virtual*/ bool	getFocus(); -	/*virtual*/ bool	handleKey(KEY key, MASK mask, bool called_from_parent); -	/*virtual*/ bool	handleKeyUp(KEY key, MASK mask, bool called_from_parent); -	/*virtual*/ bool	handleUnicodeChar(llwchar uni_char, bool called_from_parent); +	bool	getFocus(); +	/*virtual*/ bool	handleKey(KEY key, MASK mask, bool called_from_parent) override; +	/*virtual*/ bool	handleKeyUp(KEY key, MASK mask, bool called_from_parent) override; +	/*virtual*/ bool	handleUnicodeChar(llwchar uni_char, bool called_from_parent) override;  	bool handleScrollWheel(const LLVector2& texture_coords, S32 clicks_x, S32 clicks_y);  	bool handleScrollWheel(S32 x, S32 y, S32 clicks_x, S32 clicks_y); @@ -92,12 +92,12 @@ public:  	LLUUID getControlsMediaID();      // The MoaP object wants keyup and keydown events.  Overridden to return true. -    virtual bool    wantsKeyUpKeyDown() const; -    virtual bool    wantsReturnKey() const; +    virtual bool    wantsKeyUpKeyDown() const override; +    virtual bool    wantsReturnKey() const override;  protected: -	/*virtual*/ void	onFocusReceived(); -	/*virtual*/ void	onFocusLost(); +	/*virtual*/ void	onFocusReceived() override; +	/*virtual*/ void	onFocusLost() override;  private: diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 2415402ebd..24196cbd54 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1438,6 +1438,30 @@ class LLAdvancedCheckDebugViews : public view_listener_t +/////////////////// +// DEBUG UNICODE // +/////////////////// + + +class LLAdvancedToggleDebugUnicode : public view_listener_t +{ +	bool handleEvent(const LLSD& userdata) +	{ +		LLView::sDebugUnicode = !(LLView::sDebugUnicode); +		return true; +	} +}; + +class LLAdvancedCheckDebugUnicode : public view_listener_t +{ +	bool handleEvent(const LLSD& userdata) +	{ +		return LLView::sDebugUnicode; +	} +}; + + +  ///////////////////////  // XUI NAME TOOLTIPS //  /////////////////////// @@ -8576,23 +8600,8 @@ void handle_show_url(const LLSD& param)  void handle_report_bug(const LLSD& param)  { -	LLUIString url(param.asString()); -	 -	LLStringUtil::format_map_t replace; -	std::string environment = LLAppViewer::instance()->getViewerInfoString(true); -	boost::regex regex; -	regex.assign("</?nolink>"); -	std::string stripped_env = boost::regex_replace(environment, regex, ""); - -	replace["[ENVIRONMENT]"] = LLURI::escape(stripped_env); -	LLSLURL location_url; -	LLAgentUI::buildSLURL(location_url); -	replace["[LOCATION]"] = LLURI::escape(location_url.getSLURLString()); - -	LLUIString file_bug_url = gSavedSettings.getString("ReportBugURL"); -	file_bug_url.setArgs(replace); - -	LLWeb::loadURLExternal(file_bug_url.getString()); +    std::string url = gSavedSettings.getString("ReportBugURL"); +    LLWeb::loadURLExternal(url);  }  void handle_buy_currency_test(void*) @@ -9592,6 +9601,8 @@ void initialize_menus()  	view_listener_t::addMenu(new LLAdvancedCheckDebugClicks(), "Advanced.CheckDebugClicks");  	view_listener_t::addMenu(new LLAdvancedCheckDebugViews(), "Advanced.CheckDebugViews");  	view_listener_t::addMenu(new LLAdvancedToggleDebugViews(), "Advanced.ToggleDebugViews"); +	view_listener_t::addMenu(new LLAdvancedCheckDebugUnicode(), "Advanced.CheckDebugUnicode"); +	view_listener_t::addMenu(new LLAdvancedToggleDebugUnicode(), "Advanced.ToggleDebugUnicode");  	view_listener_t::addMenu(new LLAdvancedToggleXUINameTooltips(), "Advanced.ToggleXUINameTooltips");  	view_listener_t::addMenu(new LLAdvancedCheckXUINameTooltips(), "Advanced.CheckXUINameTooltips");  	view_listener_t::addMenu(new LLAdvancedToggleDebugMouseEvents(), "Advanced.ToggleDebugMouseEvents"); @@ -9691,7 +9702,11 @@ void initialize_menus()  	//Develop (clear cache immediately)  	commit.add("Develop.ClearCache", boost::bind(&handle_cache_clear_immediately) ); -     + +	// Develop (Fonts debugging) +	commit.add("Develop.Fonts.Dump", boost::bind(&LLFontGL::dumpFonts)); +	commit.add("Develop.Fonts.DumpTextures", boost::bind(&LLFontGL::dumpFontTextures)); +  	// Admin >Object  	view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy");  	view_listener_t::addMenu(new LLAdminHandleObjectOwnerSelf(), "Admin.HandleObjectOwnerSelf"); diff --git a/indra/newview/llviewerparcelaskplay.h b/indra/newview/llviewerparcelaskplay.h index dc711917d2..56faddae66 100644 --- a/indra/newview/llviewerparcelaskplay.h +++ b/indra/newview/llviewerparcelaskplay.h @@ -34,8 +34,8 @@ class LLViewerParcelAskPlay : public LLSingleton<LLViewerParcelAskPlay>  {      LLSINGLETON(LLViewerParcelAskPlay);      ~LLViewerParcelAskPlay(); -    void initSingleton(); -    void cleanupSingleton(); +    void initSingleton() override; +    void cleanupSingleton() override;  public:      // functor expects functor(region_id, parcel_id, url, play/stop)      typedef boost::function<void(const LLUUID&, const S32&, const std::string&, const bool&)> ask_callback; diff --git a/indra/newview/llviewerparcelmedia.h b/indra/newview/llviewerparcelmedia.h index 779a65bdf8..790b2b71fc 100644 --- a/indra/newview/llviewerparcelmedia.h +++ b/indra/newview/llviewerparcelmedia.h @@ -74,7 +74,7 @@ public:  	void sendMediaNavigateMessage(const std::string& url);  	// inherited from LLViewerMediaObserver -	virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); +	virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) override;  private:  	void processParcelMediaCommandMessage(LLMessageSystem *msg); diff --git a/indra/newview/llviewerparcelmediaautoplay.h b/indra/newview/llviewerparcelmediaautoplay.h index 49d7d76411..990906fedc 100644 --- a/indra/newview/llviewerparcelmediaautoplay.h +++ b/indra/newview/llviewerparcelmediaautoplay.h @@ -35,7 +35,7 @@ class LLViewerParcelMediaAutoPlay : LLEventTimer, public LLSingleton<LLViewerPar  {  	LLSINGLETON(LLViewerParcelMediaAutoPlay);  public: -	virtual bool tick(); +	virtual bool tick() override;  	static void playStarted();   private: diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 48475f19ce..cf53d07f2f 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -244,9 +244,9 @@ class LLUIImageList : public LLImageProviderInterface, public LLSingleton<LLUIIm  	LLSINGLETON_EMPTY_CTOR(LLUIImageList);  public:  	// LLImageProviderInterface -	/*virtual*/ LLPointer<LLUIImage> getUIImageByID(const LLUUID& id, S32 priority); -	/*virtual*/ LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority); -	void cleanUp(); +	/*virtual*/ LLPointer<LLUIImage> getUIImageByID(const LLUUID& id, S32 priority) override; +	/*virtual*/ LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority) override; +	void cleanUp() override;  	bool initFromFile(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 36b7fbdc8c..26e7ad9358 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -955,8 +955,7 @@ public:  		{  			const Line& line = *iter;  			LLFontGL::getFontMonospace()->renderUTF8(line.text, 0, (F32)line.x, (F32)line.y, mTextColor, -											 LLFontGL::LEFT, LLFontGL::TOP, -											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false); +					LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);  		}  	} @@ -3019,6 +3018,7 @@ bool LLViewerWindow::handleKey(KEY key, MASK mask)  					case KEY_PAGE_UP:  					case KEY_PAGE_DOWN:  					case KEY_HOME: +					case KEY_END:  						// when chatbar is empty or ArrowKeysAlwaysMove set,  						// pass arrow keys on to avatar...  						return false; diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 5add85986c..5b15acd8cd 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -170,12 +170,12 @@ class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoice  	LLSINGLETON(LLVoiceChannelProximal);  public: -	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); -	/*virtual*/ void handleStatusChange(EStatusType status); -	/*virtual*/ void handleError(EStatusType status); -	/*virtual*/ bool isActive(); -	/*virtual*/ void activate(); -	/*virtual*/ void deactivate(); +	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal) override; +	/*virtual*/ void handleStatusChange(EStatusType status) override; +	/*virtual*/ void handleError(EStatusType status) override; +	/*virtual*/ bool isActive() override; +	/*virtual*/ void activate() override; +	/*virtual*/ void deactivate() override;  }; @@ -184,15 +184,15 @@ class LLVoiceChannelP2P : public LLVoiceChannelGroup  public:  	LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id); -	/*virtual*/ void handleStatusChange(EStatusType status); -	/*virtual*/ void handleError(EStatusType status); -    /*virtual*/ void activate(); -	/*virtual*/ void getChannelInfo(); +	/*virtual*/ void handleStatusChange(EStatusType status) override; +	/*virtual*/ void handleError(EStatusType status) override; +    /*virtual*/ void activate() override; +	/*virtual*/ void getChannelInfo() override;  	void setSessionHandle(const std::string& handle, const std::string &inURI);  protected: -	virtual void setState(EState state); +	virtual void setState(EState state) override;  private: diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 6dae9e4040..bf1cec7d89 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -64,26 +64,26 @@ public:  	/// @name LLVoiceModuleInterface virtual implementations  	///  @see LLVoiceModuleInterface  	//@{ -	virtual void init(LLPumpIO *pump);	// Call this once at application startup (creates connector) -	virtual void terminate();	// Call this to clean up during shutdown +	virtual void init(LLPumpIO *pump) override;	// Call this once at application startup (creates connector) +	virtual void terminate() override;	// Call this to clean up during shutdown -	virtual const LLVoiceVersionInfo& getVersion(); +	virtual const LLVoiceVersionInfo& getVersion() override; -	virtual void updateSettings(); // call after loading settings and whenever they change +	virtual void updateSettings() override; // call after loading settings and whenever they change  	// Returns true if vivox has successfully logged in and is not in error state	 -	virtual bool isVoiceWorking() const; +	virtual bool isVoiceWorking() const override;  	/////////////////////  	/// @name Tuning  	//@{ -	virtual void tuningStart(); -	virtual void tuningStop(); -	virtual bool inTuningMode(); +	virtual void tuningStart() override; +	virtual void tuningStop() override; +	virtual bool inTuningMode() override; -	virtual void tuningSetMicVolume(float volume); -	virtual void tuningSetSpeakerVolume(float volume); -	virtual float tuningGetEnergy(void); +	virtual void tuningSetMicVolume(float volume) override; +	virtual void tuningSetSpeakerVolume(float volume) override; +	virtual float tuningGetEnergy(void) override;  	//@}  	///////////////////// @@ -91,40 +91,40 @@ public:  	//@{  	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.  	// i.e. when the daemon is running and connected, and the device lists are populated. -	virtual bool deviceSettingsAvailable(); -	virtual bool deviceSettingsUpdated();  //return if the list has been updated and never fetched,  only to be called from the voicepanel. +	virtual bool deviceSettingsAvailable() override; +	virtual bool deviceSettingsUpdated() override;  //return if the list has been updated and never fetched,  only to be called from the voicepanel.  	// Requery the vivox daemon for the current list of input/output devices.  	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed  	// (use this if you want to know when it's done).  	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. -	virtual void refreshDeviceLists(bool clearCurrentList = true); +	virtual void refreshDeviceLists(bool clearCurrentList = true) override; -	virtual void setCaptureDevice(const std::string& name); -	virtual void setRenderDevice(const std::string& name); +	virtual void setCaptureDevice(const std::string& name) override; +	virtual void setRenderDevice(const std::string& name) override; -	virtual LLVoiceDeviceList& getCaptureDevices(); -	virtual LLVoiceDeviceList& getRenderDevices(); +	virtual LLVoiceDeviceList& getCaptureDevices() override; +	virtual LLVoiceDeviceList& getRenderDevices() override;  	//@}	 -	virtual void getParticipantList(std::set<LLUUID> &participants); -	virtual bool isParticipant(const LLUUID& speaker_id); +	virtual void getParticipantList(std::set<LLUUID> &participants) override; +	virtual bool isParticipant(const LLUUID& speaker_id) override;  	// Send a text message to the specified user, initiating the session if necessary.  	// virtual bool sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;};  	// close any existing text IM session with the specified user -	virtual void endUserIMSession(const LLUUID &uuid); +	virtual void endUserIMSession(const LLUUID &uuid) override;  	// Returns true if calling back the session URI after the session has closed is possible.  	// Currently this will be false only for PSTN P2P calls.		  	// NOTE: this will return true if the session can't be found.  -	virtual bool isSessionCallBackPossible(const LLUUID &session_id); +	virtual bool isSessionCallBackPossible(const LLUUID &session_id) override;  	// Returns true if the session can accepte text IM's.  	// Currently this will be false only for PSTN P2P calls.  	// NOTE: this will return true if the session can't be found.  -	virtual bool isSessionTextIMPossible(const LLUUID &session_id); +	virtual bool isSessionTextIMPossible(const LLUUID &session_id) override;  	//////////////////////////// @@ -132,21 +132,21 @@ public:  	//@{  	// returns true iff the user is currently in a proximal (local spatial) channel.  	// Note that gestures should only fire if this returns true. -	virtual bool inProximalChannel(); +	virtual bool inProximalChannel() override;  	virtual void setNonSpatialChannel(const std::string &uri, -									  const std::string &credentials); +									  const std::string &credentials) override;  	virtual bool setSpatialChannel(const std::string &uri, -								   const std::string &credentials); +								   const std::string &credentials) override; -	virtual void leaveNonSpatialChannel(); +	virtual void leaveNonSpatialChannel() override; -	virtual void leaveChannel(void);	 +	virtual void leaveChannel(void) override;  	// Returns the URI of the current channel, or an empty string if not currently in a channel.  	// NOTE that it will return an empty string if it's in the process of joining a channel. -	virtual std::string getCurrentChannel(); +	virtual std::string getCurrentChannel() override;  	//@} @@ -154,59 +154,59 @@ public:  	/// @name invitations  	//@{  	// start a voice channel with the specified user -	virtual void callUser(const LLUUID &uuid);	 -	virtual bool isValidChannel(std::string &channelHandle); -	virtual bool answerInvite(std::string &channelHandle); -	virtual void declineInvite(std::string &channelHandle); +	virtual void callUser(const LLUUID &uuid) override; +	virtual bool isValidChannel(std::string &channelHandle) override; +	virtual bool answerInvite(std::string &channelHandle) override; +	virtual void declineInvite(std::string &channelHandle) override;  	//@}  	/////////////////////////  	/// @name Volume/gain  	//@{ -	virtual void setVoiceVolume(F32 volume); -	virtual void setMicGain(F32 volume); +	virtual void setVoiceVolume(F32 volume) override; +	virtual void setMicGain(F32 volume) override;  	//@}  	/////////////////////////  	/// @name enable disable voice and features  	//@{ -	virtual bool voiceEnabled(); -	virtual void setVoiceEnabled(bool enabled); -	virtual bool lipSyncEnabled();	 -	virtual void setLipSyncEnabled(bool enabled); -	virtual void setMuteMic(bool muted);		// Set the mute state of the local mic. +	virtual bool voiceEnabled() override; +	virtual void setVoiceEnabled(bool enabled) override; +	virtual bool lipSyncEnabled() override; +	virtual void setLipSyncEnabled(bool enabled) override; +	virtual void setMuteMic(bool muted) override;		// Set the mute state of the local mic.  	//@}  	//////////////////////////  	/// @name nearby speaker accessors  	//@{ -	virtual bool getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar -	virtual std::string getDisplayName(const LLUUID& id); -	virtual bool isParticipantAvatar(const LLUUID &id); -	virtual bool getIsSpeaking(const LLUUID& id); -	virtual bool getIsModeratorMuted(const LLUUID& id); -	virtual F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... -	virtual bool getOnMuteList(const LLUUID& id); -	virtual F32 getUserVolume(const LLUUID& id); -	virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)	 +	virtual bool getVoiceEnabled(const LLUUID& id) override;		// true if we've received data for this avatar +	virtual std::string getDisplayName(const LLUUID& id) override; +	virtual bool isParticipantAvatar(const LLUUID &id) override; +	virtual bool getIsSpeaking(const LLUUID& id) override; +	virtual bool getIsModeratorMuted(const LLUUID& id) override; +	virtual F32 getCurrentPower(const LLUUID& id) override;		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... +	virtual bool getOnMuteList(const LLUUID& id) override; +	virtual F32 getUserVolume(const LLUUID& id) override; +	virtual void setUserVolume(const LLUUID& id, F32 volume) override; // set's volume for specified agent, from 0-1 (where .5 is nominal)  	//@}  	// authorize the user  	virtual void userAuthorized(const std::string& user_id, -								const LLUUID &agentID); +								const LLUUID &agentID) override;  	//////////////////////////////  	/// @name Status notification  	//@{ -	virtual void addObserver(LLVoiceClientStatusObserver* observer); -	virtual void removeObserver(LLVoiceClientStatusObserver* observer); -	virtual void addObserver(LLFriendObserver* observer); -	virtual void removeObserver(LLFriendObserver* observer);		 -	virtual void addObserver(LLVoiceClientParticipantObserver* observer); -	virtual void removeObserver(LLVoiceClientParticipantObserver* observer); +	virtual void addObserver(LLVoiceClientStatusObserver* observer) override; +	virtual void removeObserver(LLVoiceClientStatusObserver* observer) override; +	virtual void addObserver(LLFriendObserver* observer) override; +	virtual void removeObserver(LLFriendObserver* observer) override; +	virtual void addObserver(LLVoiceClientParticipantObserver* observer) override; +	virtual void removeObserver(LLVoiceClientParticipantObserver* observer) override;  	//@} -	virtual std::string sipURIFromID(const LLUUID &id); +	virtual std::string sipURIFromID(const LLUUID &id) override;  	//@}  	/// @name LLVoiceEffectInterface virtual implementations @@ -216,32 +216,32 @@ public:  	//////////////////////////  	/// @name Accessors  	//@{ -	virtual bool setVoiceEffect(const LLUUID& id); -	virtual const LLUUID getVoiceEffect(); -	virtual LLSD getVoiceEffectProperties(const LLUUID& id); +	virtual bool setVoiceEffect(const LLUUID& id) override; +	virtual const LLUUID getVoiceEffect() override; +	virtual LLSD getVoiceEffectProperties(const LLUUID& id) override; -	virtual void refreshVoiceEffectLists(bool clear_lists); -	virtual const voice_effect_list_t& getVoiceEffectList() const; -	virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; +	virtual void refreshVoiceEffectLists(bool clear_lists) override; +	virtual const voice_effect_list_t& getVoiceEffectList() const override; +	virtual const voice_effect_list_t& getVoiceEffectTemplateList() const override;  	//@}  	//////////////////////////////  	/// @name Status notification  	//@{ -	virtual void addObserver(LLVoiceEffectObserver* observer); -	virtual void removeObserver(LLVoiceEffectObserver* observer); +	virtual void addObserver(LLVoiceEffectObserver* observer) override; +	virtual void removeObserver(LLVoiceEffectObserver* observer) override;  	//@}  	//////////////////////////////  	/// @name Effect preview buffer  	//@{ -	virtual void enablePreviewBuffer(bool enable); -	virtual void recordPreviewBuffer(); -	virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); -	virtual void stopPreviewBuffer(); +	virtual void enablePreviewBuffer(bool enable) override; +	virtual void recordPreviewBuffer() override; +	virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) override; +	virtual void stopPreviewBuffer() override; -	virtual bool isPreviewRecording(); -	virtual bool isPreviewPlaying(); +	virtual bool isPreviewRecording() override; +	virtual bool isPreviewPlaying() override;  	//@}  	//@} @@ -750,7 +750,7 @@ private:  	std::string getAudioSessionURI();  	std::string getAudioSessionHandle(); -    void setHidden(bool hidden); //virtual +    void setHidden(bool hidden) override; //virtual  	void sendPositionAndVolumeUpdate(void);      void sendCaptureAndRenderDevices(); diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index e3fa732658..4918b39141 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -428,7 +428,7 @@ public:  	{  		LLSINGLETON(ContextMenu);  	public: -		/*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y); +		/*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) override;  		void show(LLView* spawning_view, LLWearableType::EType w_type, S32 x, S32 y); @@ -441,7 +441,7 @@ public:  			MASK_UNKNOWN		= 0x10,  		}; -		/* virtual */ LLContextMenu* createMenu(); +		/* virtual */ LLContextMenu* createMenu() override;  		void updateItemsVisibility(LLContextMenu* menu);  		void updateItemsLabels(LLContextMenu* menu);  		static void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val); @@ -472,7 +472,7 @@ public:  	virtual ~LLWearableItemsList(); -	/*virtual*/ LLPanel* createNewItem(LLViewerInventoryItem* item); +	/*virtual*/ LLPanel* createNewItem(LLViewerInventoryItem* item) override;  	void updateList(const LLUUID& category_id); diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h index 524adba652..31dbfb8ffd 100644 --- a/indra/newview/llwindebug.h +++ b/indra/newview/llwindebug.h @@ -40,9 +40,9 @@ class LLWinDebug:  {  	LLSINGLETON_EMPTY_CTOR(LLWinDebug);  public: -	void initSingleton(); +	void initSingleton() override;  	static void generateMinidump(struct _EXCEPTION_POINTERS *pExceptionInfo = NULL); -	void cleanupSingleton(); +	void cleanupSingleton() override;  private:  	static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename);  }; diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index c5dac2be72..a90839ae7e 100755 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -520,7 +520,7 @@ void LLWorldMapView::draw()  					S32_MAX, //max_chars  					mMapScale, //max_pixels  					NULL, -					true); //use ellipses +					/*use_ellipses*/true);  			}  		}  	} diff --git a/indra/newview/skins/default/textures/icons/emoji_picker_icon.png b/indra/newview/skins/default/textures/icons/emoji_picker_icon.pngBinary files differ new file mode 100644 index 0000000000..ad4f3fa63c --- /dev/null +++ b/indra/newview/skins/default/textures/icons/emoji_picker_icon.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 2126db32df..1a49ec7e24 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -206,6 +206,7 @@ with the same filename but different name    <texture name="DropTarget" file_name="widgets/DropTarget.png" preload="false" /> +  <texture name="Emoji_Picker_Icon" file_name="icons/emoji_picker_icon.png" preload="true" />    <texture name="ExternalBrowser_Off" file_name="icons/ExternalBrowser_Off.png" preload="false" />    <texture name="Edit_Wrench" file_name="icons/Edit_Wrench.png" preload="false" /> @@ -213,7 +214,7 @@ with the same filename but different name    <texture name="Presets_Icon" file_name="icons/Presets_Icon.png" preload="true" />    <texture name="Presets_Icon_Graphic" file_name="icons/Presets_Icon_Graphic.png" preload="true" /> - <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" /> +  <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />    <texture name="Favorite_Star_Off" file_name="navbar/Favorite_Star_Off.png" preload="false" />    <texture name="Favorite_Star_Press" file_name="navbar/Favorite_Star_Press.png" preload="false" />    <texture name="Favorite_Star_Over" file_name="navbar/Favorite_Star_Over.png" preload="false" /> @@ -342,7 +343,7 @@ with the same filename but different name    <texture name="Inv_Underpants" file_name="icons/Inv_Underpants.png" preload="false" />    <texture name="Inv_Undershirt" file_name="icons/Inv_Undershirt.png" preload="false" />    <texture name="Inv_Link" file_name="icons/Inv_Link.png" preload="false" /> -    <texture name="Inv_Settings" file_name="icons/Inv_Settings.png" preload="false" /> +  <texture name="Inv_Settings" file_name="icons/Inv_Settings.png" preload="false" />    <texture name="Inv_SettingsSky" file_name="icons/Inv_SettingsSky.png" preload="false" />    <texture name="Inv_SettingsWater" file_name="icons/Inv_SettingsWater.png" preload="false" />    <texture name="Inv_SettingsDay" file_name="icons/Inv_SettingsDay.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/da/emoji_categories.xml b/indra/newview/skins/default/xui/da/emoji_categories.xml new file mode 100644 index 0000000000..456b18e4e2 --- /dev/null +++ b/indra/newview/skins/default/xui/da/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys and følelser</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>mennesker and krop</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>komponenter</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>dyr and natur</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>mad and drikke</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>rejser and steder</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>oplevelser</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objekter</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symboler</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/de/emoji_categories.xml b/indra/newview/skins/default/xui/de/emoji_categories.xml new file mode 100644 index 0000000000..ed63d0bac9 --- /dev/null +++ b/indra/newview/skins/default/xui/de/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>Smileys and Emotionen</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>Menschen and Körper</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>Komponenten</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>Tiere and Natur</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>Essen and Trinken</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>Reisen and Orte</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>Aktivitäten</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>Gegenstände</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>Symbole</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/en/emoji_categories.xml b/indra/newview/skins/default/xui/en/emoji_categories.xml new file mode 100644 index 0000000000..0315d0c43a --- /dev/null +++ b/indra/newview/skins/default/xui/en/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys and emotion</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>people and body</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>components</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animals and nature</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>food and drink</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>travel and places</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>activities</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objects</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symbols</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/en/floater_activeim.xml b/indra/newview/skins/default/xui/en/floater_activeim.xml index b79c5d9a19..42c3e7e935 100644 --- a/indra/newview/skins/default/xui/en/floater_activeim.xml +++ b/indra/newview/skins/default/xui/en/floater_activeim.xml @@ -23,7 +23,7 @@  		<scrolling_panel_list  			follows="left|right"  			layout="topleft" -      left="1"       +			left="1"  			name="chiclet_row_panel_list"  			width="318"/>  	</scroll_container> diff --git a/indra/newview/skins/default/xui/en/floater_emoji_complete.xml b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml new file mode 100644 index 0000000000..d290d647e8 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + name="emoji_complete" + single_instance="true" + layout="topleft" + bg_opaque_image="Window_NoTitle_Foreground" + bg_alpha_image="Window_NoTitle_Background" + can_close="false" + can_dock="false" + can_drag_on_left="false" + can_minimize="false" + can_resize="false" + can_tear_off="false" + header_height="0" + legacy_header_height="0" + show_title="false" + width="240" + height="40" + > +	<emoji_complete +	 name="emoji_complete_ctrl" +	 follows="top|left" +	 layout="topleft" +	 autosize="true" +	 vertical="true" +	 max_visible="7" +	 padding="4" +	 width="230" +	 height="30" +	 left="5" +	 top="5" +	 > +	</emoji_complete> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml new file mode 100644 index 0000000000..d21f8c82bc --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater +    name="emojipicker" +    title="CHOOSE EMOJI" +    help_topic="emojipicker" +    single_instance="true" +    can_minimize="false" +    can_tear_off="false" +    can_resize="true" +    auto_close="true" +    layout="topleft" +    min_width="250" +    chrome="true" +    height="350" +    width="304"> +  <floater.string name="title_for_recently_used" value="Recently used"/> +  <floater.string name="title_for_frequently_used" value="Frequently used"/> +  <scroll_container +      name="EmojiGridContainer" +      layout="topleft" +      follows="all" +      ignore_arrow_keys="true" +      top="25" +      left="0" +      height="275"> +    <scrolling_panel_list +        name="EmojiGrid" +        layout="topleft" +        follows="top|left|right" +        padding="4" +        spacing="0" +        top="0" +        left="0"/> +  </scroll_container> +  <panel +      name="Groups" +      layout="topleft" +      follows="top|left|right" +      top="0" +      left="0" +      height="25"> +    <panel +      name="Badge" +      layout="bottomleft" +      follows="bottom|left" +      background_visible="true" +      background_opaque="true" +      bg_opaque_color="FrogGreen" +      tab_stop="false" +      bottom="0" +      height="2" +      width="20" +      /> +  </panel> +  <text +      name="Dummy" +      type="string" +      layout="bottomleft" +      follows="bottom|left|right" +      halign="center" +      valign="center" +      bottom="14" +      left="10" +      height="25">No emoji selected</text> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index da84fbeea6..a6493c5e24 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -238,7 +238,7 @@                  </layout_stack>              </layout_panel>              <layout_panel -             height="35" +             height="30"               auto_resize="false"               name="chat_layout_panel">                  <layout_stack @@ -249,7 +249,7 @@                   name="input_panels"                   top="0"                   bottom="-1" -                 left="0" +                 left="1"                   right="-1">                      <layout_panel                       name="input_editor_layout_panel"> @@ -260,7 +260,7 @@                           default_icon_name="Generic_Person"                           layout="topleft"                           left="3" -                         bottom="-9" +                         bottom="-4"                           visible="false"                           width="20" />                          <group_icon @@ -270,7 +270,7 @@                           default_icon_name="Generic_Group"                           layout="topleft"                           left="3" -                         bottom="-9" +                         bottom="-4"                           visible="false"                           width="20" />                          <icon @@ -279,7 +279,7 @@                           image_name="Nearby_chat_icon"                           layout="topleft"                           left="3" -                         bottom="-9" +                         bottom="-4"                           name="nearby_chat_icon"                           visible="false"                           width="20"/> @@ -288,17 +288,31 @@                           expand_lines_count="5"                           follows="left|right|bottom"                           font="SansSerifSmall" -                         height="20"     +                         height="20"                           is_expandable="true"                           text_tentative_color="TextFgTentativeColor" +                         bg_writeable_color="ScriptBackground"                           name="chat_editor"                           max_length="1023"                           spellcheck="true"                           tab_group="3" -                         bottom="-8" +                         bottom="-3"                           left_pad="5" -                         right="-5" +                         right="-30"                           wrap="true" /> +                        <button +                         name="emoji_recent_panel_toggle_btn" +                         tool_tip="Shows/hides recent emojis" +                         follows="right|bottom" +                         font="EmojiLarge" +                         image_hover_unselected="Toolbar_Middle_Over" +                         image_selected="Toolbar_Middle_Selected" +                         image_unselected="Toolbar_Middle_Off" +                         image_overlay="Emoji_Picker_Icon" +                         bottom="-2" +                         right="-1" +                         height="25" +                         width="25"/>                      </layout_panel>                      <layout_panel                       auto_resize="false" @@ -319,6 +333,42 @@                      </layout_panel>                  </layout_stack>              </layout_panel> +            <layout_panel +             name="emoji_recent_layout_panel" +             height="30" +             auto_resize="false"> +                <text +                 name="emoji_recent_empty_text" +                 follows="top|left|right" +                 layout="topleft" +                 auto_resize="false" +                 h_pad="20" +                 v_pad="10" +                 top="0" +                 left="1" +                 right="-65" +                 height="30" +                >Recently used emojis will appear here</text> +                <emoji_complete +                 name="emoji_recent_icons_ctrl" +                 follows="top|left|right" +                 layout="topleft" +                 max_visible="20" +                 top="0" +                 left="1" +                 right="-65" +                 height="30"/> +                <button +                 name="emoji_picker_show_btn" +                 label="More" +                 tool_tip="Shows/hides emoji picker" +                 follows="right|bottom" +                 layout="topleft" +                 bottom="-5" +                 right="-3" +                 height="20" +                 width="60"/> +            </layout_panel>          </layout_stack>      </view>  </floater> diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml index dcbdfa8794..ac5467c036 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml @@ -73,6 +73,8 @@       spellcheck="true"       tab_group="1"       top="46" +     use_color="true" +     show_emoji_helper="true"       width="392"       word_wrap="true">          Loading... diff --git a/indra/newview/skins/default/xui/en/fonts.xml b/indra/newview/skins/default/xui/en/fonts.xml index d88c267a95..40045625fd 100644 --- a/indra/newview/skins/default/xui/en/fonts.xml +++ b/indra/newview/skins/default/xui/en/fonts.xml @@ -3,6 +3,7 @@    <font name="default" comment="default font files (global fallbacks)">      <file>DejaVuSans.ttf</file> +    <file functor="is_emoji">TwemojiSVG.ttf</file>      <os name="Windows">        <file>meiryo.TTC</file>        <file>MSGOTHIC.TTC</file> @@ -69,6 +70,11 @@      <file>DejaVuSans-BoldOblique.ttf</file>    </font> +  <font name="Emoji" +	comment="Name of emoji font"> +    <file>TwemojiSVG.ttf</file> +  </font> +    <font name="Monospace"  	comment="Name of monospace font">      <file>DejaVuSansMono.ttf</file> diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index 96fac1c6e8..40399b33ef 100644 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -161,6 +161,32 @@        <menu_item_separator />        <menu         create_jump_keys="true" +       label="Fonts" +       name="Fonts" +       tear_off="true"> +        <menu_item_call +         label="Show Font Test" +         name="Show Font Test"> +          <menu_item_call.on_click +           function="Floater.Show" +           parameter="font_test" /> +        </menu_item_call> +        <menu_item_separator /> +        <menu_item_call +         label="Dump Fonts" +         name="Dump Fonts"> +          <menu_item_call.on_click +           function="Develop.Fonts.Dump" /> +        </menu_item_call> +        <menu_item_call +         label="Dump Font Textures" +         name="Dump Font Textures"> +          <menu_item_call.on_click +           function="Develop.Fonts.DumpTextures" /> +        </menu_item_call> +      </menu> +      <menu +       create_jump_keys="true"         label="UI Tests"         name="UI Tests"         tear_off="true"> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 660f4b62c7..38763cd9a8 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3531,6 +3531,18 @@ function="World.EnvPreset"               parameter="http://duckduckgo.com"/>            </menu_item_call>            <menu_item_call +           label="Dump Fonts" +           name="Dump Fonts"> +            <menu_item_call.on_click +             function="Develop.Fonts.Dump" /> +          </menu_item_call> +          <menu_item_call +           label="Dump Font Textures" +           name="Dump Font Textures"> +            <menu_item_call.on_click +             function="Develop.Fonts.DumpTextures" /> +          </menu_item_call> +          <menu_item_call               label="Dump SelectMgr"               name="Dump SelectMgr">                  <menu_item_call.on_click @@ -3599,6 +3611,14 @@ function="World.EnvPreset"                   function="Advanced.ToggleDebugViews" />              </menu_item_check>              <menu_item_check +             label="Debug Unicode" +             name="Debug Unicode"> +                <menu_item_check.on_check +                 function="Advanced.CheckDebugUnicode" /> +                <menu_item_check.on_click +                 function="Advanced.ToggleDebugUnicode" /> +            </menu_item_check> +            <menu_item_check               label="Debug Name Tooltips"               name="Debug Name Tooltips">                  <menu_item_check.on_check @@ -4405,7 +4425,7 @@ function="World.EnvPreset"                      <menu_item_call.on_click                       function="PromptShowURL"                       name="PublicIssueTracker_url" -                     parameter="WebLaunchPublicIssue,http://jira.secondlife.com" /> +                     parameter="WebLaunchPublicIssue,https://feedback.secondlife.com/" />                  </menu_item_call>                  <menu_item_call                   label="Public Issue Tracker Help" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index 4b20afe0c4..b59d035e99 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -27,7 +27,7 @@      left_delta="110"      name="preset_text"      top="5" -    width="120"> +    width="320">        (None)    </text> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 5d33853adc..3fbb5bc7e8 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -491,9 +491,9 @@ http://secondlife.com/support for help fixing this problem.  	<!-- build floater -->  	<string name="multiple_textures">Multiple</string> -<string name="use_texture">Use texture</string> -    <string name="manip_hint1">Move mouse cursor over ruler</string> -    <string name="manip_hint2">to snap to grid</string> +	<string name="use_texture">Use texture</string> +	<string name="manip_hint1">Move mouse cursor over ruler</string> +	<string name="manip_hint2">to snap to grid</string>  	<!-- world map -->  	<string name="texture_loading">Loading...</string> @@ -508,14 +508,14 @@ http://secondlife.com/support for help fixing this problem.  	<!-- Chat -->  	<string name="NearbyChatTitle">Nearby chat</string> -  <string name="NearbyChatLabel">(Nearby chat)</string> +	<string name="NearbyChatLabel">(Nearby chat)</string>  	<string name="whisper">whispers:</string>  	<string name="shout">shouts:</string>  	<string name="ringing">Connecting to in-world Voice Chat...</string>  	<string name="connected">Connected</string>  	<string name="unavailable">Voice not available at your current location</string>  	<string name="hang_up">Disconnected from in-world Voice Chat</string> -  <string name="reconnect_nearby">You will now be reconnected to Nearby Voice Chat</string> +	<string name="reconnect_nearby">You will now be reconnected to Nearby Voice Chat</string>  	<string name="ScriptQuestionCautionChatGranted">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been granted permission to: [PERMISSIONS].</string>  	<string name="ScriptQuestionCautionChatDenied">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been denied permission to: [PERMISSIONS].</string>  	<string name="AdditionalPermissionsRequestHeader">If you allow access to your account, you will also be allowing the object to:</string> diff --git a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml index f9facb593a..c550f634e5 100644 --- a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml @@ -1,4 +1,7 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <chat_editor    name="chat_editor" -  show_context_menu="true"/> +  show_context_menu="true" +  show_emoji_helper="true" +  use_color="true" +  /> diff --git a/indra/newview/skins/default/xui/en/widgets/chat_history.xml b/indra/newview/skins/default/xui/en/widgets/chat_history.xml index c0a948931c..c4300c9350 100644 --- a/indra/newview/skins/default/xui/en/widgets/chat_history.xml +++ b/indra/newview/skins/default/xui/en/widgets/chat_history.xml @@ -10,11 +10,11 @@    bottom_separator_pad="1"    top_header_pad="12"    bottom_header_pad="5" -	max_length="2147483647" -	track_bottom="true" -	name="chat_history" -	type="string" -	word_wrap="true" +  max_length="2147483647" +  track_bottom="true" +  name="chat_history" +  type="string" +  word_wrap="true"    line_spacing.multiple="1.0"     font="SansSerif">    <more_chat_text diff --git a/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml new file mode 100644 index 0000000000..6cc8d7118f --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<emoji_complete +  autosize="false" +  hover_image="ListItem_Over" +  selected_image="ListItem_Select" +  max_visible="7" +  padding="8" +  > +</emoji_complete> diff --git a/indra/newview/skins/default/xui/es/emoji_categories.xml b/indra/newview/skins/default/xui/es/emoji_categories.xml new file mode 100644 index 0000000000..b1b73eba5e --- /dev/null +++ b/indra/newview/skins/default/xui/es/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>emoticonos y emoción</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>personas y cuerpo</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>componentes</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animales y la naturaleza</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>comida y bebida</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>viajes y lugares</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>actividades</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objetos</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>símbolos</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/fr/emoji_categories.xml b/indra/newview/skins/default/xui/fr/emoji_categories.xml new file mode 100644 index 0000000000..38dc9cb8f8 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys et émotion</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>les gens et le corps</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>composants</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animaux et la nature</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>nourriture et boissons</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>voyages et lieux</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>activités</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objets</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symboles</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/it/emoji_categories.xml b/indra/newview/skins/default/xui/it/emoji_categories.xml new file mode 100644 index 0000000000..a4782e60a6 --- /dev/null +++ b/indra/newview/skins/default/xui/it/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>smileys and emozione</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>persone e corpo</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>componenti</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animali and natura</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>cibo e bevande</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>viaggi and luoghi</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>attività</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>oggetti</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>simboli</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/ja/emoji_categories.xml b/indra/newview/skins/default/xui/ja/emoji_categories.xml new file mode 100644 index 0000000000..7750f4ad2e --- /dev/null +++ b/indra/newview/skins/default/xui/ja/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>スマイリーと感情</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>人体</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>コンポーネント</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>動物自然</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>飲み物・食べ物</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>旅行・場所</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>有効化</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>オブジェクト</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>シンボル</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/pl/emoji_categories.xml b/indra/newview/skins/default/xui/pl/emoji_categories.xml new file mode 100644 index 0000000000..9aad7af794 --- /dev/null +++ b/indra/newview/skins/default/xui/pl/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>buźki and emocje</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>ludzie and ciało</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>składniki</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>zwierzęta and przyroda</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>jedzenie i picie</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>podróże and miejsca</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>aktywność</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objekt</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>symbole</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/pt/emoji_categories.xml b/indra/newview/skins/default/xui/pt/emoji_categories.xml new file mode 100644 index 0000000000..887444b957 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>sorrisos e emoção</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>pessoas e corpo</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>componentes</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>animais e natureza</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>comida e bebida</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>viagens e lugares</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>atividades</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>objetos</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>símbolos</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/ru/emoji_categories.xml b/indra/newview/skins/default/xui/ru/emoji_categories.xml new file mode 100644 index 0000000000..b08f0d8117 --- /dev/null +++ b/indra/newview/skins/default/xui/ru/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>смайлики и люди</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>тело людей</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>компонент</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>животные и природа</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>еда и напитки</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>путешествия и местности</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>варианты досуга</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>предметы</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>символы</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/skins/default/xui/zh/emoji_categories.xml b/indra/newview/skins/default/xui/zh/emoji_categories.xml new file mode 100644 index 0000000000..fbe6165eeb --- /dev/null +++ b/indra/newview/skins/default/xui/zh/emoji_categories.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> +  <array> +    <map> +      <key>Name</key> +      <string>smileys and emotion</string> +      <key>Category</key> +      <string>笑脸</string> +    </map> +    <map> +      <key>Name</key> +      <string>people and body</string> +      <key>Category</key> +      <string>人体</string> +    </map> +    <map> +      <key>Name</key> +      <string>components</string> +      <key>Category</key> +      <string>组件</string> +    </map> +    <map> +      <key>Name</key> +      <string>animals and nature</string> +      <key>Category</key> +      <string>野生动物</string> +    </map> +    <map> +      <key>Name</key> +      <string>food and drink</string> +      <key>Category</key> +      <string>食物飲料</string> +    </map> +    <map> +      <key>Name</key> +      <string>travel and places</string> +      <key>Category</key> +      <string>旅遊地點</string> +    </map> +    <map> +      <key>Name</key> +      <string>activities</string> +      <key>Category</key> +      <string>个人活动</string> +    </map> +    <map> +      <key>Name</key> +      <string>objects</string> +      <key>Category</key> +      <string>物件</string> +    </map> +    <map> +      <key>Name</key> +      <string>symbols</string> +      <key>Category</key> +      <string>人的符号</string> +    </map> +  </array> +</llsd> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 2d7e39e8cc..3fb0a02c23 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -141,7 +141,7 @@ class ViewerManifest(LLManifest):                  self.path("*.tga")              # Include our fonts -            with self.prefix(src_dst="fonts"): +            with self.prefix(src="../packages/fonts",src_dst="fonts"):                  self.path("*.ttf")                  self.path("*.txt") @@ -553,6 +553,10 @@ class Windows_x86_64_Manifest(ViewerManifest):                  self.path("OpenAL32.dll")                  self.path("alut.dll") +            # For ICU4C +            self.path("icudt48.dll") +            self.path("icuuc48.dll") +              # For textures              self.path("openjp2.dll") diff --git a/indra/test/test.cpp b/indra/test/test.cpp index a265e1273b..94478a5263 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -54,6 +54,12 @@  #endif  #ifndef LL_WINDOWS + +typedef struct { +  void *re_pcre; +  size_t re_nsub; +  size_t re_erroffset; +} regex_t;  #include <gmock/gmock.h>  #include <gtest/gtest.h>  #endif | 
