summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/CMakeLists.txt3
-rw-r--r--indra/cmake/Copy3rdPartyLibs.cmake9
-rw-r--r--indra/cmake/ICU4C.cmake23
-rw-r--r--indra/cmake/ViewerMiscLibs.cmake3
-rw-r--r--indra/llappearance/llwearabletype.h2
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/llcoros.h2
-rw-r--r--indra/llcommon/llsingleton.h2
-rw-r--r--indra/llcommon/llstring.cpp175
-rw-r--r--indra/llcommon/llstring.h32
-rw-r--r--indra/llcommon/tests/llsingleton_test.cpp6
-rw-r--r--indra/llinventory/llfoldertype.cpp2
-rw-r--r--indra/llinventory/llinventorysettings.cpp2
-rw-r--r--indra/llmessage/llexperiencecache.h2
-rw-r--r--indra/llmessage/llproxy.h2
-rw-r--r--indra/llrender/CMakeLists.txt2
-rw-r--r--indra/llrender/llfontbitmapcache.cpp163
-rw-r--r--indra/llrender/llfontbitmapcache.h42
-rw-r--r--indra/llrender/llfontfreetype.cpp290
-rw-r--r--indra/llrender/llfontfreetype.h31
-rw-r--r--indra/llrender/llfontfreetypesvg.cpp205
-rw-r--r--indra/llrender/llfontfreetypesvg.h54
-rw-r--r--indra/llrender/llfontgl.cpp98
-rw-r--r--indra/llrender/llfontgl.h23
-rw-r--r--indra/llrender/llfontregistry.cpp165
-rw-r--r--indra/llrender/llfontregistry.h43
-rw-r--r--indra/llrender/llgl.cpp2
-rw-r--r--indra/llui/CMakeLists.txt4
-rw-r--r--indra/llui/llbutton.cpp23
-rw-r--r--indra/llui/llbutton.h5
-rw-r--r--indra/llui/llemojidictionary.cpp469
-rw-r--r--indra/llui/llemojidictionary.h126
-rw-r--r--indra/llui/llemojihelper.cpp169
-rw-r--r--indra/llui/llemojihelper.h66
-rw-r--r--indra/llui/llfloater.cpp182
-rw-r--r--indra/llui/llfloater.h21
-rw-r--r--indra/llui/llfolderviewitem.cpp19
-rw-r--r--indra/llui/lllineeditor.cpp20
-rw-r--r--indra/llui/lllineeditor.h114
-rw-r--r--indra/llui/llmenugl.cpp5
-rw-r--r--indra/llui/llmenugl.h6
-rw-r--r--indra/llui/llnotifications.h6
-rw-r--r--indra/llui/llscrollbar.cpp6
-rw-r--r--indra/llui/llscrollcontainer.cpp23
-rw-r--r--indra/llui/llscrollcontainer.h4
-rw-r--r--indra/llui/llscrollingpanellist.cpp184
-rw-r--r--indra/llui/llscrollingpanellist.h39
-rw-r--r--indra/llui/llscrolllistctrl.cpp6
-rw-r--r--indra/llui/llscrolllistctrl.h30
-rw-r--r--indra/llui/llsearcheditor.cpp4
-rw-r--r--indra/llui/llspellcheck.h2
-rw-r--r--indra/llui/lltextbase.cpp152
-rw-r--r--indra/llui/lltextbase.h88
-rw-r--r--indra/llui/lltexteditor.cpp112
-rw-r--r--indra/llui/lltexteditor.h10
-rw-r--r--indra/llui/lluictrl.cpp25
-rw-r--r--indra/llui/lluictrl.h2
-rw-r--r--indra/llui/lluistring.h1
-rw-r--r--indra/llui/llview.cpp53
-rw-r--r--indra/llui/llview.h28
-rw-r--r--indra/llui/llviewquery.h12
-rw-r--r--indra/llwindow/llopenglview-objc.mm59
-rw-r--r--indra/newview/CMakeLists.txt28
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/emoji_groups.xml82
-rw-r--r--indra/newview/app_settings/settings.xml2
-rw-r--r--indra/newview/fonts/DejaVu-license.txt187
-rw-r--r--indra/newview/fonts/DejaVuSans-Bold.ttfbin705684 -> 0 bytes
-rw-r--r--indra/newview/fonts/DejaVuSans-BoldOblique.ttfbin643292 -> 0 bytes
-rw-r--r--indra/newview/fonts/DejaVuSans-Oblique.ttfbin635416 -> 0 bytes
-rw-r--r--indra/newview/fonts/DejaVuSans.ttfbin757076 -> 0 bytes
-rw-r--r--indra/newview/fonts/DejaVuSansMono.ttfbin340712 -> 0 bytes
-rw-r--r--indra/newview/llautoreplace.h2
-rw-r--r--indra/newview/llchannelmanager.h2
-rw-r--r--indra/newview/llchathistory.cpp64
-rw-r--r--indra/newview/llchicletbar.h4
-rw-r--r--indra/newview/llconversationlog.h10
-rw-r--r--indra/newview/llexpandabletextbox.cpp2
-rw-r--r--indra/newview/llfeaturemanager.h2
-rw-r--r--indra/newview/llfloateravatarpicker.cpp1
-rw-r--r--indra/newview/llfloateremojipicker.cpp1346
-rw-r--r--indra/newview/llfloateremojipicker.h122
-rw-r--r--indra/newview/llfloaterimnearbychat.cpp7
-rw-r--r--indra/newview/llfloaterimsession.cpp2
-rw-r--r--indra/newview/llfloaterimsessiontab.cpp376
-rw-r--r--indra/newview/llfloaterimsessiontab.h47
-rw-r--r--indra/newview/llfloateruipreview.cpp8
-rw-r--r--indra/newview/llfriendcard.h2
-rw-r--r--indra/newview/llgesturemgr.h4
-rw-r--r--indra/newview/llhudrender.cpp2
-rw-r--r--indra/newview/llimagefiltersmanager.h2
-rw-r--r--indra/newview/llimview.h2
-rw-r--r--indra/newview/llinventorygallery.cpp13
-rw-r--r--indra/newview/llmutelist.h2
-rwxr-xr-xindra/newview/llnavigationbar.h8
-rw-r--r--indra/newview/lloutfitobserver.h2
-rw-r--r--indra/newview/llpanelemojicomplete.cpp579
-rw-r--r--indra/newview/llpanelemojicomplete.h132
-rw-r--r--indra/newview/llpaneltopinfobar.h6
-rw-r--r--indra/newview/llpathfindingpathtool.h18
-rw-r--r--indra/newview/llproductinforequest.h2
-rw-r--r--indra/newview/llrecentpeople.h2
-rw-r--r--indra/newview/llsceneview.cpp1
-rw-r--r--indra/newview/llselectmgr.cpp7
-rw-r--r--indra/newview/llspeakers.h4
-rw-r--r--indra/newview/llspeakingindicatormanager.cpp4
-rw-r--r--indra/newview/lltextureview.cpp3
-rw-r--r--indra/newview/lltoolbrush.h20
-rw-r--r--indra/newview/lltoolcomp.h72
-rw-r--r--indra/newview/lltooldraganddrop.h12
-rw-r--r--indra/newview/lltoolface.h10
-rw-r--r--indra/newview/lltoolfocus.h14
-rw-r--r--indra/newview/lltoolindividual.h8
-rw-r--r--indra/newview/lltoolobjpicker.h14
-rw-r--r--indra/newview/lltoolpie.h32
-rw-r--r--indra/newview/lltoolpipette.h8
-rw-r--r--indra/newview/lltoolselectland.h16
-rw-r--r--indra/newview/llversioninfo.h2
-rw-r--r--indra/newview/llviewerchat.cpp2
-rw-r--r--indra/newview/llviewerfloaterreg.cpp20
-rw-r--r--indra/newview/llviewerhelp.h10
-rw-r--r--indra/newview/llviewermedia.h2
-rw-r--r--indra/newview/llviewermediafocus.h16
-rw-r--r--indra/newview/llviewermenu.cpp51
-rw-r--r--indra/newview/llviewerparcelaskplay.h4
-rw-r--r--indra/newview/llviewerparcelmedia.h2
-rw-r--r--indra/newview/llviewerparcelmediaautoplay.h2
-rw-r--r--indra/newview/llviewertexturelist.h6
-rw-r--r--indra/newview/llviewerwindow.cpp4
-rw-r--r--indra/newview/llvoicechannel.h22
-rw-r--r--indra/newview/llvoicevivox.h144
-rw-r--r--indra/newview/llvotree.cpp4
-rw-r--r--indra/newview/llwearableitemslist.h6
-rw-r--r--indra/newview/llwindebug.h4
-rwxr-xr-xindra/newview/llworldmapview.cpp2
-rw-r--r--indra/newview/skins/default/textures/icons/emoji_picker_icon.pngbin0 -> 921 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml5
-rw-r--r--indra/newview/skins/default/xui/da/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/de/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/en/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/en/floater_activeim.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_emoji_complete.xml34
-rw-r--r--indra/newview/skins/default/xui/en/floater_emoji_picker.xml65
-rw-r--r--indra/newview/skins/default/xui/en/floater_im_session.xml66
-rw-r--r--indra/newview/skins/default/xui/en/floater_preview_notecard.xml2
-rw-r--r--indra/newview/skins/default/xui/en/fonts.xml6
-rw-r--r--indra/newview/skins/default/xui/en/menu_login.xml26
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml22
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml2
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml10
-rw-r--r--indra/newview/skins/default/xui/en/widgets/chat_editor.xml5
-rw-r--r--indra/newview/skins/default/xui/en/widgets/chat_history.xml10
-rw-r--r--indra/newview/skins/default/xui/en/widgets/emoji_complete.xml9
-rw-r--r--indra/newview/skins/default/xui/es/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/fr/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/it/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/ja/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/pl/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/pt/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/ru/emoji_categories.xml59
-rw-r--r--indra/newview/skins/default/xui/zh/emoji_categories.xml59
-rwxr-xr-xindra/newview/viewer_manifest.py6
-rw-r--r--indra/test/test.cpp6
163 files changed, 6572 insertions, 1413 deletions
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 1fd83eadff..05c51c018d 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -30,6 +30,7 @@ set(cmake_SOURCE_FILES
GoogleMock.cmake
Havok.cmake
Hunspell.cmake
+ ICU4C.cmake
JsonCpp.cmake
LLAddBuildTest.cmake
LLAppearance.cmake
@@ -64,7 +65,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 7938d4f54b..9f79c13a97 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -62,6 +62,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 793a33cc87..1fbe19ddd1 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 5f4ed2fffa..c947184dc8 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)
@@ -282,6 +283,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 fba8301c4d..64027c16c7 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 f6629803ee..82dc7c9f80 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 1fd6cac14a..bfbf25d9ab 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -189,6 +189,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);
@@ -355,6 +357,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);
@@ -678,6 +682,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.
@@ -737,6 +743,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
@@ -1594,6 +1603,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 d2c3b419ab..4856fe4619 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 c71e24c83a..42b0045cf3 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 7de3a6b56f..c63281ab70 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 e964d1586f..d87fb5245c 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,10 +115,25 @@ 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()
: mFontBitmapCachep(new LLFontBitmapCache),
mAscender(0.f),
@@ -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)
-{
- mFallbackFonts = font;
-}
-
-const LLFontFreetype::font_vector_t &LLFontFreetype::getFallbackFonts() const
+void LLFontFreetype::addFallbackFont(const LLPointer<LLFontFreetype>& fallback_font, const char_functor_t& functor)
{
- 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 FALSE;
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,17 +775,46 @@ 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);
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 f61f169987..b036d337ba 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 15fddbc99f..53661b6a63 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 9bbf9ce0c4..f6ec416c8b 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 9750bd4fa1..d2c7e466e6 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 4c7c8e6f5c..9d3abf32bb 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 49d275997a..9ef019840a 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 ccd31e90c0..257159f64f 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 2303cd24b7..de3de53569 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 3d15708295..88f9e77777 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,6 +315,9 @@ 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());
@@ -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 0dc66bf37a..bc9469cfad 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 60dbfd68c6..453fa29e7c 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 f983828d2b..5794b3c35a 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 cebca70b59..fececa15cf 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 87e3f18ebc..5c51cf8465 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 921398a693..4d9a33f1d7 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 735e2d529e..b2be9fb1e1 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 ad32f7186c..22d27b1f2a 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 dacea2a987..79dc70cac9 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 b6f2eb8ba2..3a819e7d06 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 e8df176ec3..d625039427 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 219667f766..f982dc99e8 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 73b4fb036a..326589a329 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,18 +398,20 @@ 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; }
BOOL hasSortOrder() const;
void clearSortOrder();
- void setAlternateSort() { mAlternateSort = true; }
+ 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 bafeef41fb..cfaf08ec0a 100644
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
@@ -178,6 +178,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 e0697cb454..7ccf025a19 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 3611ab0499..9d3c54fbee 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 3d2a426913..092739a538 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 f3939248c2..521405ec25 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 21afcae7c3..7eb9ae69fb 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 be1c7dd0b6..cffb9933d4 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 da7868d804..139eb17efa 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 7360fd0fb9..6e16d41cba 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 21bb1be26f..4bc9c4a08e 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 04d3b64d5c..6b15e847a3 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -18,6 +18,7 @@ include(DragDrop)
include(EXPAT)
include(FMODSTUDIO)
include(Hunspell)
+include(ICU4C)
include(JPEGEncoderBasic)
include(JsonCpp)
include(LLAppearance)
@@ -207,6 +208,7 @@ set(viewer_SOURCE_FILES
llfloaterdisplayname.cpp
llfloatereditenvironmentbase.cpp
llfloatereditextdaycycle.cpp
+ llfloateremojipicker.cpp
llfloaterenvironmentadjust.cpp
llfloaterevent.cpp
llfloaterexperiencepicker.cpp
@@ -419,6 +421,7 @@ set(viewer_SOURCE_FILES
llpaneleditsky.cpp
llpaneleditwater.cpp
llpaneleditwearable.cpp
+ llpanelemojicomplete.cpp
llpanelenvironment.cpp
llpanelexperiencelisteditor.cpp
llpanelexperiencelog.cpp
@@ -860,6 +863,7 @@ set(viewer_HEADER_FILES
llfloaterdisplayname.h
llfloatereditenvironmentbase.h
llfloatereditextdaycycle.h
+ llfloateremojipicker.h
llfloaterenvironmentadjust.h
llfloaterevent.h
llfloaterexperiencepicker.h
@@ -1064,6 +1068,7 @@ set(viewer_HEADER_FILES
llpaneleditsky.h
llpaneleditwater.h
llpaneleditwearable.h
+ llpanelemojicomplete.h
llpanelenvironment.h
llpanelexperiencelisteditor.h
llpanelexperiencelog.h
@@ -1931,6 +1936,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${LLPHYSICSEXTENSIONS_LIBRARIES}
ll::bugsplat
ll::tracy
+ ll::icu4c
)
if( TARGET ll::intel_memops )
@@ -1944,6 +1950,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 e707e1eb5e..9d4f4cf911 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11343,7 +11343,7 @@
<key>Type</key>
<string>String</string>
<key>Value</key>
- <string>https://jira.secondlife.com/secure/CreateIssueDetails!init.jspa?pid=10610&amp;issuetype=1&amp;environment=[ENVIRONMENT]&amp;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.ttf
deleted file mode 100644
index 6d65fa7dc4..0000000000
--- a/indra/newview/fonts/DejaVuSans-Bold.ttf
+++ /dev/null
Binary files differ
diff --git a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf b/indra/newview/fonts/DejaVuSans-BoldOblique.ttf
deleted file mode 100644
index 753f2d80b1..0000000000
--- a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf
+++ /dev/null
Binary files differ
diff --git a/indra/newview/fonts/DejaVuSans-Oblique.ttf b/indra/newview/fonts/DejaVuSans-Oblique.ttf
deleted file mode 100644
index 999bac7714..0000000000
--- a/indra/newview/fonts/DejaVuSans-Oblique.ttf
+++ /dev/null
Binary files differ
diff --git a/indra/newview/fonts/DejaVuSans.ttf b/indra/newview/fonts/DejaVuSans.ttf
deleted file mode 100644
index e5f7eecce4..0000000000
--- a/indra/newview/fonts/DejaVuSans.ttf
+++ /dev/null
Binary files differ
diff --git a/indra/newview/fonts/DejaVuSansMono.ttf b/indra/newview/fonts/DejaVuSansMono.ttf
deleted file mode 100644
index f5786022f1..0000000000
--- a/indra/newview/fonts/DejaVuSansMono.ttf
+++ /dev/null
Binary files differ
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 43dc10ef5f..b9bf432581 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/llchicletbar.h b/indra/newview/llchicletbar.h
index 6c521dc1d5..c295b99962 100644
--- a/indra/newview/llchicletbar.h
+++ b/indra/newview/llchicletbar.h
@@ -43,11 +43,11 @@ class LLChicletBar
public:
- BOOL postBuild();
+ BOOL postBuild() override;
LLChicletPanel* getChicletPanel() { return mChicletPanel; }
- /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent) override;
/**
diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h
index 820a5db491..54aeedcf9a 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 3395777aab..2d332f75f5 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 651404d890..70c6c09e0d 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 2422596f60..42ef41017a 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..1578caa39c
--- /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..5d0402ca83
--- /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 bb4cc9bca3..40bdf14deb 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 ee9dc35283..ed2a2807b5 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 0b0dce29fb..24cc398f3b 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()
@@ -101,7 +104,7 @@ LLFloaterIMSessionTab::~LLFloaterIMSessionTab()
delete mRefreshTimer;
}
-//static
+// static
LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uuid)
{
LLFloaterIMSessionTab* conv;
@@ -118,7 +121,7 @@ LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uui
return conv;
};
-//static
+// static
LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid)
{
LLFloaterIMSessionTab* conv;
@@ -134,14 +137,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);
}
@@ -155,27 +160,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))
@@ -220,42 +224,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");
@@ -307,17 +329,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);
@@ -357,6 +379,7 @@ LLParticipantList* LLFloaterIMSessionTab::getParticipantList()
return dynamic_cast<LLParticipantList*>(LLFloaterIMContainer::getInstance()->getSessionModel(mSessionID));
}
+// virtual
void LLFloaterIMSessionTab::draw()
{
if (mRefreshTimer->hasExpired())
@@ -381,23 +404,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);
@@ -407,13 +431,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()
@@ -426,53 +451,130 @@ 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");
@@ -502,10 +604,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++;
}
}
@@ -525,10 +627,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
{
@@ -578,11 +680,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)
@@ -638,7 +740,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();
@@ -766,7 +868,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)
@@ -783,12 +885,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?
@@ -817,10 +919,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);
}
@@ -846,7 +948,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);
}
}
@@ -892,15 +994,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();
}
}
@@ -937,12 +1039,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);
@@ -967,7 +1069,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);
@@ -995,8 +1097,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");
@@ -1086,8 +1188,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
@@ -1099,8 +1201,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
@@ -1111,8 +1213,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
@@ -1122,19 +1224,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()
@@ -1142,8 +1244,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 d478922617..cc985b2753 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 67a205417e..db69c3e2c3 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 7c8e8279c2..7bb60f00e2 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 dff310ecf9..c1f17c9d33 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 946eb02f26..bace97d37a 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 68581d04eb..d4ca58f778 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -2402,11 +2402,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);
@@ -2416,12 +2422,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 14840f1b2e..dda407e708 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 11c671294a..4649f5bcb0 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..e6e3a10e13
--- /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..36a965202e
--- /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/llpaneltopinfobar.h b/indra/newview/llpaneltopinfobar.h
index 78dd997029..b6c263e331 100644
--- a/indra/newview/llpaneltopinfobar.h
+++ b/indra/newview/llpaneltopinfobar.h
@@ -46,8 +46,8 @@ class LLPanelTopInfoBar : public LLPanel, public LLSingleton<LLPanelTopInfoBar>,
public:
typedef boost::signals2::signal<void ()> resize_signal_t;
- /*virtual*/ BOOL postBuild();
- /*virtual*/ void draw();
+ /*virtual*/ BOOL postBuild() override;
+ /*virtual*/ void draw() override;
/**
* Updates location and parcel icons on login complete
@@ -83,7 +83,7 @@ private:
*/
void initParcelIcons();
- BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override;
/**
* Handles clicks on the parcel icons.
diff --git a/indra/newview/llpathfindingpathtool.h b/indra/newview/llpathfindingpathtool.h
index 88cb3a15f8..f98624e30d 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 9b1d2d48c6..2643ee95f8 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 30e12faca5..620dcad760 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -5526,9 +5526,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";
@@ -5660,7 +5657,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
@@ -5678,7 +5674,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());
@@ -5695,7 +5690,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;
}
}
@@ -5709,7 +5703,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 ed795b5155..22c9481687 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 0111d8869c..c4c9673be3 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 62703e3499..84cd6e2da7 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -588,8 +588,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 c108d83256..6545ee3611 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 86506f725e..f539a045b7 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 db2fe87f20..60a2f01107 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 e4b8ae12b8..7c8ff20480 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 ef71f9230a..54d9827ae6 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 e7c2060fba..89dd9d9796 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 5ad9b67e21..a55cd223de 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 8f6100e4b4..dca0d12cf6 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 7575d8ad18..2636811c66 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 b5ba72f16d..88bc4e2e3d 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 f1f42afd81..ad7c4bcefa 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 effd08a559..2310e4dbfc 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 981984db6a..972aac0baf 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -1437,6 +1437,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 //
///////////////////////
@@ -8523,23 +8547,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*)
@@ -9539,6 +9548,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");
@@ -9638,7 +9649,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 d71fd4c075..e83085dee0 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 e8dd96daee..10a2cfa32a 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 0aae6f4b35..58024fb60e 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);
}
}
@@ -3000,6 +2999,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 309c3eebdd..e68bfbe1ff 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 e3ab99c675..ae2aec0e9c 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/llvotree.cpp b/indra/newview/llvotree.cpp
index 36e6da802b..575b1dbe7e 100644
--- a/indra/newview/llvotree.cpp
+++ b/indra/newview/llvotree.cpp
@@ -865,6 +865,10 @@ BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
mReferenceBuffer->unmapBuffer();
llassert(vertex_count == max_vertices);
llassert(index_count == max_indices);
+#ifndef SHOW_ASSERT
+ (void)vertex_count;
+ (void)index_count;
+#endif
}
//generate tree mesh
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index f7774a7086..0dbe50d5e4 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 cc947c87d5..a8676d2ad6 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.png
new file mode 100644
index 0000000000..ad4f3fa63c
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/emoji_picker_icon.png
Binary files differ
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 9b238693e0..1d012cd348 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3538,6 +3538,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
@@ -3606,6 +3618,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
@@ -4412,7 +4432,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 fe74cea2f1..8a9d3b755e 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 e405d9ea10..f3b26820d2 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -490,9 +490,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>
@@ -507,14 +507,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 1fa4df1682..c7f32d0da9 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")
@@ -559,6 +559,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