diff options
Diffstat (limited to 'indra')
171 files changed, 14805 insertions, 2928 deletions
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 4d70089737..31cd5d770d 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -27,6 +27,7 @@ set(cmake_SOURCE_FILES FindAutobuild.cmake FindGLH.cmake FindHUNSPELL.cmake + FindICU4C.cmake FindJsonCpp.cmake FindNDOF.cmake FindOpenJPEG.cmake @@ -42,6 +43,7 @@ set(cmake_SOURCE_FILES GoogleMock.cmake Havok.cmake Hunspell.cmake + ICU4C.cmake JPEG.cmake JsonCpp.cmake LLAddBuildTest.cmake diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ff705101de..439bec865c 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -61,6 +61,15 @@ if(WINDOWS) uriparser.dll ) + # ICU4C (same filenames for 32 and 64 bit builds) + set(release_files ${release_files} icudt48.dll) + set(release_files ${release_files} icuin48.dll) + set(release_files ${release_files} icuio48.dll) + set(release_files ${release_files} icule48.dll) + set(release_files ${release_files} iculx48.dll) + set(release_files ${release_files} icutu48.dll) + set(release_files ${release_files} icuuc48.dll) + # OpenSSL if(ADDRESS_SIZE EQUAL 64) set(release_files ${release_files} libcrypto-1_1-x64.dll) diff --git a/indra/cmake/FindICU4C.cmake b/indra/cmake/FindICU4C.cmake new file mode 100644 index 0000000000..327d761a88 --- /dev/null +++ b/indra/cmake/FindICU4C.cmake @@ -0,0 +1,33 @@ +# -*- cmake -*- + +# - Find ICU4C +# This module defines +# ICU4C_INCLUDE_DIR, where to find headers +# ICU4C_LIBRARY, the library needed to use ICU4C. +# ICU4C_FOUND, If false, do not try to use ICU4C. + +find_path(ICU4C_INCLUDE_DIR uchar.h + PATH_SUFFIXES unicode + ) + +set(ICU4C_NAMES ${ICU4C_NAMES} icuuc) +find_library(ICU4C_LIBRARY + NAMES ${ICU4C_NAMES} + ) + +if (ICU4C_LIBRARY AND ICU4C_INCLUDE_DIR) + set(ICU4C_FOUND "YES") +else (ICU4C_LIBRARY AND ICU4C_INCLUDE_DIR) + set(ICU4C_FOUND "NO") +endif (ICU4C_LIBRARY AND ICU4C_INCLUDE_DIR) + +if (ICU4C_FOUND) + message(STATUS "Found ICU4C: Library in '${ICU4C_LIBRARY}' and header in '${ICU4C_INCLUDE_DIR}' ") +else (ICU4C_FOUND) + message(FATAL_ERROR " * * *\nCould not find ICU4C library! * * *") +endif (ICU4C_FOUND) + +mark_as_advanced( + ICU4C_LIBRARY + ICU4C_INCLUDE_DIR + ) diff --git a/indra/cmake/ICU4C.cmake b/indra/cmake/ICU4C.cmake new file mode 100644 index 0000000000..007a9b6937 --- /dev/null +++ b/indra/cmake/ICU4C.cmake @@ -0,0 +1,22 @@ +# -*- cmake -*- +include(Prebuilt) + +set(ICU4C_FIND_QUIETLY ON) +set(ICU4C_FIND_REQUIRED ON) + +if (USESYSTEMLIBS) + include(FindICU4C) +else (USESYSTEMLIBS) + use_prebuilt_binary(icu4c) + if (WINDOWS) + set(ICU4C_LIBRARY icuuc) + #elseif(DARWIN) + # set(ICU4C_LIBRARY ...) + #elseif(LINUX) + # set(ICU4C_LIBRARY ...) + else() + message(FATAL_ERROR "Invalid platform") + endif() + set(ICU4C_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/unicode) + use_prebuilt_binary(dictionaries) +endif (USESYSTEMLIBS) diff --git a/indra/cmake/ViewerMiscLibs.cmake b/indra/cmake/ViewerMiscLibs.cmake index fc5bdedb5a..9b365c159a 100644 --- a/indra/cmake/ViewerMiscLibs.cmake +++ b/indra/cmake/ViewerMiscLibs.cmake @@ -10,3 +10,5 @@ if (NOT USESYSTEMLIBS) use_prebuilt_binary(slvoice) endif(NOT USESYSTEMLIBS) +use_prebuilt_binary(nanosvg) +use_prebuilt_binary(viewer-fonts) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index df65828d02..3371306f11 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) @@ -108,7 +109,6 @@ set(llcommon_SOURCE_FILES llsys.cpp lltempredirect.cpp llthread.cpp - llthreadlocalstorage.cpp llthreadsafequeue.cpp lltimer.cpp lltrace.cpp @@ -305,6 +305,7 @@ target_link_libraries( ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} ${EXPAT_LIBRARIES} + ${ICU4C_LIBRARY} ${JSONCPP_LIBRARIES} ${ZLIBNG_LIBRARIES} ${WINDOWS_LIBRARIES} diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index db94765871..435531f86f 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -30,7 +30,6 @@ #include "llapr.h" #include "llmutex.h" #include "apr_dso.h" -#include "llthreadlocalstorage.h" apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool. @@ -54,7 +53,6 @@ void ll_init_apr() LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(FALSE) ; } - LLThreadLocalPointerBase::initAllThreadLocalStorage(); gAPRInitialized = true; } @@ -70,8 +68,6 @@ void ll_cleanup_apr() LL_DEBUGS("APR") << "Cleaning up APR" << LL_ENDL; - LLThreadLocalPointerBase::destroyAllThreadLocalStorage(); - if (gAPRPoolp) { apr_pool_destroy(gAPRPoolp); diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index f6629803ee..cda1791e45 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 @@ -833,6 +834,31 @@ std::string LLStringOps::sDayFormat; std::string LLStringOps::sAM; std::string LLStringOps::sPM; +// static +bool LLStringOps::isEmoji(llwchar wch) +{ + switch (ublock_getCode(wch)) + { + case UBLOCK_MISCELLANEOUS_SYMBOLS: + case UBLOCK_DINGBATS: + 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..9afbea9afe 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); diff --git a/indra/llcommon/llthreadlocalstorage.cpp b/indra/llcommon/llthreadlocalstorage.cpp deleted file mode 100644 index d8a063e8d5..0000000000 --- a/indra/llcommon/llthreadlocalstorage.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/** - * @file llthreadlocalstorage.cpp - * @author Richard - * @date 2013-1-11 - * @brief implementation of thread local storage utility classes - * - * $LicenseInfo:firstyear=2013&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 "llthreadlocalstorage.h" -#include "llapr.h" - -// -//LLThreadLocalPointerBase -// -bool LLThreadLocalPointerBase::sInitialized = false; - -void LLThreadLocalPointerBase::set( void* value ) -{ - llassert(sInitialized && mThreadKey); - - apr_status_t result = apr_threadkey_private_set((void*)value, mThreadKey); - if (result != APR_SUCCESS) - { - ll_apr_warn_status(result); - LL_ERRS() << "Failed to set thread local data" << LL_ENDL; - } -} - -void* LLThreadLocalPointerBase::get() const -{ - // llassert(sInitialized); - void* ptr; - apr_status_t result = - apr_threadkey_private_get(&ptr, mThreadKey); - if (result != APR_SUCCESS) - { - ll_apr_warn_status(result); - LL_ERRS() << "Failed to get thread local data" << LL_ENDL; - } - return ptr; -} - - -void LLThreadLocalPointerBase::initStorage( ) -{ - apr_status_t result = apr_threadkey_private_create(&mThreadKey, NULL, gAPRPoolp); - if (result != APR_SUCCESS) - { - ll_apr_warn_status(result); - LL_ERRS() << "Failed to allocate thread local data" << LL_ENDL; - } -} - -void LLThreadLocalPointerBase::destroyStorage() -{ - if (sInitialized) - { - if (mThreadKey) - { - apr_status_t result = apr_threadkey_private_delete(mThreadKey); - if (result != APR_SUCCESS) - { - ll_apr_warn_status(result); - LL_ERRS() << "Failed to delete thread local data" << LL_ENDL; - } - } - } -} - -//static -void LLThreadLocalPointerBase::initAllThreadLocalStorage() -{ - if (!sInitialized) - { - for (auto& base : instance_snapshot()) - { - base.initStorage(); - } - sInitialized = true; - } -} - -//static -void LLThreadLocalPointerBase::destroyAllThreadLocalStorage() -{ - if (sInitialized) - { - //for (auto& base : instance_snapshot()) - //{ - // base.destroyStorage(); - //} - sInitialized = false; - } -} diff --git a/indra/llcommon/llthreadlocalstorage.h b/indra/llcommon/llthreadlocalstorage.h index 3b5786023f..bdd28ec865 100644 --- a/indra/llcommon/llthreadlocalstorage.h +++ b/indra/llcommon/llthreadlocalstorage.h @@ -30,100 +30,6 @@ #include "llinstancetracker.h" -class LLThreadLocalPointerBase : public LLInstanceTracker<LLThreadLocalPointerBase> -{ -public: - LLThreadLocalPointerBase() - : mThreadKey(NULL) - { - if (sInitialized) - { - initStorage(); - } - } - - LLThreadLocalPointerBase( const LLThreadLocalPointerBase& other) - : mThreadKey(NULL) - { - if (sInitialized) - { - initStorage(); - } - } - - ~LLThreadLocalPointerBase() - { - destroyStorage(); - } - - static void initAllThreadLocalStorage(); - static void destroyAllThreadLocalStorage(); - -protected: - void set(void* value); - - void* get() const; - - void initStorage(); - void destroyStorage(); - -protected: - struct apr_threadkey_t* mThreadKey; - static bool sInitialized; -}; - -template <typename T> -class LLThreadLocalPointer : public LLThreadLocalPointerBase -{ -public: - - LLThreadLocalPointer() - {} - - explicit LLThreadLocalPointer(T* value) - { - set(value); - } - - - LLThreadLocalPointer(const LLThreadLocalPointer<T>& other) - : LLThreadLocalPointerBase(other) - { - set(other.get()); - } - - LL_FORCE_INLINE T* get() const - { - return (T*)LLThreadLocalPointerBase::get(); - } - - T* operator -> () const - { - return (T*)get(); - } - - T& operator*() const - { - return *(T*)get(); - } - - LLThreadLocalPointer<T>& operator = (T* value) - { - set((void*)value); - return *this; - } - - bool operator ==(const T* other) const - { - if (!sInitialized) return false; - return get() == other; - } - - bool isNull() const { return !sInitialized || get() == NULL; } - - bool notNull() const { return sInitialized && get() != NULL; } -}; - template<typename DERIVED_TYPE> class LLThreadLocalSingletonPointer { @@ -139,10 +45,10 @@ public: } private: - static LL_THREAD_LOCAL DERIVED_TYPE* sInstance; + static thread_local DERIVED_TYPE* sInstance; }; template<typename DERIVED_TYPE> -LL_THREAD_LOCAL DERIVED_TYPE* LLThreadLocalSingletonPointer<DERIVED_TYPE>::sInstance = NULL; +thread_local DERIVED_TYPE* LLThreadLocalSingletonPointer<DERIVED_TYPE>::sInstance = NULL; #endif // LL_LLTHREADLOCALSTORAGE_H diff --git a/indra/llcommon/lltrace.cpp b/indra/llcommon/lltrace.cpp index 7ad50d1288..ff671a8370 100644 --- a/indra/llcommon/lltrace.cpp +++ b/indra/llcommon/lltrace.cpp @@ -40,7 +40,7 @@ StatBase::StatBase( const char* name, const char* description ) mDescription(description ? description : "") { #ifndef LL_RELEASE_FOR_DOWNLOAD - if (LLTrace::get_thread_recorder().notNull()) + if (LLTrace::get_thread_recorder() != NULL) { LL_ERRS() << "Attempting to declare trace object after program initialization. Trace objects should be statically initialized." << LL_ENDL; } diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp index 7c38cdb7cd..6bd886ae98 100644 --- a/indra/llcommon/lltraceaccumulators.cpp +++ b/indra/llcommon/lltraceaccumulators.cpp @@ -93,7 +93,7 @@ void AccumulatorBufferGroup::makeCurrent() mStackTimers.makeCurrent(); mMemStats.makeCurrent(); - ThreadRecorder* thread_recorder = get_thread_recorder().get(); + ThreadRecorder* thread_recorder = get_thread_recorder(); AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers; // update stacktimer parent pointers for (size_t i = 0, end_i = mStackTimers.size(); i < end_i; i++) diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp index 8414b234e0..dd148dd08a 100644 --- a/indra/llcommon/lltracerecording.cpp +++ b/indra/llcommon/lltracerecording.cpp @@ -95,7 +95,7 @@ Recording::~Recording() // allow recording destruction without thread recorder running, // otherwise thread shutdown could crash if a recording outlives the thread recorder // besides, recording construction and destruction is fine without a recorder...just don't attempt to start one - if (isStarted() && LLTrace::get_thread_recorder().notNull()) + if (isStarted() && LLTrace::get_thread_recorder() != NULL) { LLTrace::get_thread_recorder()->deactivate(mBuffers.write()); } @@ -112,9 +112,9 @@ void Recording::update() // must have llassert(mActiveBuffers != NULL - && LLTrace::get_thread_recorder().notNull()); + && LLTrace::get_thread_recorder() != NULL); - if(!mActiveBuffers->isCurrent()) + if(!mActiveBuffers->isCurrent() && LLTrace::get_thread_recorder() != NULL) { AccumulatorBufferGroup* buffers = mBuffers.write(); LLTrace::get_thread_recorder()->deactivate(buffers); @@ -144,7 +144,7 @@ void Recording::handleStart() mSamplingTimer.reset(); mBuffers.setStayUnique(true); // must have thread recorder running on this thread - llassert(LLTrace::get_thread_recorder().notNull()); + llassert(LLTrace::get_thread_recorder() != NULL); mActiveBuffers = LLTrace::get_thread_recorder()->activate(mBuffers.write()); #endif } @@ -155,7 +155,7 @@ void Recording::handleStop() #if LL_TRACE_ENABLED mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); // must have thread recorder running on this thread - llassert(LLTrace::get_thread_recorder().notNull()); + llassert(LLTrace::get_thread_recorder() != NULL); LLTrace::get_thread_recorder()->deactivate(mBuffers.write()); mActiveBuffers = NULL; mBuffers.setStayUnique(false); @@ -1182,8 +1182,8 @@ void ExtendablePeriodicRecording::handleSplitTo(ExtendablePeriodicRecording& oth PeriodicRecording& get_frame_recording() { - static LLThreadLocalPointer<PeriodicRecording> sRecording(new PeriodicRecording(200, PeriodicRecording::STARTED)); - return *sRecording; + static thread_local PeriodicRecording sRecording(200, PeriodicRecording::STARTED); + return sRecording; } } diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp index d10312e0ec..4028a5ce97 100644 --- a/indra/llcommon/lltracethreadrecorder.cpp +++ b/indra/llcommon/lltracethreadrecorder.cpp @@ -308,13 +308,13 @@ ThreadRecorder* get_master_thread_recorder() return sMasterThreadRecorder; } -LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder_ptr() +ThreadRecorder*& get_thread_recorder_ptr() { - static LLThreadLocalPointer<ThreadRecorder> s_thread_recorder; + static thread_local ThreadRecorder* s_thread_recorder; return s_thread_recorder; } -const LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder() +ThreadRecorder* get_thread_recorder() { return get_thread_recorder_ptr(); } diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h index 1294d4318f..8ee6729ac6 100644 --- a/indra/llcommon/lltracethreadrecorder.h +++ b/indra/llcommon/lltracethreadrecorder.h @@ -32,7 +32,6 @@ #include "llmutex.h" #include "lltraceaccumulators.h" -#include "llthreadlocalstorage.h" namespace LLTrace { @@ -92,7 +91,7 @@ namespace LLTrace }; - const LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder(); + ThreadRecorder* get_thread_recorder(); void set_thread_recorder(ThreadRecorder*); void set_master_thread_recorder(ThreadRecorder*); diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index bac630f626..ed5334d31b 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -5013,6 +5013,17 @@ void LLVolumeFace::optimize(F32 angle_cutoff) { U16 index = mIndices[i]; + if (index >= mNumVertices) + { + // invalid index + // replace with a valid index to avoid crashes + index = mNumVertices - 1; + mIndices[i] = index; + + // Needs better logging + LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL; + } + LLVolumeFace::VertexData cv; getVertexData(index, cv); @@ -5385,6 +5396,17 @@ bool LLVolumeFace::cacheOptimize() U16 idx = mIndices[i]; U32 tri_idx = i / 3; + if (idx >= mNumVertices) + { + // invalid index + // replace with a valid index to avoid crashes + idx = mNumVertices - 1; + mIndices[i] = idx; + + // Needs better logging + LL_DEBUGS_ONCE("LLVOLUME") << "Invalid index, substituting" << LL_ENDL; + } + vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx])); vertex_data[idx].mIdx = idx; triangle_data[tri_idx].mVertex[i % 3] = &(vertex_data[idx]); diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 35dcbe3836..57ea954054 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -666,7 +666,6 @@ char const* const _PREHASH_GroupRolesCount = LLMessageStringTable::getInstance() char const* const _PREHASH_SimulatorBlock = LLMessageStringTable::getInstance()->getString("SimulatorBlock"); char const* const _PREHASH_GroupID = LLMessageStringTable::getInstance()->getString("GroupID"); char const* const _PREHASH_AgentVel = LLMessageStringTable::getInstance()->getString("AgentVel"); -char const* const _PREHASH_RequestImage = LLMessageStringTable::getInstance()->getString("RequestImage"); char const* const _PREHASH_NetStats = LLMessageStringTable::getInstance()->getString("NetStats"); char const* const _PREHASH_AgentPos = LLMessageStringTable::getInstance()->getString("AgentPos"); char const* const _PREHASH_AgentSit = LLMessageStringTable::getInstance()->getString("AgentSit"); @@ -1047,7 +1046,6 @@ char const* const _PREHASH_SortOrder = LLMessageStringTable::getInstance()->getS char const* const _PREHASH_Hunter = LLMessageStringTable::getInstance()->getString("Hunter"); char const* const _PREHASH_SunAngVelocity = LLMessageStringTable::getInstance()->getString("SunAngVelocity"); char const* const _PREHASH_BinaryBucket = LLMessageStringTable::getInstance()->getString("BinaryBucket"); -char const* const _PREHASH_ImagePacket = LLMessageStringTable::getInstance()->getString("ImagePacket"); char const* const _PREHASH_StartGroupProposal = LLMessageStringTable::getInstance()->getString("StartGroupProposal"); char const* const _PREHASH_EnergyLevel = LLMessageStringTable::getInstance()->getString("EnergyLevel"); char const* const _PREHASH_PriceForListing = LLMessageStringTable::getInstance()->getString("PriceForListing"); @@ -1236,7 +1234,6 @@ char const* const _PREHASH_ForceScriptControlRelease = LLMessageStringTable::get char const* const _PREHASH_ParcelRelease = LLMessageStringTable::getInstance()->getString("ParcelRelease"); char const* const _PREHASH_VFileType = LLMessageStringTable::getInstance()->getString("VFileType"); char const* const _PREHASH_EjectGroupMemberReply = LLMessageStringTable::getInstance()->getString("EjectGroupMemberReply"); -char const* const _PREHASH_ImageData = LLMessageStringTable::getInstance()->getString("ImageData"); char const* const _PREHASH_SimulatorViewerTimeMessage = LLMessageStringTable::getInstance()->getString("SimulatorViewerTimeMessage"); char const* const _PREHASH_Rotation = LLMessageStringTable::getInstance()->getString("Rotation"); char const* const _PREHASH_Selection = LLMessageStringTable::getInstance()->getString("Selection"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 3015f438b5..572dadd408 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -666,7 +666,6 @@ extern char const* const _PREHASH_GroupRolesCount; extern char const* const _PREHASH_SimulatorBlock; extern char const* const _PREHASH_GroupID; extern char const* const _PREHASH_AgentVel; -extern char const* const _PREHASH_RequestImage; extern char const* const _PREHASH_NetStats; extern char const* const _PREHASH_AgentPos; extern char const* const _PREHASH_AgentSit; @@ -1047,7 +1046,6 @@ extern char const* const _PREHASH_SortOrder; extern char const* const _PREHASH_Hunter; extern char const* const _PREHASH_SunAngVelocity; extern char const* const _PREHASH_BinaryBucket; -extern char const* const _PREHASH_ImagePacket; extern char const* const _PREHASH_StartGroupProposal; extern char const* const _PREHASH_EnergyLevel; extern char const* const _PREHASH_PriceForListing; diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 68654486a4..752a850d42 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -330,7 +330,10 @@ LLModel::EModelStatus load_face_from_dom_triangles( // VFExtents change face.mExtents[0].set(v[0], v[1], v[2]); face.mExtents[1].set(v[0], v[1], v[2]); - point_map.clear(); + + verts.clear(); + indices.clear(); + point_map.clear(); } } diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index a6ab96ab18..2aaaa4e8c1 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -57,6 +57,8 @@ public: mutable std::vector<S32> mJointNums; typedef std::vector<LLMatrix4a, boost::alignment::aligned_allocator<LLMatrix4a, 16>> matrix_list_t; matrix_list_t mInvBindMatrix; + + // bones/joints position overrides matrix_list_t mAlternateBindMatrix; LL_ALIGN_16(LLMatrix4a mBindShapeMatrix); diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index baab09a104..a82ab2e523 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -33,6 +33,7 @@ set(llrender_SOURCE_FILES llcubemap.cpp llfontbitmapcache.cpp llfontfreetype.cpp + llfontfreetypesvg.cpp llfontgl.cpp llfontregistry.cpp llgl.cpp @@ -60,6 +61,7 @@ set(llrender_HEADER_FILES llcubemap.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..8809f9f475 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,135 @@ 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); + S32 num_components = getNumComponents(bitmap_type); + mImageRawVec[bitmap_idx].push_back(new LLImageRaw(mBitmapWidth, mBitmapHeight, num_components)); + bitmap_num = mImageRawVec[bitmap_idx].size() - 1; - // 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) + 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..5535c07615 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,91 @@ 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(); + 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 +543,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 +583,86 @@ 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; + } + + llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, load_flags) ); llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) ); @@ -601,7 +672,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 +682,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 +706,7 @@ void LLFontFreetype::resetBitmapCache() if(!mIsFallback) { // Add the empty glyph - addGlyphFromFont(this, 0, 0); + addGlyphFromFont(this, 0, 0, EFontGlyphType::Grayscale); } } @@ -651,6 +720,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,9 +763,38 @@ 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)); 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 1bf061bc8d..001b7fd7b8 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; @@ -278,7 +278,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 +288,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 +296,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 +311,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 +345,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 +355,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 +409,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(); } @@ -423,19 +424,19 @@ S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, cons return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); } -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_MAX, S32_MAX, NULL, FALSE, FALSE); } 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, S32_MAX, S32_MAX, NULL, FALSE, FALSE); } // font metrics - override for LLFontFreetype that returns units of virtual pixels @@ -512,7 +513,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); @@ -532,7 +533,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. @@ -550,7 +551,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); } } @@ -624,7 +625,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) { @@ -649,7 +650,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); } @@ -696,7 +697,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 @@ -771,7 +772,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); @@ -801,7 +802,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); } @@ -841,6 +842,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 @@ -1003,6 +1024,13 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name) return gl_vfont_align; } + //static +LLFontGL* LLFontGL::getFontEmoji() +{ + static LLFontGL* fontp = getFont(LLFontDescriptor("Emoji", "Large", 0)); + return fontp;; +} + //static LLFontGL* LLFontGL::getFontMonospace() { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 3b58a37d33..915c2439a3 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 = FALSE) 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 = FALSE) 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 = FALSE) 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_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) 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; @@ -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,7 @@ public: static void setFontDisplay(BOOL flag) { sDisplayFont = flag; } + static LLFontGL* getFontEmoji(); static LLFontGL* getFontMonospace(); static LLFontGL* getFontSansSerifSmall(); static LLFontGL* getFontSansSerif(); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index bc1a2f8887..44f2bd4cc5 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(sys_path + 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/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index e0579352d3..44c61dcdbc 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -53,6 +53,8 @@ set(llui_SOURCE_FILES lldockcontrol.cpp lldraghandle.cpp lleditmenuhandler.cpp + llemojidictionary.cpp + llemojihelper.cpp llf32uictrl.cpp llfiltereditor.cpp llflashtimer.cpp @@ -163,6 +165,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/llchat.h b/indra/llui/llchat.h index c39e44200c..b4fd5f60aa 100644 --- a/indra/llui/llchat.h +++ b/indra/llui/llchat.h @@ -38,7 +38,8 @@ typedef enum e_chat_source_type CHAT_SOURCE_AGENT = 1, CHAT_SOURCE_OBJECT = 2, CHAT_SOURCE_TELEPORT = 3, - CHAT_SOURCE_UNKNOWN = 4 + CHAT_SOURCE_UNKNOWN = 4, + CHAT_SOURCE_REGION = 5, } EChatSourceType; typedef enum e_chat_type diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 08da599ef2..362fe0c19e 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -203,11 +203,9 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) // it will work fine in case of decrease of space, but if we get more space or text // becomes longer, label will fail to grow so reinit label's dimentions. - static LLUICachedControl<S32> llcheckboxctrl_hpad("UICheckboxctrlHPad", 0); LLRect label_rect = mLabel->getRect(); - S32 new_width = getRect().getWidth() - label_rect.mLeft - llcheckboxctrl_hpad; - label_rect.mRight = label_rect.mLeft + new_width; - mLabel->setRect(label_rect); + S32 new_width = rect.getWidth() - label_rect.mLeft; + mLabel->reshape(new_width, label_rect.getHeight(), TRUE); S32 label_top = label_rect.mTop; mLabel->reshapeToFitText(TRUE); diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp new file mode 100644 index 0000000000..b70a9b2e7a --- /dev/null +++ b/indra/llui/llemojidictionary.cpp @@ -0,0 +1,200 @@ +/** +* @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 +// + +constexpr char SKINNED_EMOJI_FILENAME[] = "emoji_characters.xml"; + +// ============================================================================ +// 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; +} + +LLEmojiDescriptor::LLEmojiDescriptor(const LLSD& descriptor_sd) +{ + Name = descriptor_sd["Name"].asStringRef(); + + const LLWString emoji_string = utf8str_to_wstring(descriptor_sd["Character"].asString()); + Character = (1 == emoji_string.size()) ? emoji_string[0] : L'\0'; // We don't currently support character composition + + auto toLower = [](std::string& str) { LLStringUtil::toLower(str); }; + ShortCodes = llsd_array_to_list<std::string>(descriptor_sd["ShortCodes"], toLower); + Categories = llsd_array_to_list<std::string>(descriptor_sd["Categories"], toLower); + + if (Name.empty()) + { + Name = ShortCodes.front(); + } +} + +bool LLEmojiDescriptor::isValid() const +{ + return + Character && + !ShortCodes.empty() && + !Categories.empty(); +} + +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; + } + + for (const auto& category : descr.Categories) + { + if (boost::icontains(category, mNeedle)) + return true; + } + + return false; + } +}; + +// ============================================================================ +// LLEmojiDictionary class +// + +LLEmojiDictionary::LLEmojiDictionary() +{ +} + +// static +void LLEmojiDictionary::initClass() +{ + LLEmojiDictionary* pThis = &LLEmojiDictionary::initParamSingleton(); + + LLSD data; + + const std::string filename = gDirUtilp->findSkinnedFilenames(LLDir::XUI, SKINNED_EMOJI_FILENAME, LLDir::CURRENT_SKIN).front(); + llifstream file(filename.c_str()); + if (file.is_open()) + { + LL_DEBUGS() << "Loading emoji characters file at " << filename << LL_ENDL; + LLSDSerialize::fromXML(data, file); + } + + if (data.isUndefined()) + { + LL_WARNS() << "Emoji file characters missing or ill-formed" << LL_ENDL; + return; + } + + for (LLSD::array_const_iterator descriptor_it = data.beginArray(), descriptor_end = data.endArray(); descriptor_it != descriptor_end; ++descriptor_it) + { + LLEmojiDescriptor descriptor(*descriptor_it); + if (!descriptor.isValid()) + { + LL_WARNS() << "Skipping invalid emoji descriptor " << descriptor.Character << LL_ENDL; + continue; + } + pThis->addEmoji(std::move(descriptor)); + } +} + +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; +} + +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->Name : LLStringUtil::null; +} + +void LLEmojiDictionary::addEmoji(LLEmojiDescriptor&& descr) +{ + mEmojis.push_back(descr); + mEmoji2Descr.insert(std::make_pair(descr.Character, &mEmojis.back())); + for (const std::string& shortCode : descr.ShortCodes) + { + mShortCode2Descr.insert(std::make_pair(shortCode, &mEmojis.back())); + } +} + +// ============================================================================ diff --git a/indra/llui/llemojidictionary.h b/indra/llui/llemojidictionary.h new file mode 100644 index 0000000000..46a61f1cd7 --- /dev/null +++ b/indra/llui/llemojidictionary.h @@ -0,0 +1,73 @@ +/** +* @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 +{ + LLEmojiDescriptor(const LLSD& descriptor_sd); + + bool isValid() const; + + std::string Name; + llwchar Character; + std::list<std::string> ShortCodes; + std::list<std::string> Categories; +}; + +// ============================================================================ +// LLEmojiDictionary class +// + +class LLEmojiDictionary : public LLParamSingleton<LLEmojiDictionary>, public LLInitClass<LLEmojiDictionary> +{ + LLSINGLETON(LLEmojiDictionary); + ~LLEmojiDictionary() override {}; + +public: + static void initClass(); + LLWString findMatchingEmojis(const std::string& needle) const; + const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const; + std::string getNameFromEmoji(llwchar ch) const; + +private: + void addEmoji(LLEmojiDescriptor&& descr); + +private: + std::list<LLEmojiDescriptor> mEmojis; + std::map<llwchar, const LLEmojiDescriptor*> mEmoji2Descr; + std::map<std::string, const LLEmojiDescriptor*> mShortCode2Descr; +}; + +// ============================================================================ diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp new file mode 100644 index 0000000000..1e4c19a183 --- /dev/null +++ b/indra/llui/llemojihelper.cpp @@ -0,0 +1,166 @@ +/** +* @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_complete"; +constexpr S32 HELPER_FLOATER_OFFSET_X = 20; +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(LLWString)> cb) +{ + // Commit immediately if the user already typed a full shortcode + if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code)) + { + cb(LLWString(1, 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())); }, 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 rct = pHelperFloater->getRect(); + rct.setLeftTopAndSize(floater_x - HELPER_FLOATER_OFFSET_X, floater_y - HELPER_FLOATER_OFFSET_Y + rct.getHeight(), rct.getWidth(), rct.getHeight()); + pHelperFloater->setRect(rct); + pHelperFloater->openFloater(LLSD().with("hint", short_code)); +} + +void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p) +{ + if (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(const LLWString& wstr) +{ + if (!mHostHandle.isDead() && mEmojiCommitCb) + { + mEmojiCommitCb(wstr); + } +} + +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..63f5c640c9 --- /dev/null +++ b/indra/llui/llemojihelper.h @@ -0,0 +1,64 @@ +/** +* @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(LLWString)> commit_cb); + void hideHelper(const LLUICtrl* ctrl_p = nullptr); + + // Eventing + bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask); + void onCommitEmoji(const LLWString& wstr); + +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(LLWString)> mEmojiCommitCb; +}; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index d413fab270..763b67bb3a 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -2486,7 +2486,7 @@ 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); } @@ -3039,7 +3039,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; } } diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 668cd208a9..282f7a80ac 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -313,6 +313,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()); @@ -481,6 +484,7 @@ 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; diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index eba93beed9..d5988dadbc 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -874,7 +874,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, /*use_color*/FALSE); } void LLFolderViewItem::draw() @@ -953,7 +953,7 @@ void LLFolderViewItem::draw() { 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, /*use_ellipses*/FALSE, /*use_color*/FALSE ); } //--------------------------------------------------------------------------------// @@ -966,7 +966,7 @@ void LLFolderViewItem::draw() 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 ); + filter_string_length, S32_MAX, &right_x, /*use_ellipses*/FALSE, /*use_color*/FALSE ); } //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index 093e213be3..618169a8fe 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -172,7 +172,7 @@ public: virtual BOOL removeItem() = 0; virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0; - virtual BOOL isItemCopyable() const = 0; + virtual bool isItemCopyable(bool can_copy_as_link = true) const = 0; virtual BOOL copyToClipboard() const = 0; virtual BOOL cutToClipboard() = 0; virtual bool isCutToClipboard() { return false; }; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 65c7b420ce..88c1bdc0d5 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -3395,3 +3395,42 @@ boost::signals2::connection LLScrollListCtrl::setIsFriendCallback(const is_frien } return mIsFriendSignal->connect(cb); } + +bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str) +{ + if (filter_str == "" || filter_str == " ") + { + clearHighlightedItems(); + return false; + } + + bool res = false; + + setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red)); + + std::string filter_str_lc(filter_str); + LLStringUtil::toLower(filter_str_lc); + + std::vector<LLScrollListItem*> data = getAllData(); + std::vector<LLScrollListItem*>::iterator iter = data.begin(); + while (iter != data.end()) + { + LLScrollListCell* cell = (*iter)->getColumn(0); + if (cell) + { + std::string value = cell->getValue().asString(); + LLStringUtil::toLower(value); + if (value.find(filter_str_lc) == std::string::npos) + { + (*iter)->setHighlighted(false); + } + else + { + (*iter)->setHighlighted(true); + res = true; + } + } + iter++; + } + return res; +} diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 77d10fdec7..11ee012185 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -419,6 +419,8 @@ public: void setNeedsSort(bool val = true) { mSorted = !val; } void dirtyColumns(); // some operation has potentially affected column layout or ordering + bool highlightMatchingItems(const std::string& filter_str); + boost::signals2::connection setSortCallback(sort_signal_t::slot_type cb ) { if (!mSortCallback) mSortCallback = new sort_signal_t(); diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index ef7c8ec012..c411aafb1a 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -101,7 +101,10 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) // Spin buttons LLButton::Params up_button_params(p.up_button); up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height); - up_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); + // Click callback starts within the button and ends within the button, + // but LLSpinCtrl handles the action continuosly so subsribers needs to + // be informed about click ending even if outside view, use 'up' instead + up_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); up_button_params.commit_on_capture_lost = true; @@ -110,7 +113,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) LLButton::Params down_button_params(p.down_button); down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height); - down_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); + down_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); down_button_params.commit_on_capture_lost = true; mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 7e4aaa53bf..b2c0577604 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -29,6 +29,7 @@ #include "lltextbase.h" +#include "llemojihelper.h" #include "lllocalcliprect.h" #include "llmenugl.h" #include "llscrollcontainer.h" @@ -161,10 +162,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", false), parse_urls("parse_urls", false), force_urls_external("force_urls_external", false), parse_highlights("parse_highlights", false) @@ -208,6 +211,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 +226,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), @@ -355,95 +360,113 @@ void LLTextBase::onValueChange(S32 start, S32 end) { } - -// Draws the black box behind the selected text -void LLTextBase::drawSelectionBackground() +std::vector<LLRect> LLTextBase::getSelctionRects() { - // Draw selection even if we don't have keyboard focus for search/replace - if( hasSelection() && !mLineInfoList.empty()) - { - std::vector<LLRect> selection_rects; + // Nor supposed to be called without selection + llassert(hasSelection()); + llassert(!mLineInfoList.empty()); - S32 selection_left = llmin( mSelectionStart, mSelectionEnd ); - S32 selection_right = llmax( mSelectionStart, mSelectionEnd ); + std::vector<LLRect> selection_rects; - // Skip through the lines we aren't drawing. - LLRect content_display_rect = getVisibleDocumentRect(); + S32 selection_left = llmin(mSelectionStart, mSelectionEnd); + S32 selection_right = llmax(mSelectionStart, mSelectionEnd); - // binary search for line that starts before top of visible buffer - line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); - line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); + // Skip through the lines we aren't drawing. + LLRect content_display_rect = getVisibleDocumentRect(); - bool done = false; + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); + line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); - // Find the coordinates of the selected area - for (;line_iter != end_iter && !done; ++line_iter) - { - // is selection visible on this line? - if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) - { - segment_set_t::iterator segment_iter; - S32 segment_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); - - LLRect selection_rect; - selection_rect.mLeft = line_iter->mRect.mLeft; - selection_rect.mRight = line_iter->mRect.mLeft; - selection_rect.mBottom = line_iter->mRect.mBottom; - selection_rect.mTop = line_iter->mRect.mTop; - - for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) - { - LLTextSegmentPtr segmentp = *segment_iter; + bool done = false; - S32 segment_line_start = segmentp->getStart() + segment_offset; - S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); + // Find the coordinates of the selected area + for (; line_iter != end_iter && !done; ++line_iter) + { + // is selection visible on this line? + if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) + { + segment_set_t::iterator segment_iter; + S32 segment_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); - if (segment_line_start > segment_line_end) break; + // Use F32 otherwise a string of multiple segments + // will accumulate a large error + F32 left_precise = line_iter->mRect.mLeft; + F32 right_precise = line_iter->mRect.mLeft; - S32 segment_width = 0; - S32 segment_height = 0; + for (; segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) + { + LLTextSegmentPtr segmentp = *segment_iter; - // if selection after beginning of segment - if(selection_left >= segment_line_start) - { - S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; - segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height); - selection_rect.mLeft += segment_width; - } + S32 segment_line_start = segmentp->getStart() + segment_offset; + S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); - // if selection_right == segment_line_end then that means we are the first character of the next segment - // or first character of the next line, in either case we want to add the length of the current segment - // to the selection rectangle and continue. - // if selection right > segment_line_end then selection spans end of current segment... - if (selection_right >= segment_line_end) - { - // extend selection slightly beyond end of line - // to indicate selection of newline character (use "n" character to determine width) - S32 num_chars = segment_line_end - segment_line_start; - segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height); - selection_rect.mRight += segment_width; - } - // else if selection ends on current segment... - else - { - S32 num_chars = selection_right - segment_line_start; - segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height); - selection_rect.mRight += segment_width; + if (segment_line_start > segment_line_end) break; - break; - } - } - selection_rects.push_back(selection_rect); - } - } + F32 segment_width = 0; + S32 segment_height = 0; + + // if selection after beginning of segment + if (selection_left >= segment_line_start) + { + S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; + segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); + left_precise += segment_width; + } + + // if selection_right == segment_line_end then that means we are the first character of the next segment + // or first character of the next line, in either case we want to add the length of the current segment + // to the selection rectangle and continue. + // if selection right > segment_line_end then selection spans end of current segment... + if (selection_right >= segment_line_end) + { + // extend selection slightly beyond end of line + // to indicate selection of newline character (use "n" character to determine width) + S32 num_chars = segment_line_end - segment_line_start; + segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); + right_precise += segment_width; + } + // else if selection ends on current segment... + else + { + S32 num_chars = selection_right - segment_line_start; + segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height); + right_precise += segment_width; + + break; + } + } + + LLRect selection_rect; + selection_rect.mLeft = left_precise; + selection_rect.mRight = right_precise; + selection_rect.mBottom = line_iter->mRect.mBottom; + selection_rect.mTop = line_iter->mRect.mTop; + + selection_rects.push_back(selection_rect); + } + } + + return selection_rects; +} + +// Draws the black box behind the selected text +void LLTextBase::drawSelectionBackground() +{ + // Draw selection even if we don't have keyboard focus for search/replace + if (hasSelection() && !mLineInfoList.empty()) + { + std::vector<LLRect> selection_rects = getSelctionRects(); // Draw the selection box (we're using a box instead of reversing the colors on the selected text). gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); const LLColor4& color = mSelectedBGColor; F32 alpha = hasFocus() ? 0.7f : 0.3f; alpha *= getDrawContext().mAlpha; + LLColor4 selection_color(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], alpha); + LLRect content_display_rect = getVisibleDocumentRect(); for (std::vector<LLRect>::iterator rect_it = selection_rects.begin(); rect_it != selection_rects.end(); @@ -558,7 +581,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); @@ -872,6 +895,25 @@ 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; + for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++) + { + if (LLStringOps::isEmoji(wstr[text_kitty])) + { + 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() ) @@ -1968,8 +2010,6 @@ LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index) 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(); } @@ -1993,8 +2033,6 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 i 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(); } @@ -2551,7 +2589,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, } S32 pos = getLength(); - S32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft; + F32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft; segment_set_t::iterator line_seg_iter; S32 line_seg_offset; @@ -2563,8 +2601,9 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, S32 segment_line_start = segmentp->getStart() + line_seg_offset; S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start; - S32 text_width, text_height; - bool newline = segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height); + F32 text_width; + S32 text_height; + bool newline = segmentp->getDimensionsF32(line_seg_offset, segment_line_length, text_width, text_height); if(newline) { @@ -2584,8 +2623,9 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, S32 offset; if (!segmentp->canEdit()) { - S32 segment_width, segment_height; - segmentp->getDimensions(0, segmentp->getEnd() - segmentp->getStart(), segment_width, segment_height); + F32 segment_width; + S32 segment_height; + segmentp->getDimensionsF32(0, segmentp->getEnd() - segmentp->getStart(), segment_width, segment_height); if (round && local_x - start_x > segment_width / 2) { offset = segment_line_length; @@ -2632,17 +2672,11 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const return LLRect(); } - LLRect doc_rect; - // clamp pos to valid values pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare()); - doc_rect.mLeft = line_iter->mRect.mLeft; - doc_rect.mBottom = line_iter->mRect.mBottom; - doc_rect.mTop = line_iter->mRect.mTop; - segment_set_t::iterator line_seg_iter; S32 line_seg_offset; segment_set_t::iterator cursor_seg_iter; @@ -2650,6 +2684,8 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset); + F32 doc_left_precise = line_iter->mRect.mLeft; + while(line_seg_iter != mSegments.end()) { const LLTextSegmentPtr segmentp = *line_seg_iter; @@ -2657,18 +2693,20 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const if (line_seg_iter == cursor_seg_iter) { // cursor advanced to right based on difference in offset of cursor to start of line - S32 segment_width, segment_height; - segmentp->getDimensions(line_seg_offset, cursor_seg_offset - line_seg_offset, segment_width, segment_height); - doc_rect.mLeft += segment_width; + F32 segment_width; + S32 segment_height; + segmentp->getDimensionsF32(line_seg_offset, cursor_seg_offset - line_seg_offset, segment_width, segment_height); + doc_left_precise += segment_width; break; } else { // add remainder of current text segment to cursor position - S32 segment_width, segment_height; - segmentp->getDimensions(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset, segment_width, segment_height); - doc_rect.mLeft += segment_width; + F32 segment_width; + S32 segment_height; + segmentp->getDimensionsF32(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset, segment_width, segment_height); + doc_left_precise += segment_width; // offset will be 0 for all segments after the first line_seg_offset = 0; // go to next text segment on this line @@ -2676,6 +2714,11 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const } } + LLRect doc_rect; + doc_rect.mLeft = doc_left_precise; + doc_rect.mBottom = line_iter->mRect.mBottom; + doc_rect.mTop = line_iter->mRect.mTop; + // set rect to 0 width doc_rect.mRight = doc_rect.mLeft; @@ -3283,12 +3326,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; @@ -3302,12 +3346,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 ) @@ -3319,12 +3364,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; } @@ -3556,6 +3602,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 25f8fa1c2b..7e1f727607 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); +}; + // Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment) class LLOnHoverChangeableTextSegment : public LLNormalTextSegment { @@ -316,6 +328,7 @@ public: plain_text, wrap, use_ellipses, + use_color, parse_urls, force_urls_external, parse_highlights, @@ -335,6 +348,8 @@ public: Optional<LLFontGL::ShadowType> font_shadow; + Optional<LLFontGL::VAlign> text_valign; + Params(); }; @@ -394,6 +409,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;} @@ -638,6 +654,8 @@ protected: return mLabel.getString() + getToolTip(); } + std::vector<LLRect> getSelctionRects(); + protected: // text segmentation and flow segment_set_t mSegments; @@ -685,8 +703,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; @@ -695,6 +714,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 b1f8b00cab..168c260c7d 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"); @@ -259,6 +261,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mPrevalidateFunc(p.prevalidate_callback()), mContextMenu(NULL), mShowContextMenu(p.show_context_menu), + mShowEmojiHelper(p.show_emoji_helper), mEnableTooltipPaste(p.enable_tooltip_paste), mPassDelete(FALSE), mKeepSelectionOnReturn(false) @@ -501,6 +504,15 @@ 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()) @@ -664,6 +676,21 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p endSelection(); } +void LLTextEditor::handleEmojiCommit(const LLWString& wstr) +{ + LLWString wtext(getWText()); S32 shortCodePos; + if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos)) + { + remove(shortCodePos, mCursorPos - shortCodePos, true); + + auto styleParams = LLStyle::Params(); + styleParams.font = LLFontGL::getFontEmoji(); + insert(shortCodePos, wstr, false, new LLEmojiTextSegment(new LLStyle(styleParams), shortCodePos, shortCodePos + wstr.size(), *this)); + + setCursorPos(shortCodePos + 1); + } +} + BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -930,6 +957,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) ) { @@ -1124,6 +1157,17 @@ void LLTextEditor::addChar(llwchar wc) setCursorPos(mCursorPos + addChar( mCursorPos, wc )); + if (!mReadOnly && mShowEmojiHelper) + { + LLWString wtext(getWText()); S32 shortCodePos; + if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos)) + { + const LLRect cursorRect = getLocalRectFromDocIndex(mCursorPos - 1); + const LLWString shortCode = wtext.substr(shortCodePos, mCursorPos - shortCodePos); + LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, wstring_to_utf8str(shortCode), std::bind(&LLTextEditor::handleEmojiCommit, this, std::placeholders::_1)); + } + } + if (!mReadOnly && mAutoreplaceCallback != NULL) { // autoreplace the text, if necessary @@ -1774,6 +1818,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) @@ -1815,6 +1864,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; diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 1a10d2fd1e..f7621b39f0 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,8 @@ public: static S32 spacesPerTab(); + void handleEmojiCommit(const LLWString& wstr); + // mousehandler overrides virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); @@ -202,6 +205,9 @@ public: void setShowContextMenu(bool show) { mShowContextMenu = show; } bool getShowContextMenu() const { return mShowContextMenu; } + void setShowEmojiHelper(bool show); + bool getShowEmojiHelper() const { return mShowEmojiHelper; } + void setPassDelete(BOOL b) { mPassDelete = b; } protected: @@ -318,6 +324,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/llview.cpp b/indra/llui/llview.cpp index 9ba71913d0..74abe54690 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1324,7 +1324,7 @@ void LLView::drawDebugRect() 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); + S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE); } } LLUI::popMatrix(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index c487877caf..41f3042ace 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -3067,18 +3067,54 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ { LLMutexLock lock(&window_imp->mRawMouseMutex); - S32 speed; - const S32 DEFAULT_SPEED(10); - SystemParametersInfo(SPI_GETMOUSESPEED, 0, &speed, 0); - if (speed == DEFAULT_SPEED) + bool absolute_coordinates = (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE); + + if (absolute_coordinates) { - window_imp->mRawMouseDelta.mX += raw->data.mouse.lLastX; - window_imp->mRawMouseDelta.mY -= raw->data.mouse.lLastY; + static S32 prev_absolute_x = 0; + static S32 prev_absolute_y = 0; + S32 absolute_x; + S32 absolute_y; + + if ((raw->data.mouse.usFlags & 0x10) == 0x10) // touch screen? touch? Not defined in header + { + // touch screen spams (0,0) coordinates in a number of situations + // (0,0) might need to be filtered + absolute_x = raw->data.mouse.lLastX; + absolute_y = raw->data.mouse.lLastY; + } + else + { + bool v_desktop = (raw->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) == MOUSE_VIRTUAL_DESKTOP; + + S32 width = GetSystemMetrics(v_desktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); + S32 height = GetSystemMetrics(v_desktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); + + absolute_x = (raw->data.mouse.lLastX / 65535.0f) * width; + absolute_y = (raw->data.mouse.lLastY / 65535.0f) * height; + } + + window_imp->mRawMouseDelta.mX += absolute_x - prev_absolute_x; + window_imp->mRawMouseDelta.mY -= absolute_y - prev_absolute_y; + + prev_absolute_x = absolute_x; + prev_absolute_y = absolute_y; } else { - window_imp->mRawMouseDelta.mX += round((F32)raw->data.mouse.lLastX * (F32)speed / DEFAULT_SPEED); - window_imp->mRawMouseDelta.mY -= round((F32)raw->data.mouse.lLastY * (F32)speed / DEFAULT_SPEED); + S32 speed; + const S32 DEFAULT_SPEED(10); + SystemParametersInfo(SPI_GETMOUSESPEED, 0, &speed, 0); + if (speed == DEFAULT_SPEED) + { + window_imp->mRawMouseDelta.mX += raw->data.mouse.lLastX; + window_imp->mRawMouseDelta.mY -= raw->data.mouse.lLastY; + } + else + { + window_imp->mRawMouseDelta.mX += round((F32)raw->data.mouse.lLastX * (F32)speed / DEFAULT_SPEED); + window_imp->mRawMouseDelta.mY -= round((F32)raw->data.mouse.lLastY * (F32)speed / DEFAULT_SPEED); + } } } } @@ -4215,7 +4251,10 @@ void LLWindowWin32::handleCompositionMessage(const U32 indexes) if (needs_update) { - mPreeditor->resetPreedit(); + if (preedit_string.length() != 0 || result_string.length() != 0) + { + mPreeditor->resetPreedit(); + } if (result_string.length() > 0) { diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7d78ec9e3c..36f4680c44 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) @@ -287,9 +288,9 @@ set(viewer_SOURCE_FILES llfloaternotificationsconsole.cpp llfloaternotificationstabbed.cpp llfloateroutfitphotopreview.cpp - llfloateroutfitsnapshot.cpp llfloaterobjectweights.cpp llfloateropenobject.cpp + llfloatersimpleoutfitsnapshot.cpp llfloaterpathfindingcharacters.cpp llfloaterpathfindingconsole.cpp llfloaterpathfindinglinksets.cpp @@ -448,6 +449,7 @@ set(viewer_SOURCE_FILES llpaneleditsky.cpp llpaneleditwater.cpp llpaneleditwearable.cpp + llpanelemojicomplete.cpp llpanelenvironment.cpp llpanelexperiencelisteditor.cpp llpanelexperiencelog.cpp @@ -929,9 +931,9 @@ set(viewer_HEADER_FILES llfloaternotificationsconsole.h llfloaternotificationstabbed.h llfloateroutfitphotopreview.h - llfloateroutfitsnapshot.h llfloaterobjectweights.h llfloateropenobject.h + llfloatersimpleoutfitsnapshot.h llfloaterpathfindingcharacters.h llfloaterpathfindingconsole.h llfloaterpathfindinglinksets.h @@ -1079,6 +1081,7 @@ set(viewer_HEADER_FILES llpaneleditsky.h llpaneleditwater.h llpaneleditwearable.h + llpanelemojicomplete.h llpanelenvironment.h llpanelexperiencelisteditor.h llpanelexperiencelog.h @@ -1503,6 +1506,12 @@ if (WINDOWS) set(viewer_SOURCE_FILES "${viewer_SOURCE_FILES}" llviewerprecompiledheaders.cpp) endif(USE_PRECOMPILED_HEADERS) + 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") + # Replace the icons with the appropriate ones for the channel # ('test' is the default) set(ICON_PATH "test") @@ -2043,6 +2052,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${NDOF_LIBRARY} ${NVAPI_LIBRARY} ${HUNSPELL_LIBRARY} + ${ICU4C_LIBRARY} ${viewer_LIBRARIES} ${BOOST_PROGRAM_OPTIONS_LIBRARY} ${BOOST_REGEX_LIBRARY} diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 4aa5a3a58e..f5199477c5 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.6.8 +6.6.9 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 6861153d43..6789e71e41 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4747,7 +4747,7 @@ <key>Type</key> <string>String</string> <key>Value</key> - <string>https://search.[GRID]/?query_term=[QUERY]&search_type=[TYPE][COLLECTION]&maturity=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD]</string> + <string>https://search.[GRID]/viewer/?query_term=[QUERY]&search_type=[TYPE][COLLECTION]&maturity=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD]</string> </map> <key>GuidebookURL</key> <map> @@ -14634,6 +14634,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>MediaSoundsEarLocation</key> + <map> + <key>Comment</key> + <string>Location of the virtual ear for media and sounds</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>0</integer> + </map> <key>VoiceHost</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl index b26194f278..b30d7655db 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl @@ -202,7 +202,7 @@ VARYING vec2 vary_texcoord2; uniform float env_intensity; uniform vec4 specular_color; // specular color RGB and specular exponent (glossiness) in alpha -#ifdef HAS_ALPHA_MASK +#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) uniform float minimum_alpha; #endif @@ -227,12 +227,11 @@ void main() vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy); diffcol.rgb *= vertex_color.rgb; -#ifdef HAS_ALPHA_MASK -#if DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND - if (diffcol.a*vertex_color.a < minimum_alpha) -#else - if (diffcol.a < minimum_alpha) -#endif +#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) + + // Comparing floats cast from 8-bit values, produces acne right at the 8-bit transition points + float bias = 0.001953125; // 1/512, or half an 8-bit quantization (SL-18637) + if (diffcol.a < minimum_alpha-bias) { discard; } diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 1ccde98283..93dd5e7e70 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -33,7 +33,6 @@ RenderAvatarLODFactor 1 1.0 RenderAvatarPhysicsLODFactor 1 1.0 RenderAvatarMaxNonImpostors 1 16 RenderAvatarMaxComplexity 1 350000 -RenderAvatarVP 1 1 RenderAutoMuteSurfaceAreaLimit 1 1000.0 RenderCubeMap 1 1 RenderDelayVBUpdate 1 0 diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index 5542eee6ca..66197e6484 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -33,7 +33,6 @@ RenderAvatarLODFactor 1 1.0 RenderAvatarPhysicsLODFactor 1 1.0 RenderAvatarMaxNonImpostors 1 16 RenderAvatarMaxComplexity 1 350000 -RenderAvatarVP 1 1 RenderAutoMuteSurfaceAreaLimit 1 1000.0 RenderCubeMap 1 1 RenderDelayVBUpdate 1 0 @@ -80,7 +79,6 @@ RenderAvatarLODFactor 1 0 RenderAvatarPhysicsLODFactor 1 0 RenderAvatarMaxNonImpostors 1 3 RenderAvatarMaxComplexity 1 25000 -RenderAvatarVP 1 0 RenderFarClip 1 64 RenderFlexTimeFactor 1 0 RenderGlowResolutionPow 1 8 @@ -111,7 +109,6 @@ RenderAvatarLODFactor 1 0 RenderAvatarPhysicsLODFactor 1 0 RenderAvatarMaxNonImpostors 1 3 RenderAvatarMaxComplexity 1 35000 -RenderAvatarVP 1 0 RenderFarClip 1 64 RenderFlexTimeFactor 1 0 RenderGlowResolutionPow 1 8 @@ -141,7 +138,6 @@ RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 0.5 RenderAvatarMaxComplexity 1 100000 RenderAvatarPhysicsLODFactor 1 0.75 -RenderAvatarVP 1 1 RenderFarClip 1 96 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 8 @@ -171,7 +167,6 @@ RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 1.0 RenderAvatarMaxComplexity 1 200000 RenderAvatarPhysicsLODFactor 1 1.0 -RenderAvatarVP 1 1 RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 @@ -201,7 +196,6 @@ RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 1.0 RenderAvatarMaxComplexity 1 250000 RenderAvatarPhysicsLODFactor 1 1.0 -RenderAvatarVP 1 1 RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 @@ -231,7 +225,6 @@ RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 1.0 RenderAvatarMaxComplexity 1 300000 RenderAvatarPhysicsLODFactor 1 1.0 -RenderAvatarVP 1 1 RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 @@ -261,7 +254,6 @@ RenderAvatarCloth 1 0 RenderAvatarLODFactor 1 1.0 RenderAvatarMaxComplexity 1 350000 RenderAvatarPhysicsLODFactor 1 1.0 -RenderAvatarVP 1 1 RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 @@ -290,7 +282,6 @@ RenderAnisotropic 1 1 RenderAvatarCloth 1 1 RenderAvatarLODFactor 1 1.0 RenderAvatarPhysicsLODFactor 1 1.0 -RenderAvatarVP 1 1 RenderFarClip 1 256 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 @@ -367,7 +358,6 @@ RenderCompressTextures 1 0 // No Pixel Shaders available // list NoPixelShaders -RenderAvatarVP 0 0 RenderAvatarCloth 0 0 RenderReflectionDetail 0 0 WindLightUseAtmosShaders 0 0 @@ -380,7 +370,6 @@ RenderShadowDetail 0 0 // No Vertex Shaders available // list NoVertexShaders -RenderAvatarVP 0 0 RenderAvatarCloth 0 0 RenderReflectionDetail 0 0 WindLightUseAtmosShaders 0 0 @@ -402,7 +391,6 @@ RenderVBOMappingDisable 1 1 list safe RenderAnisotropic 1 0 RenderAvatarCloth 0 0 -RenderAvatarVP 0 0 RenderAvatarMaxNonImpostors 1 16 RenderAvatarMaxComplexity 1 80000 RenderObjectBump 0 0 @@ -597,7 +585,6 @@ Disregard128DefaultDrawDistance 1 0 // on various ATI chipsets on drivers before 8.2 list ATIOldDriver -RenderAvatarVP 0 0 RenderAvatarCloth 0 0 // Avoid driver crashes with some features on Linux with old ATI drivers UseOcclusion 0 0 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index c9efd89cc8..fed035c7fa 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -33,7 +33,6 @@ RenderAvatarLODFactor 1 1.0 RenderAvatarPhysicsLODFactor 1 1.0 RenderAvatarMaxNonImpostors 1 16 RenderAvatarMaxComplexity 1 350000 -RenderAvatarVP 1 1 RenderAutoMuteSurfaceAreaLimit 1 1000.0 RenderCubeMap 1 1 RenderDelayVBUpdate 1 0 diff --git a/indra/newview/fonts/DejaVu-license.txt b/indra/newview/fonts/DejaVu-license.txt deleted file mode 100644 index 254e2cc42a..0000000000 --- a/indra/newview/fonts/DejaVu-license.txt +++ /dev/null @@ -1,99 +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. - -$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $ diff --git a/indra/newview/fonts/DejaVuSans-Bold.ttf b/indra/newview/fonts/DejaVuSans-Bold.ttf Binary files differdeleted file mode 100644 index ec1a2ebaf2..0000000000 --- a/indra/newview/fonts/DejaVuSans-Bold.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf b/indra/newview/fonts/DejaVuSans-BoldOblique.ttf Binary files differdeleted file mode 100644 index 1a5576460d..0000000000 --- a/indra/newview/fonts/DejaVuSans-BoldOblique.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans-Oblique.ttf b/indra/newview/fonts/DejaVuSans-Oblique.ttf Binary files differdeleted file mode 100644 index becc549927..0000000000 --- a/indra/newview/fonts/DejaVuSans-Oblique.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSans.ttf b/indra/newview/fonts/DejaVuSans.ttf Binary files differdeleted file mode 100644 index c1b19d8705..0000000000 --- a/indra/newview/fonts/DejaVuSans.ttf +++ /dev/null diff --git a/indra/newview/fonts/DejaVuSansMono.ttf b/indra/newview/fonts/DejaVuSansMono.ttf Binary files differdeleted file mode 100644 index 6bc854ddae..0000000000 --- a/indra/newview/fonts/DejaVuSansMono.ttf +++ /dev/null diff --git a/indra/newview/installers/windows/lang_zh.nsi b/indra/newview/installers/windows/lang_zh.nsi Binary files differindex 7922d9df52..0985663041 100755 --- a/indra/newview/installers/windows/lang_zh.nsi +++ b/indra/newview/installers/windows/lang_zh.nsi diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 5250369813..9018a5f168 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2912,9 +2912,11 @@ void LLAgent::processMaturityPreferenceFromServer(const LLSD &result, U8 perferr bool LLAgent::requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess, httpCallback_t cbFailure) { - std::string url; - - url = getRegion()->getCapability(capName); + if (!getRegion()) + { + return false; + } + std::string url = getRegion()->getCapability(capName); if (url.empty()) { diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index b31447a72b..a4b4caddf1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -209,7 +209,7 @@ #include "llcommandlineparser.h" #include "llfloatermemleak.h" #include "llfloaterreg.h" -#include "llfloateroutfitsnapshot.h" +#include "llfloatersimpleoutfitsnapshot.h" #include "llfloatersnapshot.h" #include "llsidepanelinventory.h" #include "llatmosphere.h" @@ -1275,7 +1275,6 @@ bool LLAppViewer::init() //LLSimpleton creations LLEnvironment::createInstance(); - LLEnvironment::getInstance()->initSingleton(); LLWorld::createInstance(); LLSelectMgr::createInstance(); LLViewerCamera::createInstance(); @@ -1521,7 +1520,7 @@ bool LLAppViewer::doFrame() LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df Snapshot" ) pingMainloopTimeout("Main:Snapshot"); LLFloaterSnapshot::update(); // take snapshots - LLFloaterOutfitSnapshot::update(); + LLFloaterSimpleOutfitSnapshot::update(); gGLActive = FALSE; } } diff --git a/indra/newview/llaudiosourcevo.cpp b/indra/newview/llaudiosourcevo.cpp index 1846238d93..43abbb57ee 100644 --- a/indra/newview/llaudiosourcevo.cpp +++ b/indra/newview/llaudiosourcevo.cpp @@ -102,7 +102,7 @@ LLVector3d LLAudioSourceVO::getPosGlobal() const bool LLAudioSourceVO::isInCutOffRadius(const LLVector3d pos_global, const F32 cutoff) const { - static LLCachedControl<S32> ear_mode(gSavedSettings, "VoiceEarLocation", 0); + static LLCachedControl<S32> ear_mode(gSavedSettings, "MediaSoundsEarLocation", 0); LLVector3d pos_ear; @@ -113,9 +113,6 @@ bool LLAudioSourceVO::isInCutOffRadius(const LLVector3d pos_global, const F32 cu break; case 1: // avatar - case 2: - // voice support 'mixed' in '2' case with agent's position and camera's rotations - // but it is not defined in settings and uses camera as default pos_ear = gAgent.getPositionGlobal(); break; diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 7ff24f64ac..9e4d8c5fa7 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -650,7 +650,7 @@ public: void showInspector() { - if (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType) return; + if (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType && CHAT_SOURCE_REGION != mSourceType) return; if (mSourceType == CHAT_SOURCE_OBJECT) { @@ -798,6 +798,7 @@ public: icon->setValue(LLSD("OBJECT_Icon")); break; case CHAT_SOURCE_SYSTEM: + case CHAT_SOURCE_REGION: icon->setValue(LLSD("SL_Logo")); break; case CHAT_SOURCE_TELEPORT: @@ -947,7 +948,7 @@ protected: void showInfoCtrl() { - const bool isVisible = !mAvatarID.isNull() && !mFrom.empty() && CHAT_SOURCE_SYSTEM != mSourceType; + const bool isVisible = !mAvatarID.isNull() && !mFrom.empty() && CHAT_SOURCE_SYSTEM != mSourceType && CHAT_SOURCE_REGION != mSourceType; if (isVisible) { const LLRect sticky_rect = mUserNameTextBox->getRect(); @@ -1073,6 +1074,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)); @@ -1343,7 +1346,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params); prependNewLineState = false; } - else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log) + else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log && chat.mSourceType != CHAT_SOURCE_REGION) { LLStyle::Params link_params(body_message_params); link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index 787deeb594..7c6980a7e6 100644 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -87,7 +87,7 @@ public: virtual BOOL removeItem() { return FALSE; } virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) { } virtual void move( LLFolderViewModelItem* parent_listener ) { } - virtual BOOL isItemCopyable() const { return FALSE; } + virtual bool isItemCopyable(bool can_copy_as_link = true) const { return false; } virtual BOOL copyToClipboard() const { return FALSE; } virtual BOOL cutToClipboard() { return FALSE; } virtual BOOL isClipboardPasteable() const { return FALSE; } diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 8db6a10e26..e6b6b10408 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -41,6 +41,7 @@ #include "lldrawable.h" #include "llface.h" #include "llsky.h" +#include "llstartup.h" #include "lltextureentry.h" #include "llviewercamera.h" #include "llviewertexturelist.h" @@ -77,13 +78,10 @@ static S32 cube_channel = -1; static S32 diffuse_channel = -1; static S32 bump_channel = -1; -#define LL_BUMPLIST_MULTITHREADED 0 // TODO -- figure out why this doesn't work +// Enabled after changing LLViewerTexture::mNeedsCreateTexture to an +// LLAtomicBool; this should work just fine, now. HB +#define LL_BUMPLIST_MULTITHREADED 1 -// static -void LLStandardBumpmap::init() -{ - LLStandardBumpmap::restoreGL(); -} // static void LLStandardBumpmap::shutdown() @@ -94,7 +92,7 @@ void LLStandardBumpmap::shutdown() // static void LLStandardBumpmap::restoreGL() { - addstandard(); + addstandard(); } // static @@ -107,6 +105,12 @@ void LLStandardBumpmap::addstandard() return ; } + if (LLStartUp::getStartupState() < STATE_SEED_CAP_GRANTED) + { + // Not ready, need caps for images + return; + } + // can't assert; we destroyGL and restoreGL a lot during *first* startup, which populates this list already, THEN we explicitly init the list as part of *normal* startup. Sigh. So clear the list every time before we (re-)add the standard bumpmaps. //llassert( LLStandardBumpmap::sStandardBumpmapCount == 0 ); clear(); @@ -769,8 +773,6 @@ void LLBumpImageList::init() llassert( mBrightnessEntries.size() == 0 ); llassert( mDarknessEntries.size() == 0 ); - LLStandardBumpmap::init(); - LLStandardBumpmap::restoreGL(); sMainQueue = LL::WorkQueue::getInstance("mainloop"); sTexUpdateQueue = LL::WorkQueue::getInstance("LLImageGL"); // Share work queue with tex loader. diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h index e8a027967b..cf463f4458 100644 --- a/indra/newview/lldrawpoolbump.h +++ b/indra/newview/lldrawpoolbump.h @@ -118,7 +118,6 @@ public: static void clear(); static void addstandard(); - static void init(); static void shutdown(); static void restoreGL(); static void destroyGL(); diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index 1300cf3658..4ef14c20f7 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -874,26 +874,37 @@ void LLEnvironment::initSingleton() requestRegion(); - gAgent.addParcelChangedCallback([this]() { onParcelChange(); }); + if (!mParcelCallbackConnection.connected()) + { + mParcelCallbackConnection = gAgent.addParcelChangedCallback([this]() { onParcelChange(); }); - //TODO: This frequently results in one more request than we need. It isn't breaking, but should be nicer. - // We need to know new env version to fix this, without it we can only do full re-request - // Happens: on updates, on opening LLFloaterRegionInfo, on region crossing if info floater is open - LLRegionInfoModel::instance().setUpdateCallback([this]() { requestRegion(); }); - gAgent.addRegionChangedCallback([this]() { onRegionChange(); }); + //TODO: This frequently results in one more request than we need. It isn't breaking, but should be nicer. + // We need to know new env version to fix this, without it we can only do full re-request + // Happens: on updates, on opening LLFloaterRegionInfo, on region crossing if info floater is open + mRegionUpdateCallbackConnection = LLRegionInfoModel::instance().setUpdateCallback([this]() { requestRegion(); }); + mRegionChangeCallbackConnection = gAgent.addRegionChangedCallback([this]() { onRegionChange(); }); - gAgent.whenPositionChanged([this](const LLVector3 &localpos, const LLVector3d &) { onAgentPositionHasChanged(localpos); }); + mPositionCallbackConnection = gAgent.whenPositionChanged([this](const LLVector3 &localpos, const LLVector3d &) { onAgentPositionHasChanged(localpos); }); + } if (!gGenericDispatcher.isHandlerPresent(MESSAGE_PUSHENVIRONMENT)) { gGenericDispatcher.addHandler(MESSAGE_PUSHENVIRONMENT, &environment_push_dispatch_handler); } + LLEventPumps::instance().obtain(PUMP_EXPERIENCE).stopListening(LISTENER_NAME); LLEventPumps::instance().obtain(PUMP_EXPERIENCE).listen(LISTENER_NAME, [this](LLSD message) { listenExperiencePump(message); return false; }); } void LLEnvironment::cleanupSingleton() { + if (mParcelCallbackConnection.connected()) + { + mParcelCallbackConnection.disconnect(); + mRegionUpdateCallbackConnection.disconnect(); + mRegionChangeCallbackConnection.disconnect(); + mPositionCallbackConnection.disconnect(); + } LLEventPumps::instance().obtain(PUMP_EXPERIENCE).stopListening(LISTENER_NAME); } diff --git a/indra/newview/llenvironment.h b/indra/newview/llenvironment.h index 330de2bea8..64fd170e43 100644 --- a/indra/newview/llenvironment.h +++ b/indra/newview/llenvironment.h @@ -402,6 +402,11 @@ private: bool mShowMoonBeacon; S32 mEditorCounter; + connection_t mParcelCallbackConnection; + connection_t mRegionUpdateCallbackConnection; + connection_t mRegionChangeCallbackConnection; + connection_t mPositionCallbackConnection; + struct UpdateInfo { typedef std::shared_ptr<UpdateInfo> ptr_t; 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/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index c075f7e8bd..62a1e0af94 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -84,7 +84,7 @@ LLFloater360Capture::~LLFloater360Capture() // Tell the Simulator not to send us everything anymore // and revert to the regular "keyhole" frustum of interest // list updates. - if (gSavedSettings.getBOOL("360CaptureUseInterestListCap")) + if (!LLApp::isExiting() && gSavedSettings.getBOOL("360CaptureUseInterestListCap")) { const bool send_everything = false; changeInterestListMode(send_everything); diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 4cceddeefb..f1807f1c5b 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -307,7 +307,6 @@ void LLFloaterIMNearbyChat::onOpen(const LLSD& key) restoreFloater(); onCollapseToLine(this); } - showTranslationCheckbox(LLTranslate::isTranslationConfigured()); } // virtual diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index 48e2b8dc14..ee9dc35283 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -842,6 +842,7 @@ void LLFloaterIMSession::updateMessages() std::string from = msg["from"].asString(); std::string message = msg["message"].asString(); bool is_history = msg["is_history"].asBoolean(); + bool is_region_msg = msg["is_region_msg"].asBoolean(); LLChat chat; chat.mFromID = from_id; @@ -849,6 +850,10 @@ void LLFloaterIMSession::updateMessages() chat.mFromName = from; chat.mTimeStr = time; chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle; + if (is_region_msg) + { + chat.mSourceType = CHAT_SOURCE_REGION; + } // process offer notification if (msg.has("notification_id")) diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 93a0b39e02..204cd03b09 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -253,7 +253,6 @@ BOOL LLFloaterIMSessionTab::postBuild() mGearBtn = getChild<LLButton>("gear_btn"); mAddBtn = getChild<LLButton>("add_btn"); mVoiceButton = getChild<LLButton>("voice_call_btn"); - mTranslationCheckBox = getChild<LLUICtrl>("translate_chat_checkbox_lp"); mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel"); mRightPartPanel = getChild<LLLayoutPanel>("right_part_holder"); @@ -811,8 +810,6 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar() mCloseBtn->setVisible(is_not_torn_off && !mIsNearbyChat); enableDisableCallBtn(); - - showTranslationCheckbox(); } void LLFloaterIMSessionTab::forceReshape() @@ -829,11 +826,6 @@ void LLFloaterIMSessionTab::reshapeChatLayoutPanel() mChatLayoutPanel->reshape(mChatLayoutPanel->getRect().getWidth(), mInputEditor->getRect().getHeight() + mInputEditorPad, FALSE); } -void LLFloaterIMSessionTab::showTranslationCheckbox(BOOL show) -{ - mTranslationCheckBox->setVisible(mIsNearbyChat && show); -} - // static void LLFloaterIMSessionTab::processChatHistoryStyleUpdate(bool clean_messages/* = false*/) { diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index 375461cfc1..9f00917647 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -71,8 +71,6 @@ public: static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid); static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid); - // show/hide the translation check box - void showTranslationCheckbox(const BOOL visible = FALSE); bool isNearbyChat() {return mIsNearbyChat;} @@ -187,7 +185,6 @@ protected: LLButton* mGearBtn; LLButton* mAddBtn; LLButton* mVoiceButton; - LLUICtrl* mTranslationCheckBox; private: // Handling selection and contextual menu diff --git a/indra/newview/llfloateroutfitsnapshot.cpp b/indra/newview/llfloateroutfitsnapshot.cpp deleted file mode 100644 index ad5e97e067..0000000000 --- a/indra/newview/llfloateroutfitsnapshot.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/** - * @file llfloateroutfitsnapshot.cpp - * @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2016, 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 "llfloatersnapshot.h" -#include "llfloateroutfitsnapshot.h" - -#include "llagent.h" -#include "llfloaterreg.h" -#include "llimagefiltersmanager.h" -#include "llcheckboxctrl.h" -#include "llcombobox.h" -#include "llpostcard.h" -#include "llresmgr.h" // LLLocale -#include "llsdserialize.h" -#include "llsidetraypanelcontainer.h" -#include "llspinctrl.h" -#include "llviewercontrol.h" -#include "lltoolfocus.h" -#include "lltoolmgr.h" - -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- -LLOutfitSnapshotFloaterView* gOutfitSnapshotFloaterView = NULL; - -const S32 OUTFIT_SNAPSHOT_WIDTH = 256; -const S32 OUTFIT_SNAPSHOT_HEIGHT = 256; - -static LLDefaultChildRegistry::Register<LLOutfitSnapshotFloaterView> r("snapshot_outfit_floater_view"); - -///---------------------------------------------------------------------------- -/// Class LLFloaterOutfitSnapshot::Impl -///---------------------------------------------------------------------------- - -// virtual -LLPanelSnapshot* LLFloaterOutfitSnapshot::Impl::getActivePanel(LLFloaterSnapshotBase* floater, bool ok_if_not_found) -{ - LLPanel* panel = floater->getChild<LLPanel>("panel_outfit_snapshot_inventory"); - LLPanelSnapshot* active_panel = dynamic_cast<LLPanelSnapshot*>(panel); - if (!ok_if_not_found) - { - llassert_always(active_panel != NULL); - } - return active_panel; -} - -// virtual -LLSnapshotModel::ESnapshotFormat LLFloaterOutfitSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater) -{ - return LLSnapshotModel::SNAPSHOT_FORMAT_PNG; -} - -// virtual -LLSnapshotModel::ESnapshotLayerType LLFloaterOutfitSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater) -{ - return LLSnapshotModel::SNAPSHOT_TYPE_COLOR; -} - -// This is the main function that keeps all the GUI controls in sync with the saved settings. -// It should be called anytime a setting is changed that could affect the controls. -// No other methods should be changing any of the controls directly except for helpers called by this method. -// The basic pattern for programmatically changing the GUI settings is to first set the -// appropriate saved settings and then call this method to sync the GUI with them. -// FIXME: The above comment seems obsolete now. -// virtual -void LLFloaterOutfitSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater) -{ - LLSnapshotModel::ESnapshotType shot_type = getActiveSnapshotType(floater); - LLSnapshotModel::ESnapshotFormat shot_format = (LLSnapshotModel::ESnapshotFormat)gSavedSettings.getS32("SnapshotFormat"); - LLSnapshotModel::ESnapshotLayerType layer_type = getLayerType(floater); - - LLSnapshotLivePreview* previewp = getPreviewView(); - BOOL got_snap = previewp && previewp->getSnapshotUpToDate(); - - // *TODO: Separate maximum size for Web images from postcards - LL_DEBUGS() << "Is snapshot up-to-date? " << got_snap << LL_ENDL; - - LLLocale locale(LLLocale::USER_LOCALE); - std::string bytes_string; - if (got_snap) - { - LLResMgr::getInstance()->getIntegerString(bytes_string, (previewp->getDataSize()) >> 10); - } - - // Update displayed image resolution. - LLTextBox* image_res_tb = floater->getChild<LLTextBox>("image_res_text"); - image_res_tb->setVisible(got_snap); - if (got_snap) - { - image_res_tb->setTextArg("[WIDTH]", llformat("%d", previewp->getEncodedImageWidth())); - image_res_tb->setTextArg("[HEIGHT]", llformat("%d", previewp->getEncodedImageHeight())); - } - - floater->getChild<LLUICtrl>("file_size_label")->setTextArg("[SIZE]", got_snap ? bytes_string : floater->getString("unknown")); - floater->getChild<LLUICtrl>("file_size_label")->setColor(LLUIColorTable::instance().getColor("LabelTextColor")); - - updateResolution(floater); - - if (previewp) - { - previewp->setSnapshotType(shot_type); - previewp->setSnapshotFormat(shot_format); - previewp->setSnapshotBufferType(layer_type); - } - - LLPanelSnapshot* current_panel = Impl::getActivePanel(floater); - if (current_panel) - { - LLSD info; - info["have-snapshot"] = got_snap; - current_panel->updateControls(info); - } - LL_DEBUGS() << "finished updating controls" << LL_ENDL; -} - -// virtual -std::string LLFloaterOutfitSnapshot::Impl::getSnapshotPanelPrefix() -{ - return "panel_outfit_snapshot_"; -} - -// Show/hide upload status message. -// virtual -void LLFloaterOutfitSnapshot::Impl::setFinished(bool finished, bool ok, const std::string& msg) -{ - mFloater->setSuccessLabelPanelVisible(finished && ok); - mFloater->setFailureLabelPanelVisible(finished && !ok); - - if (finished) - { - LLUICtrl* finished_lbl = mFloater->getChild<LLUICtrl>(ok ? "succeeded_lbl" : "failed_lbl"); - std::string result_text = mFloater->getString(msg + "_" + (ok ? "succeeded_str" : "failed_str")); - finished_lbl->setValue(result_text); - - LLPanel* snapshot_panel = mFloater->getChild<LLPanel>("panel_outfit_snapshot_inventory"); - snapshot_panel->onOpen(LLSD()); - } -} - -void LLFloaterOutfitSnapshot::Impl::updateResolution(void* data) -{ - LLFloaterOutfitSnapshot *view = (LLFloaterOutfitSnapshot *)data; - - if (!view) - { - llassert(view); - return; - } - - S32 width = OUTFIT_SNAPSHOT_WIDTH; - S32 height = OUTFIT_SNAPSHOT_HEIGHT; - - LLSnapshotLivePreview* previewp = getPreviewView(); - if (previewp) - { - S32 original_width = 0, original_height = 0; - previewp->getSize(original_width, original_height); - - if (gSavedSettings.getBOOL("RenderUIInSnapshot") || gSavedSettings.getBOOL("RenderHUDInSnapshot")) - { //clamp snapshot resolution to window size when showing UI or HUD in snapshot - width = llmin(width, gViewerWindow->getWindowWidthRaw()); - height = llmin(height, gViewerWindow->getWindowHeightRaw()); - } - - - llassert(width > 0 && height > 0); - - // use the resolution from the selected pre-canned drop-down choice - LL_DEBUGS() << "Setting preview res selected from combo: " << width << "x" << height << LL_ENDL; - previewp->setSize(width, height); - - if (original_width != width || original_height != height) - { - // hide old preview as the aspect ratio could be wrong - checkAutoSnapshot(previewp, FALSE); - LL_DEBUGS() << "updating thumbnail" << LL_ENDL; - previewp->updateSnapshot(TRUE); - } - } -} - -///---------------------------------------------------------------------------- -/// Class LLFloaterOutfitSnapshot -///---------------------------------------------------------------------------- - -// Default constructor -LLFloaterOutfitSnapshot::LLFloaterOutfitSnapshot(const LLSD& key) -: LLFloaterSnapshotBase(key), -mOutfitGallery(NULL) -{ - impl = new Impl(this); -} - -LLFloaterOutfitSnapshot::~LLFloaterOutfitSnapshot() -{ -} - -// virtual -BOOL LLFloaterOutfitSnapshot::postBuild() -{ - mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn"); - childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this); - mRefreshLabel = getChild<LLUICtrl>("refresh_lbl"); - mSucceessLblPanel = getChild<LLUICtrl>("succeeded_panel"); - mFailureLblPanel = getChild<LLUICtrl>("failed_panel"); - - childSetCommitCallback("ui_check", ImplBase::onClickUICheck, this); - getChild<LLUICtrl>("ui_check")->setValue(gSavedSettings.getBOOL("RenderUIInSnapshot")); - - childSetCommitCallback("hud_check", ImplBase::onClickHUDCheck, this); - getChild<LLUICtrl>("hud_check")->setValue(gSavedSettings.getBOOL("RenderHUDInSnapshot")); - - getChild<LLUICtrl>("freeze_frame_check")->setValue(gSavedSettings.getBOOL("UseFreezeFrame")); - childSetCommitCallback("freeze_frame_check", ImplBase::onCommitFreezeFrame, this); - - getChild<LLUICtrl>("auto_snapshot_check")->setValue(gSavedSettings.getBOOL("AutoSnapshot")); - childSetCommitCallback("auto_snapshot_check", ImplBase::onClickAutoSnap, this); - - getChild<LLButton>("retract_btn")->setCommitCallback(boost::bind(&LLFloaterOutfitSnapshot::onExtendFloater, this)); - getChild<LLButton>("extend_btn")->setCommitCallback(boost::bind(&LLFloaterOutfitSnapshot::onExtendFloater, this)); - - // Filters - LLComboBox* filterbox = getChild<LLComboBox>("filters_combobox"); - std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList(); - for (U32 i = 0; i < filter_list.size(); i++) - { - filterbox->add(filter_list[i]); - } - childSetCommitCallback("filters_combobox", ImplBase::onClickFilter, this); - - mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); - - // create preview window - LLRect full_screen_rect = getRootView()->getRect(); - LLSnapshotLivePreview::Params p; - p.rect(full_screen_rect); - LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); - LLView* parent_view = gSnapshotFloaterView->getParent(); - - parent_view->removeChild(gSnapshotFloaterView); - // make sure preview is below snapshot floater - parent_view->addChild(previewp); - parent_view->addChild(gSnapshotFloaterView); - - //move snapshot floater to special purpose snapshotfloaterview - gFloaterView->removeChild(this); - gSnapshotFloaterView->addChild(this); - - impl->mPreviewHandle = previewp->getHandle(); - previewp->setContainer(this); - impl->updateControls(this); - impl->setAdvanced(gSavedSettings.getBOOL("AdvanceOutfitSnapshot")); - impl->updateLayout(this); - - previewp->mKeepAspectRatio = FALSE; - previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect()); - - return TRUE; -} - -// virtual -void LLFloaterOutfitSnapshot::onOpen(const LLSD& key) -{ - LLSnapshotLivePreview* preview = getPreviewView(); - if (preview) - { - LL_DEBUGS() << "opened, updating snapshot" << LL_ENDL; - preview->updateSnapshot(TRUE); - } - focusFirstItem(FALSE); - gSnapshotFloaterView->setEnabled(TRUE); - gSnapshotFloaterView->setVisible(TRUE); - gSnapshotFloaterView->adjustToFitScreen(this, FALSE); - - impl->updateControls(this); - impl->setAdvanced(gSavedSettings.getBOOL("AdvanceOutfitSnapshot")); - impl->updateLayout(this); - - LLPanel* snapshot_panel = getChild<LLPanel>("panel_outfit_snapshot_inventory"); - snapshot_panel->onOpen(LLSD()); - postPanelSwitch(); - -} - -void LLFloaterOutfitSnapshot::onExtendFloater() -{ - impl->setAdvanced(gSavedSettings.getBOOL("AdvanceOutfitSnapshot")); -} - -// static -void LLFloaterOutfitSnapshot::update() -{ - LLFloaterOutfitSnapshot* inst = findInstance(); - if (inst != NULL) - { - inst->impl->updateLivePreview(); - } -} - - -// static -LLFloaterOutfitSnapshot* LLFloaterOutfitSnapshot::findInstance() -{ - return LLFloaterReg::findTypedInstance<LLFloaterOutfitSnapshot>("outfit_snapshot"); -} - -// static -LLFloaterOutfitSnapshot* LLFloaterOutfitSnapshot::getInstance() -{ - return LLFloaterReg::getTypedInstance<LLFloaterOutfitSnapshot>("outfit_snapshot"); -} - -// virtual -void LLFloaterOutfitSnapshot::saveTexture() -{ - LL_DEBUGS() << "saveTexture" << LL_ENDL; - - LLSnapshotLivePreview* previewp = getPreviewView(); - if (!previewp) - { - llassert(previewp != NULL); - return; - } - - if (mOutfitGallery) - { - mOutfitGallery->onBeforeOutfitSnapshotSave(); - } - previewp->saveTexture(TRUE, getOutfitID().asString()); - if (mOutfitGallery) - { - mOutfitGallery->onAfterOutfitSnapshotSave(); - } - closeFloater(); -} - -///---------------------------------------------------------------------------- -/// Class LLOutfitSnapshotFloaterView -///---------------------------------------------------------------------------- - -LLOutfitSnapshotFloaterView::LLOutfitSnapshotFloaterView(const Params& p) : LLFloaterView(p) -{ -} - -LLOutfitSnapshotFloaterView::~LLOutfitSnapshotFloaterView() -{ -} diff --git a/indra/newview/llfloateroutfitsnapshot.h b/indra/newview/llfloateroutfitsnapshot.h deleted file mode 100644 index bee386ec63..0000000000 --- a/indra/newview/llfloateroutfitsnapshot.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * @file llfloateroutfitsnapshot.h - * @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2016, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLFLOATEROUTFITSNAPSHOT_H -#define LL_LLFLOATEROUTFITSNAPSHOT_H - -#include "llfloater.h" -#include "llfloatersnapshot.h" -#include "lloutfitgallery.h" -#include "llsnapshotlivepreview.h" - -///---------------------------------------------------------------------------- -/// Class LLFloaterOutfitSnapshot -///---------------------------------------------------------------------------- - -class LLFloaterOutfitSnapshot : public LLFloaterSnapshotBase -{ - LOG_CLASS(LLFloaterOutfitSnapshot); - -public: - - LLFloaterOutfitSnapshot(const LLSD& key); - /*virtual*/ ~LLFloaterOutfitSnapshot(); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - - static void update(); - - void onExtendFloater(); - - static LLFloaterOutfitSnapshot* getInstance(); - static LLFloaterOutfitSnapshot* findInstance(); - /*virtual*/ void saveTexture(); - - const LLRect& getThumbnailPlaceholderRect() { return mThumbnailPlaceholder->getRect(); } - - void setOutfitID(LLUUID id) { mOutfitID = id; } - LLUUID getOutfitID() { return mOutfitID; } - void setGallery(LLOutfitGallery* gallery) { mOutfitGallery = gallery; } - - class Impl; - friend class Impl; -private: - - LLUUID mOutfitID; - LLOutfitGallery* mOutfitGallery; -}; - -///---------------------------------------------------------------------------- -/// Class LLFloaterOutfitSnapshot::Impl -///---------------------------------------------------------------------------- - -class LLFloaterOutfitSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase -{ - LOG_CLASS(LLFloaterOutfitSnapshot::Impl); -public: - Impl(LLFloaterSnapshotBase* floater) - : LLFloaterSnapshotBase::ImplBase(floater) - {} - ~Impl() - {} - void updateResolution(void* data); - - static void onSnapshotUploadFinished(LLFloaterSnapshotBase* floater, bool status); - - /*virtual*/ LLPanelSnapshot* getActivePanel(LLFloaterSnapshotBase* floater, bool ok_if_not_found = true); - /*virtual*/ LLSnapshotModel::ESnapshotFormat getImageFormat(LLFloaterSnapshotBase* floater); - /*virtual*/ std::string getSnapshotPanelPrefix(); - - /*virtual*/ void updateControls(LLFloaterSnapshotBase* floater); - -private: - /*virtual*/ LLSnapshotModel::ESnapshotLayerType getLayerType(LLFloaterSnapshotBase* floater); - /*virtual*/ void setFinished(bool finished, bool ok = true, const std::string& msg = LLStringUtil::null); -}; - -///---------------------------------------------------------------------------- -/// Class LLOutfitSnapshotFloaterView -///---------------------------------------------------------------------------- - -class LLOutfitSnapshotFloaterView : public LLFloaterView -{ -public: - struct Params - : public LLInitParam::Block<Params, LLFloaterView::Params> - { - }; - -protected: - LLOutfitSnapshotFloaterView(const Params& p); - friend class LLUICtrlFactory; - -public: - virtual ~LLOutfitSnapshotFloaterView(); -}; - -extern LLOutfitSnapshotFloaterView* gOutfitSnapshotFloaterView; - -#endif // LL_LLFLOATEROUTFITSNAPSHOT_H diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 273810e8d4..6909ad513d 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -512,6 +512,7 @@ void LLFloaterPreference::saveSettings() if (panel) panel->saveSettings(); } + saveIgnoredNotifications(); } void LLFloaterPreference::apply() @@ -628,6 +629,8 @@ void LLFloaterPreference::cancel() gSavedSettings.setString("PresetGraphicActive", mSavedGraphicsPreset); LLPresetsManager::getInstance()->triggerChangeSignal(); } + + restoreIgnoredNotifications(); } void LLFloaterPreference::onOpen(const LLSD& key) @@ -1505,6 +1508,10 @@ void LLFloaterPreference::onClickEnablePopup() } buildPopupLists(); + if (!mFilterEdit->getText().empty()) + { + filterIgnorableNotifications(); + } } void LLFloaterPreference::onClickDisablePopup() @@ -1520,6 +1527,10 @@ void LLFloaterPreference::onClickDisablePopup() } buildPopupLists(); + if (!mFilterEdit->getText().empty()) + { + filterIgnorableNotifications(); + } } void LLFloaterPreference::resetAllIgnored() @@ -3545,11 +3556,24 @@ void LLFloaterPreference::onUpdateFilterTerm(bool force) return; mSearchData->mRootTab->hightlightAndHide( seachValue ); + filterIgnorableNotifications(); + LLTabContainer *pRoot = getChild< LLTabContainer >( "pref core" ); if( pRoot ) pRoot->selectFirstTab(); } +void LLFloaterPreference::filterIgnorableNotifications() +{ + bool visible = getChildRef<LLScrollListCtrl>("enabled_popups").highlightMatchingItems(mFilterEdit->getValue()); + visible |= getChildRef<LLScrollListCtrl>("disabled_popups").highlightMatchingItems(mFilterEdit->getValue()); + + if (visible) + { + getChildRef<LLTabContainer>("pref core").setTabVisibility( getChild<LLPanel>("msgs"), true ); + } +} + void collectChildren( LLView const *aView, ll::prefs::PanelDataPtr aParentPanel, ll::prefs::TabContainerDataPtr aParentTabContainer ) { if( !aView ) @@ -3638,3 +3662,28 @@ void LLFloaterPreference::collectSearchableItems() } mSearchDataDirty = false; } + +void LLFloaterPreference::saveIgnoredNotifications() +{ + for (LLNotifications::TemplateMap::const_iterator iter = LLNotifications::instance().templatesBegin(); + iter != LLNotifications::instance().templatesEnd(); + ++iter) + { + LLNotificationTemplatePtr templatep = iter->second; + LLNotificationFormPtr formp = templatep->mForm; + + LLNotificationForm::EIgnoreType ignore = formp->getIgnoreType(); + if (ignore <= LLNotificationForm::IGNORE_NO) + continue; + + mIgnorableNotifs[templatep->mName] = !formp->getIgnored(); + } +} + +void LLFloaterPreference::restoreIgnoredNotifications() +{ + for (std::map<std::string, bool>::iterator it = mIgnorableNotifs.begin(); it != mIgnorableNotifs.end(); ++it) + { + LLUI::getInstance()->mSettingGroups["ignores"]->setBOOL(it->first, it->second); + } +} diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 542df18ddb..e312c35135 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -143,6 +143,9 @@ public: // cancel() can restore them. void saveSettings(); + void saveIgnoredNotifications(); + void restoreIgnoredNotifications(); + void setCacheLocation(const LLStringExplicit& location); void onClickSetCache(); @@ -223,6 +226,9 @@ private: void onUpdateFilterTerm( bool force = false ); void collectSearchableItems(); + void filterIgnorableNotifications(); + + std::map<std::string, bool> mIgnorableNotifs; }; class LLPanelPreference : public LLPanel diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.cpp b/indra/newview/llfloatersimpleoutfitsnapshot.cpp new file mode 100644 index 0000000000..bab2efbbd5 --- /dev/null +++ b/indra/newview/llfloatersimpleoutfitsnapshot.cpp @@ -0,0 +1,333 @@ +/** +* @file llfloatersimpleoutfitsnapshot.cpp +* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery +* +* $LicenseInfo:firstyear=2022&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2022, 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 "llfloatersimpleoutfitsnapshot.h" + +#include "llfloaterreg.h" +#include "llimagefiltersmanager.h" +#include "llstatusbar.h" // can_afford_transaction() +#include "llnotificationsutil.h" +#include "llagentbenefits.h" +#include "llviewercontrol.h" + +LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView = NULL; + +const S32 OUTFIT_SNAPSHOT_WIDTH = 256; +const S32 OUTFIT_SNAPSHOT_HEIGHT = 256; + +static LLDefaultChildRegistry::Register<LLSimpleOutfitSnapshotFloaterView> r("simple_snapshot_outfit_floater_view"); + +///---------------------------------------------------------------------------- +/// Class LLFloaterSimpleOutfitSnapshot::Impl +///---------------------------------------------------------------------------- + +LLSnapshotModel::ESnapshotFormat LLFloaterSimpleOutfitSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater) +{ + return LLSnapshotModel::SNAPSHOT_FORMAT_PNG; +} + +LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleOutfitSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater) +{ + return LLSnapshotModel::SNAPSHOT_TYPE_COLOR; +} + +void LLFloaterSimpleOutfitSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater) +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + updateResolution(floater); + if (previewp) + { + previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE); + previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG); + previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR); + } +} + +std::string LLFloaterSimpleOutfitSnapshot::Impl::getSnapshotPanelPrefix() +{ + return "panel_outfit_snapshot_"; +} + +void LLFloaterSimpleOutfitSnapshot::Impl::updateResolution(void* data) +{ + LLFloaterSimpleOutfitSnapshot *view = (LLFloaterSimpleOutfitSnapshot *)data; + + if (!view) + { + llassert(view); + return; + } + + S32 width = OUTFIT_SNAPSHOT_WIDTH; + S32 height = OUTFIT_SNAPSHOT_HEIGHT; + + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + S32 original_width = 0, original_height = 0; + previewp->getSize(original_width, original_height); + + if (gSavedSettings.getBOOL("RenderHUDInSnapshot")) + { //clamp snapshot resolution to window size when showing UI HUD in snapshot + width = llmin(width, gViewerWindow->getWindowWidthRaw()); + height = llmin(height, gViewerWindow->getWindowHeightRaw()); + } + + llassert(width > 0 && height > 0); + + previewp->setSize(width, height); + + if (original_width != width || original_height != height) + { + // hide old preview as the aspect ratio could be wrong + checkAutoSnapshot(previewp, FALSE); + previewp->updateSnapshot(TRUE); + } + } +} + +void LLFloaterSimpleOutfitSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg) +{ + switch (status) + { + case STATUS_READY: + mFloater->setCtrlsEnabled(true); + break; + case STATUS_WORKING: + mFloater->setCtrlsEnabled(false); + break; + case STATUS_FINISHED: + mFloater->setCtrlsEnabled(true); + break; + } + + mStatus = status; +} + +///----------------------------------------------------------------re------------ +/// Class LLFloaterSimpleOutfitSnapshot +///---------------------------------------------------------------------------- + +LLFloaterSimpleOutfitSnapshot::LLFloaterSimpleOutfitSnapshot(const LLSD& key) + : LLFloaterSnapshotBase(key), + mOutfitGallery(NULL) +{ + impl = new Impl(this); +} + +LLFloaterSimpleOutfitSnapshot::~LLFloaterSimpleOutfitSnapshot() +{ +} + +BOOL LLFloaterSimpleOutfitSnapshot::postBuild() +{ + getChild<LLUICtrl>("save_btn")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost())); + + childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this); + childSetAction("save_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onSend, this)); + childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onCancel, this)); + + mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder"); + + // create preview window + LLRect full_screen_rect = getRootView()->getRect(); + LLSnapshotLivePreview::Params p; + p.rect(full_screen_rect); + LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); + LLView* parent_view = gSnapshotFloaterView->getParent(); + + parent_view->removeChild(gSnapshotFloaterView); + // make sure preview is below snapshot floater + parent_view->addChild(previewp); + parent_view->addChild(gSnapshotFloaterView); + + //move snapshot floater to special purpose snapshotfloaterview + gFloaterView->removeChild(this); + gSnapshotFloaterView->addChild(this); + + impl->mPreviewHandle = previewp->getHandle(); + previewp->setContainer(this); + impl->updateControls(this); + impl->setAdvanced(true); + impl->setSkipReshaping(true); + + previewp->mKeepAspectRatio = FALSE; + previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect()); + previewp->setAllowRenderUI(false); + + return TRUE; +} +const S32 PREVIEW_OFFSET_X = 12; +const S32 PREVIEW_OFFSET_Y = 70; + +void LLFloaterSimpleOutfitSnapshot::draw() +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + + if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock())) + { + // don't render snapshot window in snapshot, even if "show ui" is turned on + return; + } + + LLFloater::draw(); + + if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible()) + { + if(previewp->getThumbnailImage()) + { + bool working = impl->getStatus() == ImplBase::STATUS_WORKING; + const LLRect& thumbnail_rect = getThumbnailPlaceholderRect(); + const S32 thumbnail_w = previewp->getThumbnailWidth(); + const S32 thumbnail_h = previewp->getThumbnailHeight(); + + S32 offset_x = PREVIEW_OFFSET_X; + S32 offset_y = PREVIEW_OFFSET_Y; + + gGL.matrixMode(LLRender::MM_MODELVIEW); + // Apply floater transparency to the texture unless the floater is focused. + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + LLColor4 color = working ? LLColor4::grey4 : LLColor4::white; + gl_draw_scaled_image(offset_x, offset_y, + thumbnail_w, thumbnail_h, + previewp->getThumbnailImage(), color % alpha); +#if LL_DARWIN + std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "OutfitSnapshotMacMask" : "OutfitSnapshotMacMask2"; +#else + std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "FloaterFocusBackgroundColor" : "DkGray"; +#endif + + previewp->drawPreviewRect(offset_x, offset_y, LLUIColorTable::instance().getColor(alpha_color)); + + gGL.pushUIMatrix(); + LLUI::translate((F32) thumbnail_rect.mLeft, (F32) thumbnail_rect.mBottom); + mThumbnailPlaceholder->draw(); + gGL.popUIMatrix(); + } + } + impl->updateLayout(this); +} + +void LLFloaterSimpleOutfitSnapshot::onOpen(const LLSD& key) +{ + LLSnapshotLivePreview* preview = getPreviewView(); + if (preview) + { + preview->updateSnapshot(TRUE); + } + focusFirstItem(FALSE); + gSnapshotFloaterView->setEnabled(TRUE); + gSnapshotFloaterView->setVisible(TRUE); + gSnapshotFloaterView->adjustToFitScreen(this, FALSE); + + impl->updateControls(this); + impl->setStatus(ImplBase::STATUS_READY); +} + +void LLFloaterSimpleOutfitSnapshot::onCancel() +{ + closeFloater(); +} + +void LLFloaterSimpleOutfitSnapshot::onSend() +{ + S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); + if (can_afford_transaction(expected_upload_cost)) + { + saveTexture(); + postSave(); + } + else + { + LLSD args; + args["COST"] = llformat("%d", expected_upload_cost); + LLNotificationsUtil::add("ErrorPhotoCannotAfford", args); + inventorySaveFailed(); + } +} + +void LLFloaterSimpleOutfitSnapshot::postSave() +{ + impl->setStatus(ImplBase::STATUS_WORKING); +} + +// static +void LLFloaterSimpleOutfitSnapshot::update() +{ + LLFloaterSimpleOutfitSnapshot* inst = findInstance(); + if (inst != NULL) + { + inst->impl->updateLivePreview(); + } +} + + +// static +LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::findInstance() +{ + return LLFloaterReg::findTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot"); +} + +// static +LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::getInstance() +{ + return LLFloaterReg::getTypedInstance<LLFloaterSimpleOutfitSnapshot>("simple_outfit_snapshot"); +} + +void LLFloaterSimpleOutfitSnapshot::saveTexture() +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (!previewp) + { + llassert(previewp != NULL); + return; + } + + if (mOutfitGallery) + { + mOutfitGallery->onBeforeOutfitSnapshotSave(); + } + previewp->saveTexture(TRUE, getOutfitID().asString()); + if (mOutfitGallery) + { + mOutfitGallery->onAfterOutfitSnapshotSave(); + } + closeFloater(); +} + +///---------------------------------------------------------------------------- +/// Class LLSimpleOutfitSnapshotFloaterView +///---------------------------------------------------------------------------- + +LLSimpleOutfitSnapshotFloaterView::LLSimpleOutfitSnapshotFloaterView(const Params& p) : LLFloaterView(p) +{ +} + +LLSimpleOutfitSnapshotFloaterView::~LLSimpleOutfitSnapshotFloaterView() +{ +} diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.h b/indra/newview/llfloatersimpleoutfitsnapshot.h new file mode 100644 index 0000000000..cc9a6c5d1e --- /dev/null +++ b/indra/newview/llfloatersimpleoutfitsnapshot.h @@ -0,0 +1,129 @@ +/** +* @file llfloatersimpleoutfitsnapshot.h +* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery +* +* $LicenseInfo:firstyear=2022&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2022, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H +#define LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H + +#include "llfloater.h" +#include "llfloatersnapshot.h" +#include "lloutfitgallery.h" +#include "llsnapshotlivepreview.h" + +///---------------------------------------------------------------------------- +/// Class LLFloaterSimpleOutfitSnapshot +///---------------------------------------------------------------------------- + +class LLFloaterSimpleOutfitSnapshot : public LLFloaterSnapshotBase +{ + LOG_CLASS(LLFloaterSimpleOutfitSnapshot); + +public: + + LLFloaterSimpleOutfitSnapshot(const LLSD& key); + ~LLFloaterSimpleOutfitSnapshot(); + + BOOL postBuild(); + void onOpen(const LLSD& key); + void draw(); + + static void update(); + + static LLFloaterSimpleOutfitSnapshot* getInstance(); + static LLFloaterSimpleOutfitSnapshot* findInstance(); + void saveTexture(); + + const LLRect& getThumbnailPlaceholderRect() { return mThumbnailPlaceholder->getRect(); } + + void setOutfitID(LLUUID id) { mOutfitID = id; } + LLUUID getOutfitID() { return mOutfitID; } + void setGallery(LLOutfitGallery* gallery) { mOutfitGallery = gallery; } + + void postSave(); + + class Impl; + friend class Impl; + +private: + void onSend(); + void onCancel(); + + LLUUID mOutfitID; + LLOutfitGallery* mOutfitGallery; +}; + +///---------------------------------------------------------------------------- +/// Class LLFloaterSimpleOutfitSnapshot::Impl +///---------------------------------------------------------------------------- + +class LLFloaterSimpleOutfitSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase +{ + LOG_CLASS(LLFloaterSimpleOutfitSnapshot::Impl); +public: + Impl(LLFloaterSnapshotBase* floater) + : LLFloaterSnapshotBase::ImplBase(floater) + {} + ~Impl() + {} + void updateResolution(void* data); + + static void onSnapshotUploadFinished(LLFloaterSnapshotBase* floater, bool status); + + LLPanelSnapshot* getActivePanel(LLFloaterSnapshotBase* floater, bool ok_if_not_found = true) { return NULL; } + LLSnapshotModel::ESnapshotFormat getImageFormat(LLFloaterSnapshotBase* floater); + std::string getSnapshotPanelPrefix(); + + void updateControls(LLFloaterSnapshotBase* floater); + + void setStatus(EStatus status, bool ok = true, const std::string& msg = LLStringUtil::null); + +private: + LLSnapshotModel::ESnapshotLayerType getLayerType(LLFloaterSnapshotBase* floater); + void setFinished(bool finished, bool ok = true, const std::string& msg = LLStringUtil::null) {}; +}; + +///---------------------------------------------------------------------------- +/// Class LLSimpleOutfitSnapshotFloaterView +///---------------------------------------------------------------------------- + +class LLSimpleOutfitSnapshotFloaterView : public LLFloaterView +{ +public: + struct Params + : public LLInitParam::Block<Params, LLFloaterView::Params> + { + }; + +protected: + LLSimpleOutfitSnapshotFloaterView(const Params& p); + friend class LLUICtrlFactory; + +public: + virtual ~LLSimpleOutfitSnapshotFloaterView(); +}; + +extern LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView; + +#endif // LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 83212230e5..6b9d4580dc 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -176,16 +176,20 @@ void LLFloaterSnapshotBase::ImplBase::updateLayout(LLFloaterSnapshotBase* floate LLUICtrl* thumbnail_placeholder = floaterp->getChild<LLUICtrl>("thumbnail_placeholder"); thumbnail_placeholder->setVisible(mAdvanced); - thumbnail_placeholder->reshape(panel_width, thumbnail_placeholder->getRect().getHeight()); + floaterp->getChild<LLUICtrl>("image_res_text")->setVisible(mAdvanced); floaterp->getChild<LLUICtrl>("file_size_label")->setVisible(mAdvanced); if (floaterp->hasChild("360_label", TRUE)) { floaterp->getChild<LLUICtrl>("360_label")->setVisible(mAdvanced); } - if(!floaterp->isMinimized()) + if (!mSkipReshaping) { - floaterp->reshape(floater_width, floaterp->getRect().getHeight()); + thumbnail_placeholder->reshape(panel_width, thumbnail_placeholder->getRect().getHeight()); + if (!floaterp->isMinimized()) + { + floaterp->reshape(floater_width, floaterp->getRect().getHeight()); + } } bool use_freeze_frame = floaterp->getChild<LLUICtrl>("freeze_frame_check")->getValue().asBoolean(); @@ -1193,7 +1197,7 @@ S32 LLFloaterSnapshotBase::notify(const LLSD& info) // The refresh button is initially hidden. We show it after the first update, // i.e. when preview appears. - if (!mRefreshBtn->getVisible()) + if (mRefreshBtn && !mRefreshBtn->getVisible()) { mRefreshBtn->setVisible(true); } diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h index 7ec133ff45..7fc62a2746 100644 --- a/indra/newview/llfloatersnapshot.h +++ b/indra/newview/llfloatersnapshot.h @@ -59,9 +59,9 @@ public: const LLRect& getThumbnailPlaceholderRect() { return mThumbnailPlaceholder->getRect(); } - void setRefreshLabelVisible(bool value) { mRefreshLabel->setVisible(value); } - void setSuccessLabelPanelVisible(bool value) { mSucceessLblPanel->setVisible(value); } - void setFailureLabelPanelVisible(bool value) { mFailureLblPanel->setVisible(value); } + void setRefreshLabelVisible(bool value) { if (mRefreshLabel) mRefreshLabel->setVisible(value); } + void setSuccessLabelPanelVisible(bool value) { if (mSucceessLblPanel) mSucceessLblPanel->setVisible(value); } + void setFailureLabelPanelVisible(bool value) { if (mFailureLblPanel) mFailureLblPanel->setVisible(value); } void inventorySaveFailed(); class ImplBase; @@ -88,6 +88,7 @@ public: mLastToolset(NULL), mAspectRatioCheckOff(false), mNeedRefresh(false), + mSkipReshaping(false), mStatus(STATUS_READY), mFloater(floater) {} @@ -120,6 +121,7 @@ public: static BOOL updatePreviewList(bool initialized); void setAdvanced(bool advanced) { mAdvanced = advanced; } + void setSkipReshaping(bool skip) { mSkipReshaping = skip; } virtual LLSnapshotModel::ESnapshotLayerType getLayerType(LLFloaterSnapshotBase* floater) = 0; virtual void checkAutoSnapshot(LLSnapshotLivePreview* floater, BOOL update_thumbnail = FALSE); @@ -135,6 +137,7 @@ public: bool mAspectRatioCheckOff; bool mNeedRefresh; bool mAdvanced; + bool mSkipReshaping; EStatus mStatus; }; diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index b1316e386d..082bb888b1 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -289,7 +289,6 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))-> - showTranslationCheckbox(LLTranslate::isTranslationConfigured()); + closeFloater(false); } diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index e67c79a3a0..9d16faf0b5 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, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE); } 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, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE); // 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, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE); 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, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE); // draw element LLUI::translate(0,-mSpacing-viewp->getRect().getHeight()); // skip spacing distance + height diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 84a1278767..dbeb157323 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -175,8 +175,7 @@ public: virtual void processGroupData() = 0; protected: LLUUID mGroupId; -private: - bool mRequestProcessed; + bool mRequestProcessed; }; class LLFetchLeaveGroupData: public LLFetchGroupMemberData @@ -189,6 +188,22 @@ public: { LLGroupActions::processLeaveGroupDataResponse(mGroupId); } + void changed(LLGroupChange gc) + { + if (gc == GC_PROPERTIES && !mRequestProcessed) + { + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupId); + if (!gdatap) + { + LL_WARNS() << "GroupData was NULL" << LL_ENDL; + } + else + { + processGroupData(); + mRequestProcessed = true; + } + } + } }; LLFetchLeaveGroupData* gFetchLeaveGroupData = NULL; 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/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 0524313a5c..e6845127e3 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -55,6 +55,7 @@ #include "llviewerwindow.h" #include "llviewerregion.h" #include "llvoavatarself.h" +#include "llworld.h" #include "boost/lexical_cast.hpp" #if LL_MSVC @@ -520,8 +521,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, dialog, parent_estate_id, region_id, - position, - true); + position); if (!gIMMgr->isDNDMessageSend(session_id)) { @@ -572,6 +572,15 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } if (!mute_im) { + bool region_message = false; + if (region_id.isNull()) + { + LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(from_id); + if (regionp) + { + region_message = true; + } + } gIMMgr->addMessage( session_id, from_id, @@ -583,7 +592,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, parent_estate_id, region_id, position, - true); + region_message); } else { @@ -1102,8 +1111,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, IM_SESSION_INVITE, parent_estate_id, region_id, - position, - true); + position); } else { @@ -1128,8 +1136,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, IM_SESSION_INVITE, parent_estate_id, region_id, - position, - true); + position); } break; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 4d6ebf9cbb..bf9e226244 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -760,7 +760,7 @@ void LLIMModel::LLIMSession::sessionInitReplyReceived(const LLUUID& new_session_ } } -void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time, const bool is_history) +void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time, const bool is_history, bool is_region_msg) { LLSD message; message["from"] = from; @@ -769,6 +769,7 @@ void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& f message["time"] = time; message["index"] = (LLSD::Integer)mMsgs.size(); message["is_history"] = is_history; + message["is_region_msg"] = is_region_msg; LL_DEBUGS("UIUsage") << "addMessage " << " from " << from << " from_id " << from_id << " utf8_text " << utf8_text << " time " << time << " is_history " << is_history << " session mType " << mType << LL_ENDL; if (from_id == gAgent.getID()) @@ -1149,7 +1150,7 @@ void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id) mNoUnreadMsgsSignal(arg); } -bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { +bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text, bool is_region_msg) { LLIMSession* session = findIMSession(session_id); @@ -1159,7 +1160,7 @@ bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, return false; } - session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false)); //might want to add date separately + session->addMessage(from, from_id, utf8_text, LLLogChat::timestamp(false), false, is_region_msg); //might want to add date separately return true; } @@ -1197,9 +1198,9 @@ bool LLIMModel::proccessOnlineOfflineNotification( } bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, - const std::string& utf8_text, bool log2file /* = true */) { + const std::string& utf8_text, bool log2file, bool is_region_msg) { - LLIMSession* session = addMessageSilently(session_id, from, from_id, utf8_text, log2file); + LLIMSession* session = addMessageSilently(session_id, from, from_id, utf8_text, log2file, is_region_msg); if (!session) return false; //good place to add some1 to recent list @@ -1224,7 +1225,7 @@ bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, co } LLIMModel::LLIMSession* LLIMModel::addMessageSilently(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, - const std::string& utf8_text, bool log2file /* = true */) + const std::string& utf8_text, bool log2file, bool is_region_msg) { LLIMSession* session = findIMSession(session_id); @@ -1240,7 +1241,7 @@ LLIMModel::LLIMSession* LLIMModel::addMessageSilently(const LLUUID& session_id, from_name = SYSTEM_FROM; } - addToHistory(session_id, from_name, from_id, utf8_text); + addToHistory(session_id, from_name, from_id, utf8_text, is_region_msg); if (log2file) { logToFile(getHistoryFileName(session_id), from_name, from_id, utf8_text); @@ -2663,7 +2664,7 @@ void LLIMMgr::addMessage( U32 parent_estate_id, const LLUUID& region_id, const LLVector3& position, - bool link_name) // If this is true, then we insert the name and link it to a profile + bool is_region_msg) { LLUUID other_participant_id = target_id; @@ -2735,7 +2736,7 @@ void LLIMMgr::addMessage( //<< "*** region_id: " << region_id << std::endl //<< "*** position: " << position << std::endl; - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str()); + LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str(), true, is_region_msg); } // Logically it would make more sense to reject the session sooner, in another area of the @@ -2765,7 +2766,7 @@ void LLIMMgr::addMessage( if (!LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !skip_message) { - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg); + LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg, true, is_region_msg); } // Open conversation floater if offline messages are present @@ -3692,8 +3693,7 @@ public: IM_SESSION_INVITE, message_params["parent_estate_id"].asInteger(), message_params["region_id"].asUUID(), - ll_vector3_from_sd(message_params["position"]), - true); + ll_vector3_from_sd(message_params["position"])); if (LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat)) { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index fdf9806e2e..326e8f22e3 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -81,7 +81,7 @@ public: void sessionInitReplyReceived(const LLUUID& new_session_id); void addMessagesFromHistory(const std::list<LLSD>& history); - void addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time, const bool is_history = false); + void addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time, const bool is_history = false, bool is_region_msg = false); void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction); /** @deprecated */ @@ -208,13 +208,13 @@ public: * and also saved into a file if log2file is specified. * It sends new message signal for each added message. */ - bool addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& other_participant_id, const std::string& utf8_text, bool log2file = true); + bool addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& other_participant_id, const std::string& utf8_text, bool log2file = true, bool is_region_msg = false); /** * Similar to addMessage(...) above but won't send a signal about a new message added */ LLIMModel::LLIMSession* addMessageSilently(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, - const std::string& utf8_text, bool log2file = true); + const std::string& utf8_text, bool log2file = true, bool is_region_msg = false); /** * Add a system message to an IM Model @@ -292,7 +292,7 @@ private: /** * Add message to a list of message associated with session specified by session_id */ - bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); + bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text, bool is_region_msg = false); }; @@ -334,7 +334,7 @@ public: U32 parent_estate_id = 0, const LLUUID& region_id = LLUUID::null, const LLVector3& position = LLVector3::zero, - bool link_name = false); + bool is_region_msg = false); void addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index a0bc1035bf..7793b71f56 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -614,7 +614,7 @@ BOOL LLInvFVBridge::isClipboardPasteable() const if (cat) { LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, item_id); - if (!cat_br.isItemCopyable()) + if (!cat_br.isItemCopyable(false)) return FALSE; // Skip to the next item in the clipboard continue; @@ -622,7 +622,7 @@ BOOL LLInvFVBridge::isClipboardPasteable() const // Each item must be copyable to be pastable LLItemBridge item_br(mInventoryPanel.get(), mRoot, item_id); - if (!item_br.isItemCopyable()) + if (!item_br.isItemCopyable(false)) { return FALSE; } @@ -654,6 +654,11 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const { return FALSE; } + + if (gInventory.isObjectDescendentOf(item->getUUID(), gInventory.getLibraryRootFolderID())) + { + return FALSE; + } } const LLViewerInventoryCategory *cat = model->getCategory(objects.at(i)); if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) @@ -729,15 +734,15 @@ void hide_context_entries(LLMenuGL& menu, } bool found = false; - menuentry_vec_t::const_iterator itor2; - for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2) - { - if (*itor2 == name) - { - found = true; - break; - } - } + + std::string myinput; + std::vector<std::string> mylist{ "a", "b", "c" }; + + menuentry_vec_t::const_iterator itor2 = std::find(entries_to_show.begin(), entries_to_show.end(), name); + if (itor2 != entries_to_show.end()) + { + found = true; + } // Don't allow multiple separators in a row (e.g. such as if there are no items // between two separators). @@ -755,7 +760,21 @@ void hide_context_entries(LLMenuGL& menu, menu_item->setVisible(FALSE); } - menu_item->setEnabled(FALSE); + if (menu_item->getEnabled()) + { + // These should stay enabled unless specifically disabled + const menuentry_vec_t exceptions = { + "Detach From Yourself", + "Wearable And Object Wear", + "Wearable Add", + }; + + menuentry_vec_t::const_iterator itor2 = std::find(exceptions.begin(), exceptions.end(), name); + if (itor2 == exceptions.end()) + { + menu_item->setEnabled(FALSE); + } + } } else { @@ -882,7 +901,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Paste")); } - if (gSavedSettings.getBOOL("InventoryLinking")) + static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); + if (inventory_linking) { items.push_back(std::string("Paste As Link")); if (!isClipboardPasteableAsLink() || (flags & FIRST_SELECTED_ITEM) == 0) @@ -2059,7 +2079,8 @@ BOOL LLItemBridge::removeItem() // we can't do this check because we may have items in a folder somewhere that is // not yet in memory, so we don't want false negatives. (If disabled, then we // know we only have links in the Outfits folder which we explicitly fetch.) - if (!gSavedSettings.getBOOL("InventoryLinking")) + static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); + if (!inventory_linking) { if (!item->getIsLinkType()) { @@ -2102,22 +2123,24 @@ BOOL LLItemBridge::confirmRemoveItem(const LLSD& notification, const LLSD& respo return FALSE; } -BOOL LLItemBridge::isItemCopyable() const +bool LLItemBridge::isItemCopyable(bool can_copy_as_link) const { - LLViewerInventoryItem* item = getItem(); - if (item) - { - // Can't copy worn objects. - // Worn objects are tied to their inworld conterparts - // Copy of modified worn object will return object with obsolete asset and inventory - if(get_is_item_worn(mUUID)) - { - return FALSE; - } + LLViewerInventoryItem* item = getItem(); + if (!item) + { + return false; + } + // Can't copy worn objects. + // Worn objects are tied to their inworld conterparts + // Copy of modified worn object will return object with obsolete asset and inventory + if (get_is_item_worn(mUUID)) + { + return false; + } - return item->getPermissions().allowCopyBy(gAgent.getID()) || gSavedSettings.getBOOL("InventoryLinking"); - } - return FALSE; + static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true); + return (can_copy_as_link && inventory_linking) + || item->getPermissions().allowCopyBy(gAgent.getID()); } LLViewerInventoryItem* LLItemBridge::getItem() const @@ -2321,7 +2344,7 @@ BOOL LLFolderBridge::isUpToDate() const return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN; } -BOOL LLFolderBridge::isItemCopyable() const +bool LLFolderBridge::isItemCopyable(bool can_copy_as_link) const { // Folders are copyable if items in them are, recursively, copyable. @@ -2336,22 +2359,26 @@ BOOL LLFolderBridge::isItemCopyable() const { LLInventoryItem* item = *iter; LLItemBridge item_br(mInventoryPanel.get(), mRoot, item->getUUID()); - if (!item_br.isItemCopyable()) - return FALSE; -} + if (!item_br.isItemCopyable(false)) + { + return false; + } + } // Check the folders LLInventoryModel::cat_array_t cat_array_copy = *cat_array; for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) -{ + { LLViewerInventoryCategory* category = *iter; LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, category->getUUID()); - if (!cat_br.isItemCopyable()) - return FALSE; - } - - return TRUE; - } + if (!cat_br.isItemCopyable(false)) + { + return false; + } + } + + return true; +} BOOL LLFolderBridge::isClipboardPasteable() const { @@ -3768,6 +3795,7 @@ void LLFolderBridge::perform_pasteFromClipboard() LLInventoryObject *obj = model->getObject(item_id); if (obj) { + if (move_is_into_lost_and_found) { if (LLAssetType::AT_CATEGORY == obj->getType()) @@ -4296,7 +4324,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items.push_back(std::string("IM All Contacts In Folder")); } - if (((flags & ITEM_IN_MULTI_SELECTION) == 0) && hasChildren()) + if (((flags & ITEM_IN_MULTI_SELECTION) == 0) && hasChildren() && (type != LLFolderType::FT_OUTFIT)) { items.push_back(std::string("Ungroup folder items")); } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 0b0ef273e1..bdffecf1c6 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -119,7 +119,7 @@ public: //virtual BOOL removeItem() = 0; virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); virtual void move(LLFolderViewModelItem* new_parent_bridge) {} - virtual BOOL isItemCopyable() const { return FALSE; } + virtual bool isItemCopyable(bool can_copy_as_link = true) const { return false; } virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard(); virtual bool isCutToClipboard(); @@ -245,7 +245,7 @@ public: virtual BOOL isItemRenameable() const; virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); - virtual BOOL isItemCopyable() const; + virtual bool isItemCopyable(bool can_copy_as_link = true) const; virtual bool hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } virtual LLUIImagePtr getIconOverlay() const; @@ -318,7 +318,7 @@ public: virtual BOOL isItemRemovable() const; virtual BOOL isItemMovable() const ; virtual BOOL isUpToDate() const; - virtual BOOL isItemCopyable() const; + virtual bool isItemCopyable(bool can_copy_as_link = true) const; virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 27edc8148e..5755bc692e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -862,6 +862,9 @@ LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId) S32 depth_nesting_in_marketplace(LLUUID cur_uuid) { // Get the marketplace listings root, exit with -1 (i.e. not under the marketplace listings root) if none + // Todo: findCategoryUUIDForType is somewhat expensive with large + // flat root folders yet we use depth_nesting_in_marketplace at + // every turn, find a way to correctly cache this id. const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); if (marketplace_listings_uuid.isNull()) { @@ -1504,7 +1507,12 @@ void dump_trace(std::string& message, S32 depth, LLError::ELevel log_level) // This function does no deletion of listings but a mere audit and raises issues to the user (through the // optional callback cb). It also returns a boolean, true if things validate, false if issues are raised. // The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders. -bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_t cb, bool fix_hierarchy, S32 depth) +bool validate_marketplacelistings( + LLInventoryCategory* cat, + validation_callback_t cb, + bool fix_hierarchy, + S32 depth, + bool notify_observers) { #if 0 // Used only for debug @@ -1570,7 +1578,7 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName()); LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid); gInventory.changeCategoryParent(viewer_cat, folder_uuid, false); - result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy, depth + 1); + result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy, depth + 1, notify_observers); return result; } else @@ -1740,7 +1748,10 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ // Next type update_marketplace_category(parent_uuid); update_marketplace_category(folder_uuid); - gInventory.notifyObservers(); + if (notify_observers) + { + gInventory.notifyObservers(); + } items_vector_it++; } } @@ -1754,7 +1765,7 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ { LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (*iter); gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); - result &= validate_marketplacelistings(viewer_cat, cb, fix_hierarchy, depth); + result &= validate_marketplacelistings(viewer_cat, cb, fix_hierarchy, depth, false); } } } @@ -1826,7 +1837,10 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ cb(message,depth,LLError::LEVEL_WARN); } gInventory.removeCategory(cat->getUUID()); - gInventory.notifyObservers(); + if (notify_observers) + { + gInventory.notifyObservers(); + } return result && !has_bad_items; } } @@ -1840,11 +1854,14 @@ bool validate_marketplacelistings(LLInventoryCategory* cat, validation_callback_ for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { LLInventoryCategory* category = *iter; - result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1); + result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1, false); } update_marketplace_category(cat->getUUID(), true, true); - gInventory.notifyObservers(); + if (notify_observers) + { + gInventory.notifyObservers(); + } return result && !has_bad_items; } @@ -2583,8 +2600,62 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root } std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); + + // copy list of applicable items into a vector for bulk handling uuid_vec_t ids; - std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids)); + if (action == "wear" || action == "wear_add") + { + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + std::copy_if(selected_uuid_set.begin(), + selected_uuid_set.end(), + std::back_inserter(ids), + [trash_id, mp_id](LLUUID id) + { + if (get_is_item_worn(id) + || LLAppearanceMgr::instance().getIsInCOF(id) + || gInventory.isObjectDescendentOf(id, trash_id)) + { + return false; + } + if (mp_id.notNull() && gInventory.isObjectDescendentOf(id, mp_id)) + { + return false; + } + LLInventoryObject* obj = (LLInventoryObject*)gInventory.getObject(id); + if (!obj) + { + return false; + } + if (obj->getIsLinkType() && gInventory.isObjectDescendentOf(obj->getLinkedUUID(), trash_id)) + { + return false; + } + if (obj->getIsLinkType() && LLAssetType::lookupIsLinkType(obj->getType())) + { + // missing + return false; + } + return true; + } + ); + } + else if (isRemoveAction(action)) + { + std::copy_if(selected_uuid_set.begin(), + selected_uuid_set.end(), + std::back_inserter(ids), + [](LLUUID id) + { + return get_is_item_worn(id); + } + ); + } + else + { + std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids)); + } + // Check for actions that get handled in bulk if (action == "wear") { diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index ba9f157e47..56ad6f6496 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -87,7 +87,7 @@ bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInve bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false); bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false); bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false, bool move_no_copy_items = false); -bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1); +bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1, bool notify_observers = true); S32 depth_nesting_in_marketplace(LLUUID cur_uuid); LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false); diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index fab7ae8f1a..6ba04cdff2 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1851,9 +1851,13 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) mChangedItemIDs.insert(referent); } - // Fix me: From DD-81, probably shouldn't be here, instead - // should be somewhere in an observer - update_marketplace_category(referent, false); + if (mask != LLInventoryObserver::LABEL) + { + // Fix me: From DD-81, probably shouldn't be here, instead + // should be somewhere in an observer or in + // LLMarketplaceInventoryObserver::onIdleProcessQueue + update_marketplace_category(referent, false); + } if (mask & LLInventoryObserver::ADD) { diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index dd4ae4d201..2d726409c6 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -30,6 +30,7 @@ #include "llagent.h" #include "llbufferstream.h" +#include "llcallbacklist.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "llnotificationsutil.h" @@ -605,20 +606,67 @@ public: LLMarketplaceInventoryObserver() {} virtual ~LLMarketplaceInventoryObserver() {} virtual void changed(U32 mask); + +private: + static void onIdleProcessQueue(void *userdata); + + // doesn't hold just marketplace related ids + static std::set<LLUUID> sAddQueue; + static std::set<LLUUID> sStructureQueue; + static bool sProcessingQueue; }; +std::set<LLUUID> LLMarketplaceInventoryObserver::sAddQueue; +std::set<LLUUID> LLMarketplaceInventoryObserver::sStructureQueue; +bool LLMarketplaceInventoryObserver::sProcessingQueue = false; + void LLMarketplaceInventoryObserver::changed(U32 mask) { - // When things are added to the marketplace, we might need to re-validate and fix the containing listings - if (mask & LLInventoryObserver::ADD) + if (mask & LLInventoryObserver::ADD && LLMarketplaceData::instance().hasValidationWaiting()) { + // When things are added to the marketplace, we might need to re-validate and fix the containing listings + // just add whole list even if it contains items and non-marketplace folders const std::set<LLUUID>& changed_items = gInventory.getChangedIDs(); - - std::set<LLUUID>::const_iterator id_it = changed_items.begin(); - std::set<LLUUID>::const_iterator id_end = changed_items.end(); + sAddQueue.insert(changed_items.begin(), changed_items.end()); + } + + if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE)) + { + // When things are changed in the inventory, this can trigger a host of changes in the marketplace listings folder: + // * stock counts changing : no copy items coming in and out will change the stock count on folders + // * version and listing folders : moving those might invalidate the marketplace data itself + // Since we should cannot raise inventory change while the observer is called (the list will be cleared + // once observers are called) we need to raise a flag in the inventory to signal that things have been dirtied. + const std::set<LLUUID>& changed_items = gInventory.getChangedIDs(); + sStructureQueue.insert(changed_items.begin(), changed_items.end()); + } + + if (!sProcessingQueue && (!sAddQueue.empty() || !sStructureQueue.empty())) + { + gIdleCallbacks.addFunction(onIdleProcessQueue, NULL); + // can do without sProcessingQueue, but it's usufull for simplicity and reliability + sProcessingQueue = true; + } +} + +void LLMarketplaceInventoryObserver::onIdleProcessQueue(void *userdata) +{ + U64 start_time = LLTimer::getTotalTime(); // microseconds + const U64 MAX_PROCESSING_TIME = 1000; + U64 stop_time = start_time + MAX_PROCESSING_TIME; + + if (!sAddQueue.empty()) + { + // Make a copy of sAddQueue since decrementValidationWaiting + // can theoretically add more items + std::set<LLUUID> add_queue(sAddQueue); + sAddQueue.clear(); + + std::set<LLUUID>::const_iterator id_it = add_queue.begin(); + std::set<LLUUID>::const_iterator id_end = add_queue.end(); // First, count the number of items in this list... S32 count = 0; - for (;id_it != id_end; ++id_it) + for (; id_it != id_end; ++id_it) { LLInventoryObject* obj = gInventory.getObject(*id_it); if (obj && (LLAssetType::AT_CATEGORY != obj->getType())) @@ -629,56 +677,58 @@ void LLMarketplaceInventoryObserver::changed(U32 mask) // Then, decrement the folders of that amount // Note that of all of those, only one folder will be a listing folder (if at all). // The other will be ignored by the decrement method. - id_it = changed_items.begin(); - for (;id_it != id_end; ++id_it) + id_it = add_queue.begin(); + for (; id_it != id_end; ++id_it) { LLInventoryObject* obj = gInventory.getObject(*id_it); if (obj && (LLAssetType::AT_CATEGORY == obj->getType())) { - LLMarketplaceData::instance().decrementValidationWaiting(obj->getUUID(),count); + // can trigger notifyObservers + LLMarketplaceData::instance().decrementValidationWaiting(obj->getUUID(), count); } } - } - - // When things are changed in the inventory, this can trigger a host of changes in the marketplace listings folder: - // * stock counts changing : no copy items coming in and out will change the stock count on folders - // * version and listing folders : moving those might invalidate the marketplace data itself - // Since we should cannot raise inventory change while the observer is called (the list will be cleared - // once observers are called) we need to raise a flag in the inventory to signal that things have been dirtied. - - if (mask & (LLInventoryObserver::INTERNAL | LLInventoryObserver::STRUCTURE)) - { - const std::set<LLUUID>& changed_items = gInventory.getChangedIDs(); - - std::set<LLUUID>::const_iterator id_it = changed_items.begin(); - std::set<LLUUID>::const_iterator id_end = changed_items.end(); - for (;id_it != id_end; ++id_it) + } + + while (!sStructureQueue.empty() && LLTimer::getTotalTime() < stop_time) + { + std::set<LLUUID>::const_iterator id_it = sStructureQueue.begin(); + LLInventoryObject* obj = gInventory.getObject(*id_it); + if (obj) { - LLInventoryObject* obj = gInventory.getObject(*id_it); - if (obj) + if (LLAssetType::AT_CATEGORY == obj->getType()) { - if (LLAssetType::AT_CATEGORY == obj->getType()) + // If it's a folder known to the marketplace, let's check it's in proper shape + if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it)) { - // If it's a folder known to the marketplace, let's check it's in proper shape - if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it)) - { - LLInventoryCategory* cat = (LLInventoryCategory*)(obj); - validate_marketplacelistings(cat); - } + LLInventoryCategory* cat = (LLInventoryCategory*)(obj); + // can trigger notifyObservers + // can cause more structural changes + validate_marketplacelistings(cat); } - else + } + else + { + // If it's not a category, it's an item... + LLInventoryItem* item = (LLInventoryItem*)(obj); + // If it's a no copy item, we may need to update the label count of marketplace listings + if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { - // If it's not a category, it's an item... - LLInventoryItem* item = (LLInventoryItem*)(obj); - // If it's a no copy item, we may need to update the label count of marketplace listings - if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) - { - LLMarketplaceData::instance().setDirtyCount(); - } + LLMarketplaceData::instance().setDirtyCount(); } } } - } + + // sStructureQueue could have been modified in validate_marketplacelistings + // adding items does not invalidate existing iterator + sStructureQueue.erase(id_it); + } + + if (LLApp::isExiting() || (sAddQueue.empty() && sStructureQueue.empty())) + { + // Nothing to do anymore + gIdleCallbacks.deleteFunction(onIdleProcessQueue, NULL); + sProcessingQueue = false; + } } // Tuple == Item diff --git a/indra/newview/llmarketplacefunctions.h b/indra/newview/llmarketplacefunctions.h index 088507d850..24fdc5e0ad 100644 --- a/indra/newview/llmarketplacefunctions.h +++ b/indra/newview/llmarketplacefunctions.h @@ -242,6 +242,7 @@ public: void setUpdating(const LLUUID& folder_id, bool isUpdating); // Used to decide when to run a validation on listing folders + bool hasValidationWaiting() { return mValidationWaitingList.size() > 0; } void setValidationWaiting(const LLUUID& folder_id, S32 count); void decrementValidationWaiting(const LLUUID& folder_id, S32 count = 1); diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index f419e2e06d..1facbbf37c 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -41,7 +41,7 @@ #include "llfilepicker.h" #include "llfloaterperms.h" #include "llfloaterreg.h" -#include "llfloateroutfitsnapshot.h" +#include "llfloatersimpleoutfitsnapshot.h" #include "llimagedimensionsinfo.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" @@ -1386,8 +1386,8 @@ void LLOutfitGallery::onSelectPhoto(LLUUID selected_outfit_id) void LLOutfitGallery::onTakeSnapshot(LLUUID selected_outfit_id) { - LLFloaterReg::toggleInstanceOrBringToFront("outfit_snapshot"); - LLFloaterOutfitSnapshot* snapshot_floater = LLFloaterOutfitSnapshot::getInstance(); + LLFloaterReg::toggleInstanceOrBringToFront("simple_outfit_snapshot"); + LLFloaterSimpleOutfitSnapshot* snapshot_floater = LLFloaterSimpleOutfitSnapshot::getInstance(); if (snapshot_floater) { snapshot_floater->setOutfitID(selected_outfit_id); diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp new file mode 100644 index 0000000000..8b89e3aa14 --- /dev/null +++ b/indra/newview/llpanelemojicomplete.cpp @@ -0,0 +1,321 @@ +/** +* @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 "lluictrlfactory.h" + +constexpr U32 MIN_MOUSE_MOVE_DELTA = 4; + +// ============================================================================ +// LLPanelEmojiComplete +// + +static LLDefaultChildRegistry::Register<LLPanelEmojiComplete> r("emoji_complete"); + +LLPanelEmojiComplete::Params::Params() + : autosize("autosize") + , max_emoji("max_emoji") + , padding("padding") + , selected_image("selected_image") +{ +} + +LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) + : LLUICtrl(p) + , mAutoSize(p.autosize) + , mMaxVisible(p.max_emoji) + , mPadding(p.padding) + , mSelectedImage(p.selected_image) +{ + setFont(p.font); +} + +LLPanelEmojiComplete::~LLPanelEmojiComplete() +{ +} + +void LLPanelEmojiComplete::draw() +{ + if (!mEmojis.empty()) + { + const S32 centerY = mRenderRect.getCenterY(); + const size_t firstVisibleIdx = mScrollPos, lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1; + + if (mCurSelected >= firstVisibleIdx && mCurSelected <= lastVisibleIdx) + { + const S32 emoji_left = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth; + const S32 emoji_height = mFont->getLineHeight() + mPadding; + mSelectedImage->draw(emoji_left, centerY - emoji_height / 2, mEmojiWidth, emoji_height); + } + + U32 left = mRenderRect.mLeft + mPadding; + for (U32 curIdx = firstVisibleIdx; curIdx <= lastVisibleIdx; curIdx++) + { + mFont->render( + mEmojis, curIdx, + left, centerY, + LLColor4::white, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW_SOFT, + 1, S32_MAX, nullptr, false, true); + left += mEmojiWidth; + } + } +} + +BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask) +{ + LLVector2 curHover(x, y); + if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA) + { + mCurSelected = posToIndex(x, y); + mLastHover = curHover; + } + + return TRUE; +} + +BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + bool handled = false; + if (MASK_NONE == mask) + { + switch (key) + { + case KEY_LEFT: + case KEY_UP: + selectPrevious(); + handled = true; + break; + case KEY_RIGHT: + case KEY_DOWN: + selectNext(); + handled = true; + break; + case KEY_RETURN: + if (!mEmojis.empty()) + { + 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) +{ + mCurSelected = posToIndex(x, y); + mLastHover = LLVector2(x, y); + + return TRUE; +} + +BOOL LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask) +{ + mCurSelected = posToIndex(x, y); + onCommit(); + + return TRUE; +} + +void LLPanelEmojiComplete::onCommit() +{ + if (npos != mCurSelected) + { + LLWString wstr; + wstr.push_back(mEmojis.at(mCurSelected)); + setValue(wstring_to_utf8str(wstr)); + LLUICtrl::onCommit(); + } +} + +void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + updateConstraints(); +} + +void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) +{ + llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; + + mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); + size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; + mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; + + if (mAutoSize) + { + mVisibleEmojis = std::min(mEmojis.size(), mMaxVisible); + reshape(mVisibleEmojis * mEmojiWidth, getRect().getHeight(), false); + } + else + { + updateConstraints(); + } + + mScrollPos = llmin(mScrollPos, mEmojis.size()); +} + +size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const +{ + if (mRenderRect.pointInRect(x, y)) + { + return mScrollPos + llmin((size_t)x / mEmojiWidth, mEmojis.size() - 1); + } + return npos; +} + +void LLPanelEmojiComplete::select(size_t emoji_idx) +{ + mCurSelected = llclamp<size_t>(emoji_idx, 0, mEmojis.size()); + updateScrollPos(); +} + +void LLPanelEmojiComplete::selectNext() +{ + select(mCurSelected + 1 < mEmojis.size() ? mCurSelected + 1 : 0); +} + +void LLPanelEmojiComplete::selectPrevious() +{ + select(mCurSelected - 1 >= 0 ? mCurSelected - 1 : mEmojis.size() - 1); +} + +void LLPanelEmojiComplete::setFont(const LLFontGL* fontp) +{ + mFont = fontp; + updateConstraints(); +} + +void LLPanelEmojiComplete::updateConstraints() +{ + const S32 ctrlWidth = getLocalRect().getWidth(); + + mEmojiWidth = mFont->getWidthF32(u8"\U0001F431") + mPadding * 2; + mVisibleEmojis = ctrlWidth / mEmojiWidth; + mRenderRect = getLocalRect().stretch((ctrlWidth - mVisibleEmojis * mEmojiWidth) / -2, 0); + + updateScrollPos(); +} + +void LLPanelEmojiComplete::updateScrollPos() +{ + const size_t cntEmoji = mEmojis.size(); + if (0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) + { + mScrollPos = 0; + } + else if (cntEmoji - 1 == mCurSelected) + { + mScrollPos = mCurSelected - mVisibleEmojis + 1; + } + else + { + mScrollPos = mCurSelected - ((float)mCurSelected / (cntEmoji - 2) * (mVisibleEmojis - 2)); + } +} + +// ============================================================================ +// 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(); + } +} + +BOOL LLFloaterEmojiComplete::postBuild() +{ + mEmojiCtrl = findChild<LLPanelEmojiComplete>("emoji_complete_ctrl"); + mEmojiCtrl->setCommitCallback( + std::bind([&](const LLSD& sdValue) + { + setValue(sdValue); + onCommit(); + }, std::placeholders::_2)); + mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth(); + + return LLFloater::postBuild(); +} + +void LLFloaterEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + if (!called_from_parent) + { + LLRect rctFloater = getRect(), rctCtrl = mEmojiCtrl->getRect(); + rctFloater.mRight = rctFloater.mLeft + rctCtrl.getWidth() + mEmojiCtrlHorz; + setRect(rctFloater); + + return; + } + + LLFloater::reshape(width, height, called_from_parent); +} + +// ============================================================================ diff --git a/indra/newview/llpanelemojicomplete.h b/indra/newview/llpanelemojicomplete.h new file mode 100644 index 0000000000..aa0f806525 --- /dev/null +++ b/indra/newview/llpanelemojicomplete.h @@ -0,0 +1,115 @@ +/** +* @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 "llfloater.h" +#include "lluictrl.h" + +// ============================================================================ +// LLPanelEmojiComplete +// + +class LLPanelEmojiComplete : public LLUICtrl +{ + friend class LLUICtrlFactory; +public: + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> + { + Optional<bool> autosize; + Optional<S32> max_emoji, + 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; + void onCommit() override; + void reshape(S32 width, S32 height, BOOL called_from_parent) override; + +public: + size_t getEmojiCount() const { return mEmojis.size(); } + void setEmojiHint(const std::string& hint); +protected: + size_t posToIndex(S32 x, S32 y) const; + void select(size_t emoji_idx); + void selectNext(); + void selectPrevious(); + void setFont(const LLFontGL* fontp); + void updateConstraints(); + void updateScrollPos(); + +protected: + static constexpr auto npos = std::numeric_limits<size_t>::max(); + + bool mAutoSize = false; + const LLFontGL* mFont; + U16 mEmojiWidth = 0; + size_t mMaxVisible = 0; + S32 mPadding = 8; + LLRect mRenderRect; + LLUIImagePtr mSelectedImage; + + LLWString mEmojis; + 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; +}; + +// ============================================================================ diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index e1818cc68b..416857bd30 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -454,7 +454,8 @@ bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace(bool only_if_current_ LLViewerMedia::getInstance()->getMediaImplFromTextureID(object->getTE(face)->getMediaData()->getMediaID()); if(media_impl) { - media_impl->navigateHome(); + media_impl->setPriority(LLPluginClassMedia::PRIORITY_NORMAL); + media_impl->navigateHome(); return true; } } diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 0bfc1297d3..bc7933d84b 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -93,6 +93,8 @@ enum { MI_HOLE_COUNT }; +const F32 MAX_ATTACHMENT_DIST = 3.5f; // meters + //static const std::string LEGACY_FULLBRIGHT_DESC =LLTrans::getString("Fullbright"); BOOL LLPanelObject::postBuild() @@ -1685,6 +1687,16 @@ void LLPanelObject::sendPosition(BOOL btn_down) mCtrlPosZ->set(LLWorld::getInstance()->resolveLandHeightAgent(newpos) + 1.f); } } + else + { + if (newpos.length() > MAX_ATTACHMENT_DIST) + { + newpos.clampLength(MAX_ATTACHMENT_DIST); + mCtrlPosX->set(newpos.mV[VX]); + mCtrlPosY->set(newpos.mV[VY]); + mCtrlPosZ->set(newpos.mV[VZ]); + } + } // Make sure new position is in a valid region, so the object // won't get dumped by the simulator. @@ -2191,6 +2203,10 @@ void LLPanelObject::onPastePos() mClipboardPos.mV[VY] = llclamp(mClipboardPos.mV[VY], 0.f, max_width); //height will get properly clamped by sendPosition } + else + { + mClipboardPos.clampLength(MAX_ATTACHMENT_DIST); + } mCtrlPosX->set( mClipboardPos.mV[VX] ); mCtrlPosY->set( mClipboardPos.mV[VY] ); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index cfaa9456be..fff25c6c61 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -131,7 +131,7 @@ public: virtual BOOL removeItem(); virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); virtual void move(LLFolderViewModelItem* parent_listener); - virtual BOOL isItemCopyable() const; + virtual bool isItemCopyable(bool can_copy_as_link = true) const; virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard(); virtual BOOL isClipboardPasteable() const; @@ -439,10 +439,10 @@ void LLTaskInvFVBridge::move(LLFolderViewModelItem* parent_listener) { } -BOOL LLTaskInvFVBridge::isItemCopyable() const +bool LLTaskInvFVBridge::isItemCopyable(bool can_link) const { LLInventoryItem* item = findItem(); - if(!item) return FALSE; + if(!item) return false; return gAgent.allowOperation(PERM_COPY, item->getPermissions(), GP_OBJECT_MANIPULATE); } diff --git a/indra/newview/llpanelpresetscamerapulldown.cpp b/indra/newview/llpanelpresetscamerapulldown.cpp index 183123e534..4c9c30160c 100644 --- a/indra/newview/llpanelpresetscamerapulldown.cpp +++ b/indra/newview/llpanelpresetscamerapulldown.cpp @@ -127,7 +127,10 @@ void LLPanelPresetsCameraPulldown::onRowClick(const LLSD& user_data) LL_DEBUGS() << "selected '" << name << "'" << LL_ENDL; LLFloaterCamera::switchToPreset(name); - setVisible(FALSE); + // Scroll grabbed focus, drop it to prevent selection of parent menu + setFocus(FALSE); + + setVisible(FALSE); } else { diff --git a/indra/newview/llpanelpresetspulldown.cpp b/indra/newview/llpanelpresetspulldown.cpp index d52ad8056f..23e4fa8887 100644 --- a/indra/newview/llpanelpresetspulldown.cpp +++ b/indra/newview/llpanelpresetspulldown.cpp @@ -122,6 +122,9 @@ void LLPanelPresetsPulldown::onRowClick(const LLSD& user_data) LL_DEBUGS() << "selected '" << name << "'" << LL_ENDL; LLPresetsManager::getInstance()->loadPreset(PRESETS_GRAPHIC, name); + // Scroll grabbed focus, drop it to prevent selection of parent menu + setFocus(FALSE); + setVisible(FALSE); } else diff --git a/indra/newview/llpanelpulldown.cpp b/indra/newview/llpanelpulldown.cpp index 4de6ee8182..075278f44c 100644 --- a/indra/newview/llpanelpulldown.cpp +++ b/indra/newview/llpanelpulldown.cpp @@ -51,6 +51,7 @@ void LLPanelPulldown::onMouseEnter(S32 x, S32 y, MASK mask) /*virtual*/ void LLPanelPulldown::onTopLost() { + setFocus(FALSE); // drop focus to prevent transfer to parent setVisible(FALSE); } @@ -113,6 +114,7 @@ void LLPanelPulldown::draw() if (alpha == 0.f) { + setFocus(FALSE); // drop focus to prevent transfer to parent setVisible(FALSE); } } diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index c6bb2f19dd..140cbbedbe 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -187,82 +187,8 @@ void LLScriptEditor::drawSelectionBackground() // Draw selection even if we don't have keyboard focus for search/replace if( hasSelection() && !mLineInfoList.empty()) { - std::vector<LLRect> selection_rects; - - S32 selection_left = llmin( mSelectionStart, mSelectionEnd ); - S32 selection_right = llmax( mSelectionStart, mSelectionEnd ); - - // Skip through the lines we aren't drawing. - LLRect content_display_rect = getVisibleDocumentRect(); - - // binary search for line that starts before top of visible buffer - line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, LLTextBase::compare_bottom()); - line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, LLTextBase::compare_top()); - - bool done = false; - - // Find the coordinates of the selected area - for (;line_iter != end_iter && !done; ++line_iter) - { - // is selection visible on this line? - if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) - { - segment_set_t::iterator segment_iter; - S32 segment_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); - - LLRect selection_rect; - selection_rect.mLeft = line_iter->mRect.mLeft; - selection_rect.mRight = line_iter->mRect.mLeft; - selection_rect.mBottom = line_iter->mRect.mBottom; - selection_rect.mTop = line_iter->mRect.mTop; - - for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) - { - LLTextSegmentPtr segmentp = *segment_iter; - - S32 segment_line_start = segmentp->getStart() + segment_offset; - S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); - - if (segment_line_start > segment_line_end) break; - - S32 segment_width = 0; - S32 segment_height = 0; - - // if selection after beginning of segment - if(selection_left >= segment_line_start) - { - S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; - segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height); - selection_rect.mLeft += segment_width; - } - - // if selection_right == segment_line_end then that means we are the first character of the next segment - // or first character of the next line, in either case we want to add the length of the current segment - // to the selection rectangle and continue. - // if selection right > segment_line_end then selection spans end of current segment... - if (selection_right >= segment_line_end) - { - // extend selection slightly beyond end of line - // to indicate selection of newline character (use "n" character to determine width) - S32 num_chars = segment_line_end - segment_line_start; - segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height); - selection_rect.mRight += segment_width; - } - // else if selection ends on current segment... - else - { - S32 num_chars = selection_right - segment_line_start; - segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height); - selection_rect.mRight += segment_width; - - break; - } - } - selection_rects.push_back(selection_rect); - } - } - + std::vector<LLRect> selection_rects = getSelctionRects(); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); const LLColor4& color = mReadOnly ? mReadOnlyFgColor : mFgColor; F32 alpha = hasFocus() ? 0.7f : 0.3f; @@ -272,6 +198,7 @@ void LLScriptEditor::drawSelectionBackground() (1.f + color.mV[VGREEN]) * 0.5f, (1.f + color.mV[VBLUE]) * 0.5f, alpha); + LLRect content_display_rect = getVisibleDocumentRect(); for (std::vector<LLRect>::iterator rect_it = selection_rects.begin(); rect_it != selection_rects.end(); diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h index 8c0d70c16c..ec0de1fbfd 100644 --- a/indra/newview/llsky.h +++ b/indra/newview/llsky.h @@ -39,7 +39,6 @@ class LLViewerCamera; class LLVOWLSky; -class LLVOWLClouds; class LLSky diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 8134187c21..ed7e18fadc 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -233,7 +233,7 @@ bool LLSnapshotLivePreview::setSnapshotQuality(S32 quality, bool set_by_user) return false; } -void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y) +void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y, LLColor4 alpha_color) { F32 line_width ; glGetFloatv(GL_LINE_WIDTH, &line_width) ; @@ -246,7 +246,6 @@ void LLSnapshotLivePreview::drawPreviewRect(S32 offset_x, S32 offset_y) //draw four alpha rectangles to cover areas outside of the snapshot image if(!mKeepAspectRatio) { - LLColor4 alpha_color(0.5f, 0.5f, 0.5f, 0.8f) ; S32 dwl = 0, dwr = 0 ; if(mThumbnailWidth > mPreviewRect.getWidth()) { diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h index 683cd016d8..1f81307976 100644 --- a/indra/newview/llsnapshotlivepreview.h +++ b/indra/newview/llsnapshotlivepreview.h @@ -112,7 +112,7 @@ public: BOOL setThumbnailImageSize() ; void generateThumbnailImage(BOOL force_update = FALSE) ; void resetThumbnailImage() { mThumbnailImage = NULL ; } - void drawPreviewRect(S32 offset_x, S32 offset_y) ; + void drawPreviewRect(S32 offset_x, S32 offset_y, LLColor4 alpha_color = LLColor4(0.5f, 0.5f, 0.5f, 0.8f)); void prepareFreezeFrame(); LLViewerTexture* getBigThumbnailImage(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 054e9530d4..1dd5c5cbe5 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1285,9 +1285,6 @@ bool idle_startup() // Initialize classes w/graphics stuff. // LLViewerStatsRecorder::instance(); // Since textures work in threads - gTextureList.doPrefetchImages(); - display_startup(); - LLSurface::initClasses(); display_startup(); @@ -1432,6 +1429,15 @@ bool idle_startup() if (STATE_SEED_CAP_GRANTED == LLStartUp::getStartupState()) { display_startup(); + + // These textures are not warrantied to be cached, so needs + // to hapen with caps granted + gTextureList.doPrefetchImages(); + + // will init images, should be done with caps, but before gSky.init() + LLEnvironment::getInstance()->initSingleton(); + + display_startup(); update_texture_fetch(); display_startup(); @@ -2526,8 +2532,6 @@ void use_circuit_callback(void**, S32 result) void register_viewer_callbacks(LLMessageSystem* msg) { msg->setHandlerFuncFast(_PREHASH_LayerData, process_layer_data ); - msg->setHandlerFuncFast(_PREHASH_ImageData, LLViewerTextureList::receiveImageHeader ); - msg->setHandlerFuncFast(_PREHASH_ImagePacket, LLViewerTextureList::receiveImagePacket ); msg->setHandlerFuncFast(_PREHASH_ObjectUpdate, process_object_update ); msg->setHandlerFunc("ObjectUpdateCompressed", process_compressed_object_update ); msg->setHandlerFunc("ObjectUpdateCached", process_cached_object_update ); @@ -2913,6 +2917,7 @@ void reset_login() gAgentWearables.cleanup(); gAgentCamera.cleanup(); gAgent.cleanup(); + gSky.cleanup(); // mVOSkyp is an inworld object. LLWorld::getInstance()->resetClass(); if ( gViewerWindow ) diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp index aeefcd6fb8..449d3d95c8 100644 --- a/indra/newview/llsurfacepatch.cpp +++ b/indra/newview/llsurfacepatch.cpp @@ -714,7 +714,7 @@ BOOL LLSurfacePatch::updateTexture() { mVObjp->dirtyGeom(); gPipeline.markGLRebuild(mVObjp); - return TRUE; + return !mSTexUpdate; } } } diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 67da311493..5c6f7254f2 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -1646,6 +1646,12 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) { gDirUtilp->deleteFilesInDir(dirname, mask); } +#if LL_WINDOWS + // Texture cache can be large and can take a while to remove + // assure OS that processes is alive and not hanging + MSG msg; + PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE | PM_NOYIELD); +#endif } gDirUtilp->deleteFilesInDir(mTexturesDirName, mask); // headers, fast cache if (purge_directories) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 8923f53cf5..6f6ca2be9b 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -282,7 +282,6 @@ static const char* e_state_name[] = "LOAD_FROM_TEXTURE_CACHE", "CACHE_POST", "LOAD_FROM_NETWORK", - "LOAD_FROM_SIMULATOR", "WAIT_HTTP_RESOURCE", "WAIT_HTTP_RESOURCE2", "SEND_HTTP_REQ", @@ -456,7 +455,6 @@ public: LOAD_FROM_TEXTURE_CACHE, CACHE_POST, LOAD_FROM_NETWORK, - LOAD_FROM_SIMULATOR, WAIT_HTTP_RESOURCE, // Waiting for HTTP resources WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources SEND_HTTP_REQ, // Commit to sending as HTTP @@ -497,8 +495,6 @@ private: // Locks: Mw void clearPackets(); - // Locks: Mw - void setupPacketData(); // Locks: Mw (ctor invokes without lock) U32 calcWorkPriority(); @@ -507,10 +503,6 @@ private: void removeFromCache(); // Threads: Ttf - // Locks: Mw - bool processSimulatorPackets(); - - // Threads: Ttf bool writeToCacheComplete(); // Threads: Ttf @@ -612,8 +604,7 @@ private: BOOL mHaveAllData; BOOL mInLocalCache; BOOL mInCache; - bool mCanUseHTTP, - mCanUseNET ; //can get from asset server. + bool mCanUseHTTP; S32 mRetryAttempt; S32 mActiveCount; LLCore::HttpStatus mGetStatus; @@ -885,7 +876,6 @@ const char* sStateDescs[] = { "LOAD_FROM_TEXTURE_CACHE", "CACHE_POST", "LOAD_FROM_NETWORK", - "LOAD_FROM_SIMULATOR", "WAIT_HTTP_RESOURCE", "WAIT_HTTP_RESOURCE2", "SEND_HTTP_REQ", @@ -897,7 +887,7 @@ const char* sStateDescs[] = { "DONE" }; -const std::set<S32> LOGGED_STATES = { LLTextureFetchWorker::LOAD_FROM_TEXTURE_CACHE, LLTextureFetchWorker::LOAD_FROM_NETWORK, LLTextureFetchWorker::LOAD_FROM_SIMULATOR, +const std::set<S32> LOGGED_STATES = { LLTextureFetchWorker::LOAD_FROM_TEXTURE_CACHE, LLTextureFetchWorker::LOAD_FROM_NETWORK, LLTextureFetchWorker::WAIT_HTTP_REQ, LLTextureFetchWorker::DECODE_IMAGE_UPDATE, LLTextureFetchWorker::WAIT_ON_WRITE }; // static @@ -972,8 +962,6 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mResourceWaitCount(0U), mFetchRetryPolicy(10.0,3600.0,2.0,10) { - mCanUseNET = mUrl.empty() ; - calcWorkPriority(); mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; // LL_INFOS(LOG_TXT) << "Create: " << mID << " mHost:" << host << " Discard=" << discard << LL_ENDL; @@ -1037,39 +1025,6 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } -// Locks: Mw -void LLTextureFetchWorker::setupPacketData() -{ - S32 data_size = 0; - if (mFormattedImage.notNull()) - { - data_size = mFormattedImage->getDataSize(); - } - if (data_size > 0) - { - // Only used for simulator requests - mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1; - if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size) - { - LL_WARNS(LOG_TXT) << "Bad CACHED TEXTURE size: " << data_size << " removing." << LL_ENDL; - removeFromCache(); - resetFormattedData(); - clearPackets(); - } - else if (mFileSize > 0) - { - mLastPacket = mFirstPacket-1; - mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1; - } - else - { - // This file was cached using HTTP so we have to refetch the first packet - resetFormattedData(); - clearPackets(); - } - } -} - // Locks: Mw (ctor invokes without lock) U32 LLTextureFetchWorker::calcWorkPriority() { @@ -1177,13 +1132,13 @@ bool LLTextureFetchWorker::doWork(S32 param) if(mImagePriority < F_ALMOST_ZERO) { - if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR) + if (mState == INIT || mState == LOAD_FROM_NETWORK) { LL_DEBUGS(LOG_TXT) << mID << " abort: mImagePriority < F_ALMOST_ZERO" << LL_ENDL; return true; // abort } } - if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP) + if(mState > CACHE_POST && !mCanUseHTTP) { //nowhere to get data, abort. LL_WARNS(LOG_TXT) << mID << " abort, nowhere to get data" << LL_ENDL; @@ -1387,10 +1342,14 @@ bool LLTextureFetchWorker::doWork(S32 param) if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. { LLViewerRegion* region = NULL; - if (mHost.isInvalid()) - region = gAgent.getRegion(); - else - region = LLWorld::getInstance()->getRegion(mHost); + if (mHost.isInvalid()) + { + region = gAgent.getRegion(); + } + else if (LLWorld::instanceExists()) + { + region = LLWorld::getInstance()->getRegion(mHost); + } if (region) { @@ -1408,14 +1367,14 @@ bool LLTextureFetchWorker::doWork(S32 param) else { mCanUseHTTP = false ; - LL_DEBUGS(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL; + LL_WARNS(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL; } } else { // This will happen if not logged in or if a region deoes not have HTTP Texture enabled //LL_WARNS(LOG_TXT) << "Region not found for host: " << mHost << LL_ENDL; - LL_DEBUGS(LOG_TXT) << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; + LL_WARNS(LOG_TXT) << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; mCanUseHTTP = false; } } @@ -1434,84 +1393,12 @@ bool LLTextureFetchWorker::doWork(S32 param) } // don't return, fall through to next state } - else if (mSentRequest == UNSENT && mCanUseNET) - { - // Add this to the network queue and sit here. - // LLTextureFetch::update() will send off a request which will change our state - mWriteToCacheState = CAN_WRITE ; - mRequestedSize = mDesiredSize; - mRequestedDiscard = mDesiredDiscard; - mSentRequest = QUEUED; - mFetcher->addToNetworkQueue(this); - recordTextureStart(false); - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - - return false; - } else { - // Shouldn't need to do anything here - //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); - // Make certain this is in the network queue - //mFetcher->addToNetworkQueue(this); - //recordTextureStart(false); - //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - return false; } } - if (mState == LOAD_FROM_SIMULATOR) - { - if (mFormattedImage.isNull()) - { - mFormattedImage = new LLImageJ2C; - } - if (processSimulatorPackets()) - { - // Capture some measure of total size for metrics - F64 byte_count = 0; - if (mLastPacket >= mFirstPacket) - { - for (S32 i=mFirstPacket; i<=mLastPacket; i++) - { - llassert_always((i>=0) && (i<mPackets.size())); - if (mPackets[i]) - { - byte_count += mPackets[i]->mSize; - } - } - } - - LL_DEBUGS(LOG_TXT) << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL; - mFetcher->removeFromNetworkQueue(this, false); - if (mFormattedImage.isNull() || !mFormattedImage->getDataSize()) - { - // processSimulatorPackets() failed -// LL_WARNS(LOG_TXT) << "processSimulatorPackets() failed to load buffer" << LL_ENDL; - LL_WARNS(LOG_TXT) << mID << " processSimulatorPackets() failed to load buffer" << LL_ENDL; - return true; // failed - } - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - if (mLoadedDiscard < 0) - { - LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard - << ", should be >=0" << LL_ENDL; - } - setState(DECODE_IMAGE); - mWriteToCacheState = SHOULD_WRITE; - - recordTextureDone(false, byte_count); - } - else - { - mFetcher->addToNetworkQueue(this); // failsafe - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - recordTextureStart(false); - } - return false; - } - if (mState == WAIT_HTTP_RESOURCE) { // NOTE: @@ -1553,8 +1440,6 @@ bool LLTextureFetchWorker::doWork(S32 param) LL_WARNS(LOG_TXT) << mID << " abort: SEND_HTTP_REQ but !mCanUseHTTP" << LL_ENDL; return true; // abort } - - mFetcher->removeFromNetworkQueue(this, false); S32 cur_size = 0; if (mFormattedImage.notNull()) @@ -1701,17 +1586,6 @@ bool LLTextureFetchWorker::doWork(S32 param) } return true; } - - // roll back to try UDP - if (mCanUseNET) - { - setState(INIT); - mCanUseHTTP = false; - mUrl.clear(); - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - releaseHttpSemaphore(); - return false; - } } else if (http_service_unavail == mGetStatus) { @@ -2284,69 +2158,6 @@ void LLTextureFetchWorker::removeFromCache() // Threads: Ttf // Locks: Mw -bool LLTextureFetchWorker::processSimulatorPackets() -{ - if (mFormattedImage.isNull() || mRequestedSize < 0) - { - // not sure how we got here, but not a valid state, abort! - llassert_always(mDecodeHandle == 0); - mFormattedImage = NULL; - return true; - } - - if (mLastPacket >= mFirstPacket) - { - S32 buffer_size = mFormattedImage->getDataSize(); - for (S32 i = mFirstPacket; i<=mLastPacket; i++) - { - llassert_always((i>=0) && (i<mPackets.size())); - llassert_always(mPackets[i]); - buffer_size += mPackets[i]->mSize; - } - bool have_all_data = mLastPacket >= mTotalPackets-1; - if (mRequestedSize <= 0) - { - // We received a packed but haven't requested anything yet (edge case) - // Return true (we're "done") since we didn't request anything - return true; - } - if (buffer_size >= mRequestedSize || have_all_data) - { - /// We have enough (or all) data - if (have_all_data) - { - mHaveAllData = TRUE; - } - S32 cur_size = mFormattedImage->getDataSize(); - if (buffer_size > cur_size) - { - /// We have new data - U8* buffer = (U8*)ll_aligned_malloc_16(buffer_size); - S32 offset = 0; - if (cur_size > 0 && mFirstPacket > 0) - { - memcpy(buffer, mFormattedImage->getData(), cur_size); - offset = cur_size; - } - for (S32 i=mFirstPacket; i<=mLastPacket; i++) - { - memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize); - offset += mPackets[i]->mSize; - } - // NOTE: setData releases current data - mFormattedImage->setData(buffer, buffer_size); - } - mLoadedDiscard = mRequestedDiscard; - return true; - } - } - return false; -} - -////////////////////////////////////////////////////////////////////////////// - -// Threads: Ttf -// Locks: Mw S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, bool partial, bool success) { @@ -2813,40 +2624,6 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const } -// Threads: T* (but Ttf in practice) - -// protected -void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) -{ - lockQueue(); // +Mfq - bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ; - unlockQueue(); // -Mfq - - LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq - if (in_request_map) - { - // only add to the queue if in the request map - // i.e. a delete has not been requested - mNetworkQueue.insert(worker->mID); - } - for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); - iter1 != mCancelQueue.end(); ++iter1) - { - iter1->second.erase(worker->mID); - } -} // -Mfnq - -// Threads: T* -void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) -{ - LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq - size_t erased = mNetworkQueue.erase(worker->mID); - if (cancel && erased > 0) - { - mCancelQueue[worker->mHost].insert(worker->mID); - } -} // -Mfnq - // Threads: T* // // protected @@ -2880,7 +2657,6 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; - removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; worker->scheduleDelete(); @@ -2908,7 +2684,6 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; - removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; worker->scheduleDelete(); @@ -3197,17 +2972,6 @@ size_t LLTextureFetch::update(F32 max_time_ms) size_t res = LLWorkerThread::update(max_time_ms); - if (!mDebugPause) - { - // this is the startup state when send_complete_agent_movement() message is sent. - // Before this, the RequestImages message sent by sendRequestListToSimulators - // won't work so don't bother trying - if (LLStartUp::getStartupState() > STATE_AGENT_SEND) - { - sendRequestListToSimulators(); - } - } - if (!mThreaded) { commonUpdate(); @@ -3298,202 +3062,6 @@ void LLTextureFetch::threadedUpdate() ////////////////////////////////////////////////////////////////////////////// -// Threads: Tmain -void LLTextureFetch::sendRequestListToSimulators() -{ - // All requests - const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps - - // Sim requests - const S32 IMAGES_PER_REQUEST = 50; - const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp - const F32 MIN_REQUEST_TIME = 1.0f; - const F32 MIN_DELTA_PRIORITY = 1000.f; - - // Periodically, gather the list of textures that need data from the network - // And send the requests out to the simulators - static LLFrameTimer timer; - if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME) - { - return; - } - timer.reset(); - - // Send requests - typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t; - typedef std::map< LLHost, request_list_t > work_request_map_t; - work_request_map_t requests; - { - LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq - for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) - { - queue_t::iterator curiter = iter++; - LLTextureFetchWorker* req = getWorker(*curiter); - if (!req) - { - mNetworkQueue.erase(curiter); - continue; // paranoia - } - if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && - (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) - { - // We already received our URL, remove from the queue - LL_WARNS(LOG_TXT) << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << LL_ENDL; - mNetworkQueue.erase(curiter); - continue; - } - if (req->mID == mDebugID) - { - mDebugCount++; // for setting breakpoints - } - if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && - req->mTotalPackets > 0 && - req->mLastPacket >= req->mTotalPackets-1) - { - // We have all the packets... make sure this is high priority -// req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); - continue; - } - F32 elapsed = req->mRequestedDeltaTimer.getElapsedTimeF32(); - { - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) - { - requests[req->mHost].insert(req); - } - } - } - } // -Mfnq - - for (work_request_map_t::iterator iter1 = requests.begin(); - iter1 != requests.end(); ++iter1) - { - LLHost host = iter1->first; - // invalid host = use agent host - if (host.isInvalid()) - { - host = gAgent.getRegionHost(); - } - - S32 sim_request_count = 0; - - for (request_list_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) - { - LLTextureFetchWorker* req = *iter2; - if (gMessageSystem) - { - if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) - { - // Initialize packet data based on data read from cache - req->lockWorkMutex(); // +Mw - req->setupPacketData(); - req->unlockWorkMutex(); // -Mw - } - if (0 == sim_request_count) - { - gMessageSystem->newMessageFast(_PREHASH_RequestImage); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - S32 packet = req->mLastPacket + 1; - gMessageSystem->nextBlockFast(_PREHASH_RequestImage); - gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID); - gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mDesiredDiscard); - gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority); - gMessageSystem->addU32Fast(_PREHASH_Packet, packet); - gMessageSystem->addU8Fast(_PREHASH_Type, req->mType); -// LL_INFOS(LOG_TXT) << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard -// << " Packet: " << packet << " Priority: " << req->mImagePriority << LL_ENDL; - - static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog", false); - static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator", false); - if (log_to_viewer_log || log_to_sim) - { - mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime()); - mTextureInfo.setRequestOffset(req->mID, 0); - mTextureInfo.setRequestSize(req->mID, 0); - mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); - } - - req->lockWorkMutex(); // +Mw - req->mSentRequest = LLTextureFetchWorker::SENT_SIM; - req->mSimRequestedDiscard = req->mDesiredDiscard; - req->mRequestedPriority = req->mImagePriority; - req->mRequestedDeltaTimer.reset(); - req->unlockWorkMutex(); // -Mw - sim_request_count++; - if (sim_request_count >= IMAGES_PER_REQUEST) - { -// LL_INFOS(LOG_TXT) << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << LL_ENDL; - - gMessageSystem->sendSemiReliable(host, NULL, NULL); - sim_request_count = 0; - } - } - } - if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST) - { -// LL_INFOS(LOG_TXT) << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << LL_ENDL; - gMessageSystem->sendSemiReliable(host, NULL, NULL); - sim_request_count = 0; - } - } - - // Send cancelations - { - LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq - if (gMessageSystem && !mCancelQueue.empty()) - { - for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); - iter1 != mCancelQueue.end(); ++iter1) - { - LLHost host = iter1->first; - if (host.isInvalid()) - { - host = gAgent.getRegionHost(); - } - S32 request_count = 0; - for (queue_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) - { - if (0 == request_count) - { - gMessageSystem->newMessageFast(_PREHASH_RequestImage); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - gMessageSystem->nextBlockFast(_PREHASH_RequestImage); - gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); - gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); - gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); - gMessageSystem->addU32Fast(_PREHASH_Packet, 0); - gMessageSystem->addU8Fast(_PREHASH_Type, 0); -// LL_INFOS(LOG_TXT) << "CANCELING IMAGE REQUEST: " << (*iter2) << LL_ENDL; - - request_count++; - if (request_count >= IMAGES_PER_REQUEST) - { - gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; - } - } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { - gMessageSystem->sendSemiReliable(host, NULL, NULL); - } - } - mCancelQueue.clear(); - } - } // -Mfnq -} - -////////////////////////////////////////////////////////////////////////////// - // Threads: T* // Locks: Mw bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) @@ -3556,138 +3124,6 @@ void LLTextureFetchWorker::setState(e_state new_state) mState = new_state; } -// Threads: T* -bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, - U16 data_size, U8* data) -{ - LLTextureFetchWorker* worker = getWorker(id); - bool res = true; - - ++mPacketCount; - - if (!worker) - { -// LL_WARNS(LOG_TXT) << "Received header for non active worker: " << id << LL_ENDL; - res = false; - } - else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK || - worker->mSentRequest != LLTextureFetchWorker::SENT_SIM) - { -// LL_WARNS(LOG_TXT) << "receiveImageHeader for worker: " << id -// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] -// << " sent: " << worker->mSentRequest << LL_ENDL; - res = false; - } - else if (worker->mLastPacket != -1) - { - // check to see if we've gotten this packet before -// LL_WARNS(LOG_TXT) << "Received duplicate header for: " << id << LL_ENDL; - res = false; - } - else if (!data_size) - { -// LL_WARNS(LOG_TXT) << "Img: " << id << ":" << " Empty Image Header" << LL_ENDL; - res = false; - } - if (!res) - { - mNetworkQueueMutex.lock(); // +Mfnq - ++mBadPacketCount; - mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock(); // -Mfnq - return false; - } - - LLViewerStatsRecorder::instance().textureFetch(data_size); - LLViewerStatsRecorder::instance().log(0.1f); - - worker->lockWorkMutex(); - - - // Copy header data into image object - worker->mImageCodec = codec; - worker->mTotalPackets = packets; - worker->mFileSize = (S32)totalbytes; - llassert_always(totalbytes > 0); - llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); - res = worker->insertPacket(0, data, data_size); - worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); - worker->setState(LLTextureFetchWorker::LOAD_FROM_SIMULATOR); - worker->unlockWorkMutex(); // -Mw - return res; -} - - -// Threads: T* -bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) -{ - LLTextureFetchWorker* worker = getWorker(id); - bool res = true; - - ++mPacketCount; - - if (!worker) - { -// LL_WARNS(LOG_TXT) << "Received packet " << packet_num << " for non active worker: " << id << LL_ENDL; - res = false; - } - else if (worker->mLastPacket == -1) - { -// LL_WARNS(LOG_TXT) << "Received packet " << packet_num << " before header for: " << id << LL_ENDL; - res = false; - } - else if (!data_size) - { -// LL_WARNS(LOG_TXT) << "Img: " << id << ":" << " Empty Image Header" << LL_ENDL; - res = false; - } - if (!res) - { - mNetworkQueueMutex.lock(); // +Mfnq - ++mBadPacketCount; - mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock(); // -Mfnq - return false; - } - - LLViewerStatsRecorder::instance().textureFetch(data_size); - LLViewerStatsRecorder::instance().log(0.1f); - - worker->lockWorkMutex(); - - - res = worker->insertPacket(packet_num, data, data_size); - - if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) || - (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK)) - { - worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); - worker->setState(LLTextureFetchWorker::LOAD_FROM_SIMULATOR); - } - else - { -// LL_WARNS(LOG_TXT) << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id -// << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << LL_ENDL; - removeFromNetworkQueue(worker, true); // failsafe - } - - if (packet_num >= (worker->mTotalPackets - 1)) - { - static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog", false); - static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator", false); - - if (log_to_viewer_log || log_to_sim) - { - U64Microseconds timeNow = LLTimer::getTotalTime(); - mTextureInfoMainThread.setRequestSize(id, worker->mFileSize); - mTextureInfoMainThread.setRequestCompleteTimeAndLog(id, timeNow); - } - } - worker->unlockWorkMutex(); // -Mw - - return res; -} - ////////////////////////////////////////////////////////////////////////////// // Threads: T* @@ -3726,13 +3162,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r request_dtime = worker->mRequestedDeltaTimer.getElapsedTimeF32(); if (worker->mFileSize > 0) { - if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) - { - S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE; - data_size = llmax(data_size, 0); - data_progress = (F32)data_size / (F32)worker->mFileSize; - } - else if (worker->mFormattedImage.notNull()) + if (worker->mFormattedImage.notNull()) { data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize; } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index e2d2aa365c..611a7d6419 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -101,12 +101,6 @@ public: // Threads: T* bool updateRequestPriority(const LLUUID& id, F32 priority); - // Threads: T* - bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); - - // Threads: T* - bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); - // Threads: T* (but not safe) void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } @@ -227,12 +221,6 @@ public: // ---------------------------------- protected: - // Threads: T* (but Ttf in practice) - void addToNetworkQueue(LLTextureFetchWorker* worker); - - // Threads: T* - void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); - // Threads: T* void addToHTTPQueue(const LLUUID& id); @@ -251,9 +239,6 @@ protected: bool runCondition(); private: - // Threads: Tmain - void sendRequestListToSimulators(); - // Threads: Ttf /*virtual*/ void startThread(void); @@ -319,7 +304,7 @@ public: private: LLMutex mQueueMutex; //to protect mRequestMap and mCommands only - LLMutex mNetworkQueueMutex; //to protect mNetworkQueue, mHTTPTextureQueue and mCancelQueue. + LLMutex mNetworkQueueMutex; //to protect mHTTPTextureQueue LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; @@ -330,10 +315,8 @@ private: // Set of requests that require network data typedef std::set<LLUUID> queue_t; - queue_t mNetworkQueue; // Mfnq queue_t mHTTPTextureQueue; // Mfnq typedef std::map<LLHost,std::set<LLUUID> > cancel_queue_t; - cancel_queue_t mCancelQueue; // Mfnq F32 mTextureBandwidth; // <none> F32 mMaxBandwidth; // Mfnq LLTextureInfo mTextureInfo; diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index b74577315e..4431f6527d 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -234,7 +234,6 @@ void LLTextureBar::draw() { "DSK", LLColor4::cyan }, // LOAD_FROM_TEXTURE_CACHE { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK - { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE2 { "REQ", LLColor4::yellow },// SEND_HTTP_REQ @@ -614,7 +613,7 @@ void LLGLTexMemBar::draw() 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); + &x_right, /*use_ellipses*/FALSE, /*use_color*/FALSE); F32Kilobits bandwidth(LLAppViewer::getTextureFetch()->getTextureBandwidth()); F32Kilobits max_bandwidth(gSavedSettings.getF32("ThrottleBandwidthKBPS")); diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index 01d799dcd5..752fc6f3f3 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -44,6 +44,7 @@ #include "llagent.h" // HACK for destinations guide on startup #include "llfloaterreg.h" // HACK for destinations guide on startup #include "llviewercontrol.h" // HACK for destinations guide on startup +#include "llinventorymodel.h" // HACK to disable starter avatars button for NUX #include <boost/foreach.hpp> @@ -319,6 +320,22 @@ bool LLToolBarView::loadToolbars(bool force_default) } } } + + // SL-18581: Don't show the starter avatar toolbar button for NUX users + LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS)); + if (gAgent.isFirstLogin() + && my_outfits_cat != NULL + && my_outfits_cat->getDescendentCount() > 0) + { + for (S32 i = LLToolBarEnums::TOOLBAR_FIRST; i <= LLToolBarEnums::TOOLBAR_LAST; i++) + { + if (mToolbars[i]) + { + mToolbars[i]->removeCommand(LLCommandId("avatar")); + } + } + } + mToolbarsLoaded = true; return true; } diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 14fae8d035..232b52a3f9 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -868,11 +868,6 @@ void LLViewerAssetUpload::AssetInventoryUploadCoproc(LLCoreHttpUtil::HttpCorouti { floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", success).with("msg", "inventory"))); } - LLFloater* floater_outfit_snapshot = LLFloaterReg::findInstance("outfit_snapshot"); - if (uploadInfo->getAssetType() == LLAssetType::AT_TEXTURE && floater_outfit_snapshot && floater_outfit_snapshot->isShown()) - { - floater_outfit_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", success).with("msg", "inventory"))); - } } //========================================================================= @@ -951,11 +946,5 @@ void LLViewerAssetUpload::HandleUploadError(LLCore::HttpStatus status, LLSD &res floater_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", false).with("msg", "inventory"))); } } - - LLFloater* floater_outfit_snapshot = LLFloaterReg::findInstance("outfit_snapshot"); - if (uploadInfo->getAssetType() == LLAssetType::AT_TEXTURE && floater_outfit_snapshot && floater_outfit_snapshot->isShown()) - { - floater_outfit_snapshot->notify(LLSD().with("set-finished", LLSD().with("ok", false).with("msg", "inventory"))); - } } diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index f810e5f4ef..cc73f7ca80 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -496,7 +496,20 @@ void audio_update_listener() if (gAudiop) { // update listener position because agent has moved - LLVector3d lpos_global = gAgentCamera.getCameraPositionGlobal(); + static LLUICachedControl<S32> mEarLocation("MediaSoundsEarLocation", 0); + LLVector3d ear_position; + switch(mEarLocation) + { + case 0: + default: + ear_position = gAgentCamera.getCameraPositionGlobal(); + break; + + case 1: + ear_position = gAgent.getPositionGlobal(); + break; + } + LLVector3d lpos_global = ear_position; LLVector3 lpos_global_f; lpos_global_f.setVec(lpos_global); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index bc46a61fb0..34c9dae4bb 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -616,7 +616,7 @@ bool toggle_show_navigation_panel(const LLSD& newvalue) LLNavigationBar::getInstance()->setVisible(value); gSavedSettings.setBOOL("ShowMiniLocationPanel", !value); - + gViewerWindow->reshapeStatusBarContainer(); return true; } diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 06a6c5e373..32b32fdc02 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -103,7 +103,7 @@ #include "llfloaterobjectweights.h" #include "llfloateropenobject.h" #include "llfloateroutfitphotopreview.h" -#include "llfloateroutfitsnapshot.h" +#include "llfloatersimpleoutfitsnapshot.h" #include "llfloaterpathfindingcharacters.h" #include "llfloaterpathfindingconsole.h" #include "llfloaterpathfindinglinksets.h" @@ -158,6 +158,7 @@ #include "llfloaterimnearbychat.h" #include "llpanelblockedlist.h" #include "llpanelprofileclassifieds.h" +#include "llpanelemojicomplete.h" #include "llpreviewanim.h" #include "llpreviewgesture.h" #include "llpreviewnotecard.h" @@ -170,6 +171,7 @@ // *NOTE: Please add files in alphabetical order to keep merges easy. // handle secondlife:///app/openfloater/{NAME} URLs +const std::string FLOATER_PROFILE("profile"); class LLFloaterOpenHandler : public LLCommandHandler { public: @@ -185,7 +187,12 @@ public: } const std::string floater_name = LLURI::unescape(params[0].asString()); - LLFloaterReg::showInstance(floater_name); + LLSD key; + if (floater_name == FLOATER_PROFILE) + { + key["id"] = gAgentID; + } + LLFloaterReg::showInstance(floater_name, key); return true; } @@ -238,6 +245,7 @@ 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_complete", "floater_emoji_complete.xml", &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>); @@ -368,7 +376,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("scene_load_stats", "floater_scene_load_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSceneLoadStats>); LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>); LLFloaterReg::add("snapshot", "floater_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSnapshot>); - LLFloaterReg::add("outfit_snapshot", "floater_outfit_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOutfitSnapshot>); + LLFloaterReg::add("simple_outfit_snapshot", "floater_simple_outfit_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSimpleOutfitSnapshot>); LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearch>); LLFloaterReg::add("profile", "floater_profile.xml",(LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProfile>); LLFloaterReg::add("guidebook", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHowTo>); diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp index fd314ed3dc..b3bfb86b99 100644 --- a/indra/newview/llviewerjointattachment.cpp +++ b/indra/newview/llviewerjointattachment.cpp @@ -45,7 +45,7 @@ #include "llglheaders.h" extern LLPipeline gPipeline; -const F32 MAX_ATTACHMENT_DIST = 3.5f; // meters? +const F32 MAX_ATTACHMENT_DIST = 3.5f; // meters //----------------------------------------------------------------------------- // LLViewerJointAttachment() diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index a1cb152e1e..8570c0cd5d 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -3581,7 +3581,20 @@ void LLViewerMediaImpl::calculateInterest() LLVector3d global_delta = agent_global - obj_global ; mProximityDistance = global_delta.magVecSquared(); // use distance-squared because it's cheaper and sorts the same. - LLVector3d camera_delta = gAgentCamera.getCameraPositionGlobal() - obj_global; + static LLUICachedControl<S32> mEarLocation("MediaSoundsEarLocation", 0); + LLVector3d ear_position; + switch(mEarLocation) + { + case 0: + default: + ear_position = gAgentCamera.getCameraPositionGlobal(); + break; + + case 1: + ear_position = agent_global; + break; + } + LLVector3d camera_delta = ear_position - obj_global; mProximityCamera = camera_delta.magVec(); } } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 3573af40cb..6a0cf00a07 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -4334,6 +4334,10 @@ class LLLandSit : public view_listener_t { bool handleEvent(const LLSD& userdata) { + if (gAgent.isSitting()) + { + gAgent.standUp(); + } LLVector3d posGlobal = LLToolPie::getInstance()->getPick().mPosGlobal; LLQuaternion target_rot; @@ -9564,6 +9568,10 @@ 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/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index f1e2c06e0c..fdf1d04c09 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -38,7 +38,7 @@ #include "llfloatermap.h" #include "llfloatermodelpreview.h" #include "llfloatersnapshot.h" -#include "llfloateroutfitsnapshot.h" +#include "llfloatersimpleoutfitsnapshot.h" #include "llimage.h" #include "llimagebmp.h" #include "llimagepng.h" @@ -664,7 +664,7 @@ class LLFileEnableCloseAllWindows : public view_listener_t bool handleEvent(const LLSD& userdata) { LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance(); - LLFloaterOutfitSnapshot* floater_outfit_snapshot = LLFloaterOutfitSnapshot::findInstance(); + LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance(); bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain()) || (floater_outfit_snapshot && floater_outfit_snapshot->isInVisibleChain()); bool open_children = gFloaterView->allChildrenClosed() && !is_floaters_snapshot_opened; @@ -681,7 +681,7 @@ class LLFileCloseAllWindows : public view_listener_t LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance(); if (floater_snapshot) floater_snapshot->closeFloater(app_quitting); - LLFloaterOutfitSnapshot* floater_outfit_snapshot = LLFloaterOutfitSnapshot::findInstance(); + LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance(); if (floater_outfit_snapshot) floater_outfit_snapshot->closeFloater(app_quitting); if (gMenuHolder) gMenuHolder->hideMenus(); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index aad6c14b4d..7ac1df8e31 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -316,7 +316,8 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mLastUpdateType(OUT_UNKNOWN), mLastUpdateCached(FALSE), mCachedMuteListUpdateTime(0), - mCachedOwnerInMuteList(false) + mCachedOwnerInMuteList(false), + mRiggedAttachedWarned(false) { if (!is_global) { diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index bef8e3e7e3..0005cdf14e 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -702,6 +702,8 @@ public: // Replace textures with web pages on this object while drawing BOOL mRenderMedia; + bool mRiggedAttachedWarned; + // In bits S32 mBestUpdatePrecision; diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 75eb16c085..97dc916bfe 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1974,7 +1974,7 @@ void LLViewerParcelMgr::optionallyStartMusic(const std::string &music_url, const static LLCachedControl<bool> tentative_autoplay(gSavedSettings, "MediaTentativeAutoPlay", true); // only play music when you enter a new parcel if the UI control for this // was not *explicitly* stopped by the user. (part of SL-4878) - LLPanelNearByMedia* nearby_media_panel = gStatusBar->getNearbyMediaPanel(); + LLPanelNearByMedia* nearby_media_panel = gStatusBar ? gStatusBar->getNearbyMediaPanel() : NULL; LLViewerAudio* viewer_audio = LLViewerAudio::getInstance(); // ask mode //todo constants diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index e3ac56d0d3..8a11c5cf8f 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1118,7 +1118,7 @@ void LLViewerFetchedTexture::init(bool firstinit) mLoadedCallbackDesiredDiscardLevel = S8_MAX; mPauseLoadedCallBacks = FALSE; - mNeedsCreateTexture = FALSE; + mNeedsCreateTexture = false; mIsRawImageValid = FALSE; mRawDiscardLevel = INVALID_DISCARD_LEVEL; @@ -1400,12 +1400,12 @@ void LLViewerFetchedTexture::addToCreateTexture() { //just update some variables, not to create a real GL texture. createGLTexture(mRawDiscardLevel, mRawImage, 0, FALSE); - mNeedsCreateTexture = FALSE; + mNeedsCreateTexture = false; destroyRawImage(); } else if(!force_update && getDiscardLevel() > -1 && getDiscardLevel() <= mRawDiscardLevel) { - mNeedsCreateTexture = FALSE; + mNeedsCreateTexture = false; destroyRawImage(); } else @@ -1441,7 +1441,7 @@ void LLViewerFetchedTexture::addToCreateTexture() mRawDiscardLevel += i; if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0) { - mNeedsCreateTexture = FALSE; + mNeedsCreateTexture = false; destroyRawImage(); return; } @@ -1473,7 +1473,7 @@ BOOL LLViewerFetchedTexture::preCreateTexture(S32 usename/*= 0*/) destroyRawImage(); return FALSE; } - mNeedsCreateTexture = FALSE; + mNeedsCreateTexture = false; if (mRawImage.isNull()) { @@ -1609,14 +1609,14 @@ void LLViewerFetchedTexture::postCreateTexture() destroyRawImage(); } - mNeedsCreateTexture = FALSE; + mNeedsCreateTexture = false; } void LLViewerFetchedTexture::scheduleCreateTexture() { if (!mNeedsCreateTexture) { - mNeedsCreateTexture = TRUE; + mNeedsCreateTexture = true; if (preCreateTexture()) { #if LL_IMAGEGL_THREAD_CHECK @@ -1630,7 +1630,7 @@ void LLViewerFetchedTexture::scheduleCreateTexture() memcpy(data_copy, data, size); } #endif - mNeedsCreateTexture = TRUE; + mNeedsCreateTexture = true; auto mainq = LLImageGLThread::sEnabled ? mMainQueue.lock() : nullptr; if (mainq) { diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index b953d7006b..2f5e0d01df 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -27,6 +27,7 @@ #ifndef LL_LLVIEWERTEXTURE_H #define LL_LLVIEWERTEXTURE_H +#include "llatomic.h" #include "llgltexture.h" #include "lltimer.h" #include "llframetimer.h" @@ -528,7 +529,9 @@ protected: LLFrameTimer mStopFetchingTimer; // Time since mDecodePriority == 0.f. BOOL mInImageList; // TRUE if image is in list (in which case don't reset priority!) - BOOL mNeedsCreateTexture; + // This needs to be atomic, since it is written both in the main thread + // and in the GL image worker thread... HB + LLAtomicBool mNeedsCreateTexture; BOOL mForSculpt ; //a flag if the texture is used as sculpt data. BOOL mIsFetched ; //is loaded from remote or from cache, not generated locally. diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index da3c860ddb..93ae1670c8 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -45,11 +45,13 @@ #include "llxmltree.h" #include "message.h" +#include "lldrawpoolbump.h" // to init bumpmap images #include "lltexturecache.h" #include "lltexturefetch.h" #include "llviewercontrol.h" #include "llviewertexture.h" #include "llviewermedia.h" +#include "llviewernetwork.h" #include "llviewerregion.h" #include "llviewerstats.h" #include "pipeline.h" @@ -139,9 +141,6 @@ void LLViewerTextureList::doPreloadImages() //uv_test->setClamp(FALSE, FALSE); //uv_test->setMipFilterNearest(TRUE, TRUE); - // prefetch specific UUIDs - LLViewerTextureManager::getFetchedTexture(IMG_SHOT); - LLViewerTextureManager::getFetchedTexture(IMG_SMOKE_POOF); LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTextureFromFile("silhouette.j2c", FTT_LOCAL_FILE, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); if (image) { @@ -160,12 +159,6 @@ void LLViewerTextureList::doPreloadImages() image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } - image = LLViewerTextureManager::getFetchedTexture(DEFAULT_WATER_NORMAL, FTT_DEFAULT, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); - if (image) - { - image->setAddressMode(LLTexUnit::TAM_WRAP); - mImagePreloads.insert(image); - } image = LLViewerTextureManager::getFetchedTextureFromFile("transparent.j2c", FTT_LOCAL_FILE, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI, LLViewerTexture::FETCHED_TEXTURE, 0, 0, IMG_TRANSPARENT); if (image) @@ -198,7 +191,18 @@ void LLViewerTextureList::doPreloadImages() static std::string get_texture_list_name() { - return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "texture_list_" + gSavedSettings.getString("LoginLocation") + "." + gDirUtilp->getUserName() + ".xml"); + if (LLGridManager::getInstance()->isInProductionGrid()) + { + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, + "texture_list_" + gSavedSettings.getString("LoginLocation") + "." + gDirUtilp->getUserName() + ".xml"); + } + else + { + const std::string& grid_id_str = LLGridManager::getInstance()->getGridId(); + const std::string& grid_id_lower = utf8str_tolower(grid_id_str); + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, + "texture_list_" + gSavedSettings.getString("LoginLocation") + "." + gDirUtilp->getUserName() + "." + grid_id_lower + ".xml"); + } } void LLViewerTextureList::doPrefetchImages() @@ -207,6 +211,26 @@ void LLViewerTextureList::doPrefetchImages() gTextureTimer.start(); gTextureTimer.pause(); + // todo: do not load without getViewerAssetUrl() + // either fail login without caps or provide this + // in some other way, textures won't load otherwise + LLViewerFetchedTexture *imagep = findImage(DEFAULT_WATER_NORMAL, TEX_LIST_STANDARD); + if (!imagep) + { + // add it to mImagePreloads only once + imagep = LLViewerTextureManager::getFetchedTexture(DEFAULT_WATER_NORMAL, FTT_DEFAULT, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); + if (imagep) + { + imagep->setAddressMode(LLTexUnit::TAM_WRAP); + mImagePreloads.insert(imagep); + } + } + + LLViewerTextureManager::getFetchedTexture(IMG_SHOT); + LLViewerTextureManager::getFetchedTexture(IMG_SMOKE_POOF); + + LLStandardBumpmap::addstandard(); + if (LLAppViewer::instance()->getPurgeCache()) { // cache was purged, no point @@ -1501,152 +1525,6 @@ void LLViewerTextureList::updateMaxResidentTexMem(S32Megabytes mem) /////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_data) -{ - static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic", false) ; - - LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - - // Receive image header, copy into image object and decompresses - // if this is a one-packet image. - - LLUUID id; - - char ip_string[256]; - u32_to_ip_string(msg->getSenderIP(),ip_string); - - U32Bytes received_size ; - if (msg->getReceiveCompressedSize()) - { - received_size = (U32Bytes)msg->getReceiveCompressedSize() ; - } - else - { - received_size = (U32Bytes)msg->getReceiveSize() ; - } - add(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED, received_size); - add(LLStatViewer::TEXTURE_PACKETS, 1); - - U8 codec; - U16 packets; - U32 totalbytes; - msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); - msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, codec); - msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, packets); - msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, totalbytes); - - S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); - if (!data_size) - { - return; - } - if (data_size < 0) - { - // msg->getSizeFast() is probably trying to tell us there - // was an error. - LL_ERRS() << "image header chunk size was negative: " - << data_size << LL_ENDL; - return; - } - - // this buffer gets saved off in the packet list - U8 *data = new U8[data_size]; - msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); - - LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - if (!image) - { - delete [] data; - return; - } - if(log_texture_traffic) - { - gTotalTextureBytesPerBoostLevel[image->getBoostLevel()] += received_size ; - } - - //image->getLastPacketTimer()->reset(); - bool res = LLAppViewer::getTextureFetch()->receiveImageHeader(msg->getSender(), id, codec, packets, totalbytes, data_size, data); - if (!res) - { - delete[] data; - } -} - -// static -void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_data) -{ - static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic", false) ; - - LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - - // Receives image packet, copy into image object, - // checks if all packets received, decompresses if so. - - LLUUID id; - U16 packet_num; - - char ip_string[256]; - u32_to_ip_string(msg->getSenderIP(),ip_string); - - U32Bytes received_size ; - if (msg->getReceiveCompressedSize()) - { - received_size = (U32Bytes)msg->getReceiveCompressedSize() ; - } - else - { - received_size = (U32Bytes)msg->getReceiveSize() ; - } - - add(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED, F64Bytes(received_size)); - add(LLStatViewer::TEXTURE_PACKETS, 1); - - //llprintline("Start decode, image header..."); - msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); - msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num); - S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); - - if (!data_size) - { - return; - } - if (data_size < 0) - { - // msg->getSizeFast() is probably trying to tell us there - // was an error. - LL_ERRS() << "image data chunk size was negative: " - << data_size << LL_ENDL; - return; - } - if (data_size > MTUBYTES) - { - LL_ERRS() << "image data chunk too large: " << data_size << " bytes" << LL_ENDL; - return; - } - U8 *data = new U8[data_size]; - msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); - - LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - if (!image) - { - delete [] data; - return; - } - if(log_texture_traffic) - { - gTotalTextureBytesPerBoostLevel[image->getBoostLevel()] += received_size ; - } - - //image->getLastPacketTimer()->reset(); - bool res = LLAppViewer::getTextureFetch()->receiveImagePacket(msg->getSender(), id, packet_num, data_size, data); - if (!res) - { - delete[] data; - } -} - - // We've been that the asset server does not contain the requested image id. // static void LLViewerTextureList::processImageNotInDatabase(LLMessageSystem *msg,void **user_data) diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 6fb0d3552e..0018e78d45 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -98,8 +98,6 @@ public: const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); static LLPointer<LLImageJ2C> convertToUploadFile(LLPointer<LLImageRaw> raw_image, const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data ); - static void receiveImageHeader(LLMessageSystem *msg, void **user_data); - static void receiveImagePacket(LLMessageSystem *msg, void **user_data); public: LLViewerTextureList(); @@ -133,7 +131,9 @@ public: void updateMaxResidentTexMem(S32Megabytes mem); + // Local UI images void doPreloadImages(); + // Network images. Needs caps and cache to work void doPrefetchImages(); void clearFetchingRequests(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 8a7d5f12d0..f65deb4577 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -262,6 +262,8 @@ static const F32 MIN_UI_SCALE = 0.75f; static const F32 MAX_UI_SCALE = 7.0f; static const F32 MIN_DISPLAY_SCALE = 0.75f; +static const char KEY_MOUSELOOK = 'M'; + static LLCachedControl<std::string> sSnapshotBaseName(LLCachedControl<std::string>(gSavedPerAccountSettings, "SnapshotBaseName", "Snapshot")); static LLCachedControl<std::string> sSnapshotDir(LLCachedControl<std::string>(gSavedPerAccountSettings, "SnapshotBaseDir", "")); @@ -1006,7 +1008,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::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE); } } @@ -2232,31 +2234,36 @@ void LLViewerWindow::initWorldUI() // Force gFloaterTools to initialize LLFloaterReg::getInstance("build"); - // Status bar LLPanel* status_bar_container = getRootView()->getChild<LLPanel>("status_bar_container"); gStatusBar = new LLStatusBar(status_bar_container->getLocalRect()); - gStatusBar->setFollowsAll(); + gStatusBar->setFollows(FOLLOWS_LEFT | FOLLOWS_TOP | FOLLOWS_RIGHT); gStatusBar->setShape(status_bar_container->getLocalRect()); // sync bg color with menu bar gStatusBar->setBackgroundColor( gMenuBarView->getBackgroundColor().get() ); // add InBack so that gStatusBar won't be drawn over menu - status_bar_container->addChildInBack(gStatusBar); - status_bar_container->setVisible(TRUE); + status_bar_container->addChildInBack(gStatusBar, 2/*tab order, after menu*/); + status_bar_container->setVisible(TRUE); // Navigation bar - LLPanel* nav_bar_container = getRootView()->getChild<LLPanel>("nav_bar_container"); + LLView* nav_bar_container = getRootView()->getChild<LLView>("nav_bar_container"); LLNavigationBar* navbar = LLNavigationBar::getInstance(); navbar->setShape(nav_bar_container->getLocalRect()); navbar->setBackgroundColor(gMenuBarView->getBackgroundColor().get()); nav_bar_container->addChild(navbar); nav_bar_container->setVisible(TRUE); - + + if (!gSavedSettings.getBOOL("ShowNavbarNavigationPanel")) { navbar->setVisible(FALSE); } + else + { + reshapeStatusBarContainer(); + } + // Top Info bar LLPanel* topinfo_bar_container = getRootView()->getChild<LLPanel>("topinfo_bar_container"); @@ -2883,6 +2890,13 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if (keyboard_focus && !gFocusMgr.getKeystrokesOnly()) { + //Most things should fall through, but mouselook is an exception, + //don't switch to mouselook if any floater has focus + if ((key == KEY_MOUSELOOK) && !(mask & (MASK_CONTROL | MASK_ALT))) + { + return TRUE; + } + LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(keyboard_focus); if (cur_focus && cur_focus->acceptsTextInput()) { @@ -5842,6 +5856,27 @@ LLRect LLViewerWindow::getChatConsoleRect() return console_rect; } + +void LLViewerWindow::reshapeStatusBarContainer() +{ + LLPanel* status_bar_container = getRootView()->getChild<LLPanel>("status_bar_container"); + LLView* nav_bar_container = getRootView()->getChild<LLView>("nav_bar_container"); + + S32 new_height = status_bar_container->getRect().getHeight(); + S32 new_width = status_bar_container->getRect().getWidth(); + + if (gSavedSettings.getBOOL("ShowNavbarNavigationPanel")) + { + // Navigation bar is outside visible area, expand status_bar_container to show it + new_height += nav_bar_container->getRect().getHeight(); + } + else + { + // collapse status_bar_container + new_height -= nav_bar_container->getRect().getHeight(); + } + status_bar_container->reshape(new_width, new_height, TRUE); +} //---------------------------------------------------------------------------- diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 979a560508..1927e01ddb 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -177,6 +177,8 @@ public: bool getUIVisibility(); void handlePieMenu(S32 x, S32 y, MASK mask); + void reshapeStatusBarContainer(); + BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); // diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 46beac8255..0e6734f6e0 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -287,6 +287,12 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, BOOL delete_raw = (mDetailTextures[i]->reloadRawImage(ddiscard) != NULL) ; if(mDetailTextures[i]->getRawImageLevel() != ddiscard)//raw iamge is not ready, will enter here again later. { + if (mDetailTextures[i]->getDecodePriority() <= 0.0f && !mDetailTextures[i]->hasSavedRawImage()) + { + mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_MAP); + mDetailTextures[i]->forceToRefetchTexture(ddiscard); + } + if(delete_raw) { mDetailTextures[i]->destroyRawImage() ; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 42550ed48d..176528cb56 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -10636,7 +10636,7 @@ void LLVOAvatar::updateVisualComplexity() // with an avatar. This will be either an attached object or an animated // object. void LLVOAvatar::accountRenderComplexityForObject( - const LLViewerObject *attached_object, + LLViewerObject *attached_object, const F32 max_attachment_complexity, LLVOVolume::texture_cost_t& textures, U32& cost, @@ -10708,7 +10708,7 @@ void LLVOAvatar::accountRenderComplexityForObject( && attached_object->mDrawable) { textures.clear(); - + BOOL is_rigged_mesh = attached_object->isRiggedMesh(); mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); @@ -10729,6 +10729,7 @@ void LLVOAvatar::accountRenderComplexityForObject( iter != child_list.end(); ++iter) { LLViewerObject* childp = *iter; + is_rigged_mesh |= childp->isRiggedMesh(); const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp); if (chld_volume) { @@ -10737,6 +10738,16 @@ void LLVOAvatar::accountRenderComplexityForObject( hud_object_complexity.objectsCount++; } } + if (is_rigged_mesh && !attached_object->mRiggedAttachedWarned) + { + LLSD args; + LLViewerInventoryItem* itemp = gInventory.getItem(attached_object->getAttachmentItemID()); + args["NAME"] = itemp ? itemp->getName() : LLTrans::getString("Unknown"); + args["POINT"] = LLTrans::getString(getTargetAttachmentPoint(attached_object)->getName()); + LLNotificationsUtil::add("RiggedMeshAttachedToHUD", args); + + attached_object->mRiggedAttachedWarned = true; + } hud_object_complexity.texturesCount += textures.size(); @@ -10841,7 +10852,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) { - const LLViewerObject* attached_object = attachment_iter->get(); + LLViewerObject* attached_object = attachment_iter->get(); accountRenderComplexityForObject(attached_object, max_attachment_complexity, textures, cost, hud_complexity_list); } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 8d1dcbcda2..56f2b73bef 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -293,7 +293,7 @@ public: void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font, const bool use_ellipses = false); void idleUpdateRenderComplexity(); void idleUpdateDebugInfo(); - void accountRenderComplexityForObject(const LLViewerObject *attached_object, + void accountRenderComplexityForObject(LLViewerObject *attached_object, const F32 max_attachment_complexity, LLVOVolume::texture_cost_t& textures, U32& cost, diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h index a53f309e46..e7033290c7 100644 --- a/indra/newview/llvoground.h +++ b/indra/newview/llvoground.h @@ -49,7 +49,6 @@ public: /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); - void cleanupGL(); }; #endif // LL_LLVOGROUND_H diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index ac6369e4e2..ab90f2e482 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -1247,8 +1247,8 @@ bool LLVivoxVoiceClient::establishVoiceConnection() if (result.has("connector")) { - LLVoiceVivoxStats::getInstance()->establishAttemptEnd(connected); connected = LLSD::Boolean(result["connector"]); + LLVoiceVivoxStats::getInstance()->establishAttemptEnd(connected); if (!connected) { if (result.has("retry") && ++retries <= CONNECT_RETRY_MAX && !sShuttingDown) @@ -4551,9 +4551,7 @@ void LLVivoxVoiceClient::messageEvent( IM_NOTHING_SPECIAL, // default arg 0, // default arg LLUUID::null, // default arg - LLVector3::zero, // default arg - true); // prepend name and make it a link to the user's profile - + LLVector3::zero); // default arg } } } @@ -6229,7 +6227,7 @@ void LLVivoxVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) { if (session) { - if (session->mHandle.empty()) + if (!session->mHandle.empty()) { sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); if (iter != mSessionsByHandle.end()) diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index aa32ef04b2..55bcbf8aa5 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -93,7 +93,7 @@ LLWorld::LLWorld() : mLastPacketsLost(0), mSpaceTimeUSec(0) { - for (S32 i = 0; i < 8; i++) + for (S32 i = 0; i < EDGE_WATER_OBJECTS_COUNT; i++) { mEdgeWaterObjects[i] = NULL; } @@ -126,7 +126,7 @@ void LLWorld::resetClass() LLViewerPartSim::getInstance()->destroyClass(); mDefaultWaterTexturep = NULL ; - for (S32 i = 0; i < 8; i++) + for (S32 i = 0; i < EDGE_WATER_OBJECTS_COUNT; i++) { mEdgeWaterObjects[i] = NULL; } @@ -753,6 +753,8 @@ void LLWorld::clearAllVisibleObjects() //clear all cached visible objects. (*iter)->clearCachedVisibleObjects(); } + clearHoleWaterObjects(); + clearEdgeWaterObjects(); } void LLWorld::updateParticles() @@ -920,7 +922,7 @@ void LLWorld::precullWaterObjects(LLCamera& camera, LLCullResult* cull, bool inc } S32 dir; - for (dir = 0; dir < 8; dir++) + for (dir = 0; dir < EDGE_WATER_OBJECTS_COUNT; dir++) { LLVOWater* waterp = mEdgeWaterObjects[dir]; if (waterp && waterp->mDrawable) @@ -931,6 +933,26 @@ void LLWorld::precullWaterObjects(LLCamera& camera, LLCullResult* cull, bool inc } } +void LLWorld::clearHoleWaterObjects() +{ + for (std::list<LLPointer<LLVOWater> >::iterator iter = mHoleWaterObjects.begin(); + iter != mHoleWaterObjects.end(); ++iter) + { + LLVOWater* waterp = (*iter).get(); + gObjectList.killObject(waterp); + } + mHoleWaterObjects.clear(); +} + +void LLWorld::clearEdgeWaterObjects() +{ + for (S32 i = 0; i < EDGE_WATER_OBJECTS_COUNT; i++) + { + gObjectList.killObject(mEdgeWaterObjects[i]); + mEdgeWaterObjects[i] = NULL; + } +} + void LLWorld::updateWaterObjects() { if (!gAgent.getRegion()) @@ -974,13 +996,7 @@ void LLWorld::updateWaterObjects() } } - for (std::list<LLPointer<LLVOWater> >::iterator iter = mHoleWaterObjects.begin(); - iter != mHoleWaterObjects.end(); ++ iter) - { - LLVOWater* waterp = (*iter).get(); - gObjectList.killObject(waterp); - } - mHoleWaterObjects.clear(); + clearHoleWaterObjects(); // Use the water height of the region we're on for areas where there is no region F32 water_height = gAgent.getRegion()->getWaterHeight(); @@ -1021,7 +1037,7 @@ void LLWorld::updateWaterObjects() (S32)(512 - (region_y - min_y)) }; S32 dir; - for (dir = 0; dir < 8; dir++) + for (dir = 0; dir < EDGE_WATER_OBJECTS_COUNT; dir++) { S32 dim[2] = { 0 }; switch (gDirAxes[dir][0]) diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h index 5c43cdf4e2..5dee8eea0f 100644 --- a/indra/newview/llworld.h +++ b/indra/newview/llworld.h @@ -122,12 +122,9 @@ public: void updateRegions(F32 max_update_time); void updateVisibilities(); void updateParticles(); - void updateClouds(const F32 dt); - LLCloudGroup * findCloudGroup(const LLCloudPuff &puff); void renderPropertyLines(); - void resetStats(); void updateNetStats(); // Update network statistics for all the regions... void printPacketsLost(); @@ -140,7 +137,7 @@ public: void setLandFarClip(const F32 far_clip); LLViewerTexture *getDefaultWaterTexture(); - void updateWaterObjects(); + void updateWaterObjects(); void precullWaterObjects(LLCamera& camera, LLCullResult* cull, bool include_void_water); @@ -175,6 +172,9 @@ public: bool isRegionListed(const LLViewerRegion* region) const; private: + void clearHoleWaterObjects(); + void clearEdgeWaterObjects(); + region_list_t mActiveRegionList; region_list_t mRegionList; region_list_t mVisibleRegionList; @@ -198,15 +198,14 @@ private: U32 mNumOfActiveCachedObjects; U64MicrosecondsImplicit mSpaceTimeUSec; - BOOL mClassicCloudsEnabled; - //////////////////////////// // // Data for "Fake" objects // std::list<LLPointer<LLVOWater> > mHoleWaterObjects; - LLPointer<LLVOWater> mEdgeWaterObjects[8]; + static const S32 EDGE_WATER_OBJECTS_COUNT = 8; + LLPointer<LLVOWater> mEdgeWaterObjects[EDGE_WATER_OBJECTS_COUNT]; LLPointer<LLViewerTexture> mDefaultWaterTexturep; }; diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 6e994b4e68..67632cdbf7 100755 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -551,7 +551,8 @@ void LLWorldMapView::draw() S32_MAX, //max_chars mMapScale, //max_pixels NULL, - TRUE); //use ellipses + /*use_ellipses*/TRUE, + /*use_color*/FALSE); } } } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 96ba80dacc..13d6966723 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -685,7 +685,9 @@ void LLPipeline::cleanup() mFaceSelectImagep = NULL; - mMovedBridge.clear(); + mMovedList.clear(); + mMovedBridge.clear(); + mShiftList.clear(); mInitialized = false; @@ -1773,15 +1775,20 @@ void LLPipeline::unlinkDrawable(LLDrawable *drawable) void LLPipeline::removeMutedAVsLights(LLVOAvatar* muted_avatar) { LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE; - for (light_set_t::iterator iter = gPipeline.mNearbyLights.begin(); - iter != gPipeline.mNearbyLights.end(); iter++) - { - if (iter->drawable->getVObj()->isAttachment() && iter->drawable->getVObj()->getAvatar() == muted_avatar) - { - gPipeline.mLights.erase(iter->drawable); - gPipeline.mNearbyLights.erase(iter); - } - } + light_set_t::iterator iter = gPipeline.mNearbyLights.begin(); + + while (iter != gPipeline.mNearbyLights.end()) + { + if (iter->drawable->getVObj()->isAttachment() && iter->drawable->getVObj()->getAvatar() == muted_avatar) + { + gPipeline.mLights.erase(iter->drawable); + iter = gPipeline.mNearbyLights.erase(iter); + } + else + { + iter++; + } + } } U32 LLPipeline::addObject(LLViewerObject *vobj) @@ -2817,6 +2824,14 @@ void LLPipeline::clearRebuildDrawables() drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED | LLDrawable::ON_MOVE_LIST | LLDrawable::ANIMATED_CHILD); } mMovedList.clear(); + + for (LLDrawable::drawable_vector_t::iterator iter = mShiftList.begin(); + iter != mShiftList.end(); ++iter) + { + LLDrawable *drawablep = *iter; + drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED | LLDrawable::ON_MOVE_LIST | LLDrawable::ANIMATED_CHILD | LLDrawable::ON_SHIFT_LIST); + } + mShiftList.clear(); } void LLPipeline::rebuildPriorityGroups() @@ -10906,6 +10921,8 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar, bool preview_avatar) if (preview_avatar) { // Only show rigged attachments for preview + // For the sake of performance and so that static + // objects won't obstruct previewing changes LLVOAvatar::attachment_map_t::iterator iter; for (iter = avatar->mAttachmentPoints.begin(); iter != avatar->mAttachmentPoints.end(); @@ -10917,9 +10934,27 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar, bool preview_avatar) ++attachment_iter) { LLViewerObject* attached_object = attachment_iter->get(); - if (attached_object && attached_object->isRiggedMesh()) + if (attached_object) { - markVisible(attached_object->mDrawable->getSpatialBridge(), *viewer_camera); + if (attached_object->isRiggedMesh()) + { + markVisible(attached_object->mDrawable->getSpatialBridge(), *viewer_camera); + } + else + { + // sometimes object is a linkset and rigged mesh is a child + LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child = *iter; + if (child->isRiggedMesh()) + { + markVisible(attached_object->mDrawable->getSpatialBridge(), *viewer_camera); + break; + } + } + } } } } diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 62d3ae7a39..0830d4a2ea 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -465,7 +465,7 @@ public: RENDER_TYPE_PASS_SIMPLE_RIGGED = LLRenderPass::PASS_SIMPLE_RIGGED, RENDER_TYPE_PASS_GRASS = LLRenderPass::PASS_GRASS, RENDER_TYPE_PASS_FULLBRIGHT = LLRenderPass::PASS_FULLBRIGHT, - RENDER_TYPE_PASS_FULLBRIGHT_RIGGED = LLRenderPass::PASS_FULLBRIGHT, + RENDER_TYPE_PASS_FULLBRIGHT_RIGGED = LLRenderPass::PASS_FULLBRIGHT_RIGGED, RENDER_TYPE_PASS_INVISIBLE = LLRenderPass::PASS_INVISIBLE, RENDER_TYPE_PASS_INVISIBLE_RIGGED = LLRenderPass::PASS_INVISIBLE_RIGGED, RENDER_TYPE_PASS_INVISI_SHINY = LLRenderPass::PASS_INVISI_SHINY, diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index c3ce83e834..8bb3feaeb0 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -985,4 +985,10 @@ <color name="AddPaymentPanel" value="0.27 0.27 0.27 1" /> + <color + name="OutfitSnapshotMacMask" + value="0.115 0.115 0.115 1"/> + <color + name="OutfitSnapshotMacMask2" + value="0.1 0.1 0.1 1"/> </colors> diff --git a/indra/newview/skins/default/xui/en/emoji_characters.xml b/indra/newview/skins/default/xui/en/emoji_characters.xml new file mode 100644 index 0000000000..55aefe6745 --- /dev/null +++ b/indra/newview/skins/default/xui/en/emoji_characters.xml @@ -0,0 +1,10955 @@ +<?xml version="1.0" ?> +<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> + <array> + <map> + <key>Character</key> + <string>😀</string> + <key>ShortCodes</key> + <array> + <string>:grinning:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😄</string> + <key>ShortCodes</key> + <array> + <string>:smile:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😆</string> + <key>ShortCodes</key> + <array> + <string>:laughing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤣</string> + <key>ShortCodes</key> + <array> + <string>:rofl:</string> + <string>:satisfied:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙂</string> + <key>ShortCodes</key> + <array> + <string>:slightly_smiling_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😉</string> + <key>ShortCodes</key> + <array> + <string>:wink:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😇</string> + <key>ShortCodes</key> + <array> + <string>:innocent:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😃</string> + <key>ShortCodes</key> + <array> + <string>:smiley:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😁</string> + <key>ShortCodes</key> + <array> + <string>:grin:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😅</string> + <key>ShortCodes</key> + <array> + <string>:sweat_smile:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😂</string> + <key>ShortCodes</key> + <array> + <string>:joy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙃</string> + <key>ShortCodes</key> + <array> + <string>:upside_down_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>😊</string> + <key>ShortCodes</key> + <array> + <string>:blush:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Smiling</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥰</string> + <key>ShortCodes</key> + <array> + <string>:smiling_face_with_three_hearts:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤩</string> + <key>ShortCodes</key> + <array> + <string>:star_struck:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>😗</string> + <key>ShortCodes</key> + <array> + <string>:kissing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>😚</string> + <key>ShortCodes</key> + <array> + <string>:kissing_closed_eyes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥲</string> + <key>ShortCodes</key> + <array> + <string>:smiling_face_with_tear:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>😍</string> + <key>ShortCodes</key> + <array> + <string>:heart_eyes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>😘</string> + <key>ShortCodes</key> + <array> + <string>:kissing_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>☺️</string> + <key>ShortCodes</key> + <array> + <string>:relaxed:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>😙</string> + <key>ShortCodes</key> + <array> + <string>:kissing_smiling_eyes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Affection</string> + </array> + </map> + <map> + <key>Character</key> + <string>😋</string> + <key>ShortCodes</key> + <array> + <string>:yum:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Tongue</string> + </array> + </map> + <map> + <key>Character</key> + <string>😜</string> + <key>ShortCodes</key> + <array> + <string>:stuck_out_tongue_winking_eye:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Tongue</string> + </array> + </map> + <map> + <key>Character</key> + <string>😝</string> + <key>ShortCodes</key> + <array> + <string>:stuck_out_tongue_closed_eyes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Tongue</string> + </array> + </map> + <map> + <key>Character</key> + <string>😛</string> + <key>ShortCodes</key> + <array> + <string>:stuck_out_tongue:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Tongue</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤪</string> + <key>ShortCodes</key> + <array> + <string>:zany_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Tongue</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤑</string> + <key>ShortCodes</key> + <array> + <string>:money_mouth_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Tongue</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤗</string> + <key>ShortCodes</key> + <array> + <string>:hugs:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Hand</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤫</string> + <key>ShortCodes</key> + <array> + <string>:shushing_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Hand</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤭</string> + <key>ShortCodes</key> + <array> + <string>:hand_over_mouth:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Hand</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤔</string> + <key>ShortCodes</key> + <array> + <string>:thinking:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Hand</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤐</string> + <key>ShortCodes</key> + <array> + <string>:zipper_mouth_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😐</string> + <key>ShortCodes</key> + <array> + <string>:neutral_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😶</string> + <key>ShortCodes</key> + <array> + <string>:no_mouth:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😏</string> + <key>ShortCodes</key> + <array> + <string>:smirk:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙄</string> + <key>ShortCodes</key> + <array> + <string>:roll_eyes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😮💨</string> + <key>ShortCodes</key> + <array> + <string>:face_exhaling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤨</string> + <key>ShortCodes</key> + <array> + <string>:raised_eyebrow:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😑</string> + <key>ShortCodes</key> + <array> + <string>:expressionless:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😶🌫️</string> + <key>ShortCodes</key> + <array> + <string>:face_in_clouds:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😒</string> + <key>ShortCodes</key> + <array> + <string>:unamused:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😬</string> + <key>ShortCodes</key> + <array> + <string>:grimacing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤥</string> + <key>ShortCodes</key> + <array> + <string>:lying_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Neutral Skeptical</string> + </array> + </map> + <map> + <key>Character</key> + <string>😌</string> + <key>ShortCodes</key> + <array> + <string>:relieved:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Sleepy</string> + </array> + </map> + <map> + <key>Character</key> + <string>😪</string> + <key>ShortCodes</key> + <array> + <string>:sleepy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Sleepy</string> + </array> + </map> + <map> + <key>Character</key> + <string>😴</string> + <key>ShortCodes</key> + <array> + <string>:sleeping:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Sleepy</string> + </array> + </map> + <map> + <key>Character</key> + <string>😔</string> + <key>ShortCodes</key> + <array> + <string>:pensive:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Sleepy</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤤</string> + <key>ShortCodes</key> + <array> + <string>:drooling_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Sleepy</string> + </array> + </map> + <map> + <key>Character</key> + <string>😷</string> + <key>ShortCodes</key> + <array> + <string>:mask:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤕</string> + <key>ShortCodes</key> + <array> + <string>:face_with_head_bandage:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤮</string> + <key>ShortCodes</key> + <array> + <string>:vomiting_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥵</string> + <key>ShortCodes</key> + <array> + <string>:hot_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥴</string> + <key>ShortCodes</key> + <array> + <string>:woozy_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>😵💫</string> + <key>ShortCodes</key> + <array> + <string>:face_with_spiral_eyes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤒</string> + <key>ShortCodes</key> + <array> + <string>:face_with_thermometer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤢</string> + <key>ShortCodes</key> + <array> + <string>:nauseated_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤧</string> + <key>ShortCodes</key> + <array> + <string>:sneezing_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥶</string> + <key>ShortCodes</key> + <array> + <string>:cold_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>😵</string> + <key>ShortCodes</key> + <array> + <string>:dizzy_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤯</string> + <key>ShortCodes</key> + <array> + <string>:exploding_head:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Unwell</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤠</string> + <key>ShortCodes</key> + <array> + <string>:cowboy_hat_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Hat</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥸</string> + <key>ShortCodes</key> + <array> + <string>:disguised_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Hat</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥳</string> + <key>ShortCodes</key> + <array> + <string>:partying_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Hat</string> + </array> + </map> + <map> + <key>Character</key> + <string>😎</string> + <key>ShortCodes</key> + <array> + <string>:sunglasses:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Glasses</string> + </array> + </map> + <map> + <key>Character</key> + <string>🧐</string> + <key>ShortCodes</key> + <array> + <string>:monocle_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Glasses</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤓</string> + <key>ShortCodes</key> + <array> + <string>:nerd_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Glasses</string> + </array> + </map> + <map> + <key>Character</key> + <string>😕</string> + <key>ShortCodes</key> + <array> + <string>:confused:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙁</string> + <key>ShortCodes</key> + <array> + <string>:slightly_frowning_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😮</string> + <key>ShortCodes</key> + <array> + <string>:open_mouth:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😲</string> + <key>ShortCodes</key> + <array> + <string>:astonished:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥺</string> + <key>ShortCodes</key> + <array> + <string>:pleading_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😧</string> + <key>ShortCodes</key> + <array> + <string>:anguished:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😰</string> + <key>ShortCodes</key> + <array> + <string>:cold_sweat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😢</string> + <key>ShortCodes</key> + <array> + <string>:cry:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😱</string> + <key>ShortCodes</key> + <array> + <string>:scream:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😣</string> + <key>ShortCodes</key> + <array> + <string>:persevere:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😓</string> + <key>ShortCodes</key> + <array> + <string>:sweat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😫</string> + <key>ShortCodes</key> + <array> + <string>:tired_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😟</string> + <key>ShortCodes</key> + <array> + <string>:worried:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>☹️</string> + <key>ShortCodes</key> + <array> + <string>:frowning_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😯</string> + <key>ShortCodes</key> + <array> + <string>:hushed:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😳</string> + <key>ShortCodes</key> + <array> + <string>:flushed:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😦</string> + <key>ShortCodes</key> + <array> + <string>:frowning:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😨</string> + <key>ShortCodes</key> + <array> + <string>:fearful:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😥</string> + <key>ShortCodes</key> + <array> + <string>:disappointed_relieved:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😭</string> + <key>ShortCodes</key> + <array> + <string>:sob:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😖</string> + <key>ShortCodes</key> + <array> + <string>:confounded:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😞</string> + <key>ShortCodes</key> + <array> + <string>:disappointed:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😩</string> + <key>ShortCodes</key> + <array> + <string>:weary:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>🥱</string> + <key>ShortCodes</key> + <array> + <string>:yawning_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Concerned</string> + </array> + </map> + <map> + <key>Character</key> + <string>😤</string> + <key>ShortCodes</key> + <array> + <string>:triumph:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>😠</string> + <key>ShortCodes</key> + <array> + <string>:angry:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>😈</string> + <key>ShortCodes</key> + <array> + <string>:smiling_imp:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>💀</string> + <key>ShortCodes</key> + <array> + <string>:skull:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>😡</string> + <key>ShortCodes</key> + <array> + <string>:pout:</string> + <string>:rage:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤬</string> + <key>ShortCodes</key> + <array> + <string>:cursing_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>👿</string> + <key>ShortCodes</key> + <array> + <string>:imp:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>☠️</string> + <key>ShortCodes</key> + <array> + <string>:skull_and_crossbones:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Negative</string> + </array> + </map> + <map> + <key>Character</key> + <string>💩</string> + <key>ShortCodes</key> + <array> + <string>:hankey:</string> + <string>:poop:</string> + <string>:shit:</string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>👹</string> + <key>ShortCodes</key> + <array> + <string>:japanese_ogre:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>👻</string> + <key>ShortCodes</key> + <array> + <string>:ghost:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>👾</string> + <key>ShortCodes</key> + <array> + <string>:space_invader:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤡</string> + <key>ShortCodes</key> + <array> + <string>:clown_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>👺</string> + <key>ShortCodes</key> + <array> + <string>:japanese_goblin:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>👽</string> + <key>ShortCodes</key> + <array> + <string>:alien:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤖</string> + <key>ShortCodes</key> + <array> + <string>:robot:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Face Costume</string> + </array> + </map> + <map> + <key>Character</key> + <string>😺</string> + <key>ShortCodes</key> + <array> + <string>:smiley_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>😹</string> + <key>ShortCodes</key> + <array> + <string>:joy_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>😼</string> + <key>ShortCodes</key> + <array> + <string>:smirk_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙀</string> + <key>ShortCodes</key> + <array> + <string>:scream_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>😾</string> + <key>ShortCodes</key> + <array> + <string>:pouting_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>😸</string> + <key>ShortCodes</key> + <array> + <string>:smile_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>😻</string> + <key>ShortCodes</key> + <array> + <string>:heart_eyes_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>😽</string> + <key>ShortCodes</key> + <array> + <string>:kissing_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>😿</string> + <key>ShortCodes</key> + <array> + <string>:crying_cat_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Cat Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙈</string> + <key>ShortCodes</key> + <array> + <string>:see_no_evil:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Monkey Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙊</string> + <key>ShortCodes</key> + <array> + <string>:speak_no_evil:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Monkey Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙉</string> + <key>ShortCodes</key> + <array> + <string>:hear_no_evil:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Smiley</string> + <string>Monkey Face</string> + </array> + </map> + <map> + <key>Character</key> + <string>💋</string> + <key>ShortCodes</key> + <array> + <string>:kiss:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💘</string> + <key>ShortCodes</key> + <array> + <string>:cupid:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💖</string> + <key>ShortCodes</key> + <array> + <string>:sparkling_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💓</string> + <key>ShortCodes</key> + <array> + <string>:heartbeat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💕</string> + <key>ShortCodes</key> + <array> + <string>:two_hearts:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>❣️</string> + <key>ShortCodes</key> + <array> + <string>:heavy_heart_exclamation:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>❤️🔥</string> + <key>ShortCodes</key> + <array> + <string>:heart_on_fire:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>❤️</string> + <key>ShortCodes</key> + <array> + <string>:heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💛</string> + <key>ShortCodes</key> + <array> + <string>:yellow_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💙</string> + <key>ShortCodes</key> + <array> + <string>:blue_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤎</string> + <key>ShortCodes</key> + <array> + <string>:brown_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤍</string> + <key>ShortCodes</key> + <array> + <string>:white_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💢</string> + <key>ShortCodes</key> + <array> + <string>:anger:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💫</string> + <key>ShortCodes</key> + <array> + <string>:dizzy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💨</string> + <key>ShortCodes</key> + <array> + <string>:dash:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💣</string> + <key>ShortCodes</key> + <array> + <string>:bomb:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👁️🗨️</string> + <key>ShortCodes</key> + <array> + <string>:eye_speech_bubble:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🗯️</string> + <key>ShortCodes</key> + <array> + <string>:right_anger_bubble:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💤</string> + <key>ShortCodes</key> + <array> + <string>:zzz:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💌</string> + <key>ShortCodes</key> + <array> + <string>:love_letter:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💝</string> + <key>ShortCodes</key> + <array> + <string>:gift_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💗</string> + <key>ShortCodes</key> + <array> + <string>:heartpulse:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💞</string> + <key>ShortCodes</key> + <array> + <string>:revolving_hearts:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💟</string> + <key>ShortCodes</key> + <array> + <string>:heart_decoration:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💔</string> + <key>ShortCodes</key> + <array> + <string>:broken_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>❤️🩹</string> + <key>ShortCodes</key> + <array> + <string>:mending_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧡</string> + <key>ShortCodes</key> + <array> + <string>:orange_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💚</string> + <key>ShortCodes</key> + <array> + <string>:green_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💜</string> + <key>ShortCodes</key> + <array> + <string>:purple_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🖤</string> + <key>ShortCodes</key> + <array> + <string>:black_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💯</string> + <key>ShortCodes</key> + <array> + <string>:100:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💥</string> + <key>ShortCodes</key> + <array> + <string>:boom:</string> + <string>:collision:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💦</string> + <key>ShortCodes</key> + <array> + <string>:sweat_drops:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕳️</string> + <key>ShortCodes</key> + <array> + <string>:hole:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💬</string> + <key>ShortCodes</key> + <array> + <string>:speech_balloon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🗨️</string> + <key>ShortCodes</key> + <array> + <string>:left_speech_bubble:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💭</string> + <key>ShortCodes</key> + <array> + <string>:thought_balloon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Emotion</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👋</string> + <key>ShortCodes</key> + <array> + <string>:wave:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Open</string> + </array> + </map> + <map> + <key>Character</key> + <string>🖐️</string> + <key>ShortCodes</key> + <array> + <string>:raised_hand_with_fingers_splayed:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Open</string> + </array> + </map> + <map> + <key>Character</key> + <string>🖖</string> + <key>ShortCodes</key> + <array> + <string>:vulcan_salute:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Open</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤚</string> + <key>ShortCodes</key> + <array> + <string>:raised_back_of_hand:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Open</string> + </array> + </map> + <map> + <key>Character</key> + <string>✋</string> + <key>ShortCodes</key> + <array> + <string>:hand:</string> + <string>:raised_hand:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Open</string> + </array> + </map> + <map> + <key>Character</key> + <string>👌</string> + <key>ShortCodes</key> + <array> + <string>:ok_hand:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤏</string> + <key>ShortCodes</key> + <array> + <string>:pinching_hand:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤞</string> + <key>ShortCodes</key> + <array> + <string>:crossed_fingers:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤘</string> + <key>ShortCodes</key> + <array> + <string>:metal:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤌</string> + <key>ShortCodes</key> + <array> + <string>:pinched_fingers:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>✌️</string> + <key>ShortCodes</key> + <array> + <string>:v:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤟</string> + <key>ShortCodes</key> + <array> + <string>:love_you_gesture:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤙</string> + <key>ShortCodes</key> + <array> + <string>:call_me_hand:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Partial</string> + </array> + </map> + <map> + <key>Character</key> + <string>👈</string> + <key>ShortCodes</key> + <array> + <string>:point_left:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Single Finger</string> + </array> + </map> + <map> + <key>Character</key> + <string>👆</string> + <key>ShortCodes</key> + <array> + <string>:point_up_2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Single Finger</string> + </array> + </map> + <map> + <key>Character</key> + <string>👇</string> + <key>ShortCodes</key> + <array> + <string>:point_down:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Single Finger</string> + </array> + </map> + <map> + <key>Character</key> + <string>👉</string> + <key>ShortCodes</key> + <array> + <string>:point_right:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Single Finger</string> + </array> + </map> + <map> + <key>Character</key> + <string>🖕</string> + <key>ShortCodes</key> + <array> + <string>:fu:</string> + <string>:middle_finger:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Single Finger</string> + </array> + </map> + <map> + <key>Character</key> + <string>☝️</string> + <key>ShortCodes</key> + <array> + <string>:point_up:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Single Finger</string> + </array> + </map> + <map> + <key>Character</key> + <string>👍</string> + <key>ShortCodes</key> + <array> + <string>:+1:</string> + <string>:thumbsup:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Closed</string> + </array> + </map> + <map> + <key>Character</key> + <string>✊</string> + <key>ShortCodes</key> + <array> + <string>:fist:</string> + <string>:fist_raised:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Closed</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤛</string> + <key>ShortCodes</key> + <array> + <string>:fist_left:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Closed</string> + </array> + </map> + <map> + <key>Character</key> + <string>👎</string> + <key>ShortCodes</key> + <array> + <string>:-1:</string> + <string>:thumbsdown:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Closed</string> + </array> + </map> + <map> + <key>Character</key> + <string>👊</string> + <key>ShortCodes</key> + <array> + <string>:facepunch:</string> + <string>:fist_oncoming:</string> + <string>:punch:</string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Closed</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤜</string> + <key>ShortCodes</key> + <array> + <string>:fist_right:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Fingers Closed</string> + </array> + </map> + <map> + <key>Character</key> + <string>👏</string> + <key>ShortCodes</key> + <array> + <string>:clap:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hands</string> + </array> + </map> + <map> + <key>Character</key> + <string>👐</string> + <key>ShortCodes</key> + <array> + <string>:open_hands:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hands</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤝</string> + <key>ShortCodes</key> + <array> + <string>:handshake:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hands</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙌</string> + <key>ShortCodes</key> + <array> + <string>:raised_hands:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hands</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤲</string> + <key>ShortCodes</key> + <array> + <string>:palms_up_together:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hands</string> + </array> + </map> + <map> + <key>Character</key> + <string>🙏</string> + <key>ShortCodes</key> + <array> + <string>:pray:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hands</string> + </array> + </map> + <map> + <key>Character</key> + <string>✍️</string> + <key>ShortCodes</key> + <array> + <string>:writing_hand:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Prop</string> + </array> + </map> + <map> + <key>Character</key> + <string>🤳</string> + <key>ShortCodes</key> + <array> + <string>:selfie:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Prop</string> + </array> + </map> + <map> + <key>Character</key> + <string>💅</string> + <key>ShortCodes</key> + <array> + <string>:nail_care:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Hand Prop</string> + </array> + </map> + <map> + <key>Character</key> + <string>💪</string> + <key>ShortCodes</key> + <array> + <string>:muscle:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🦿</string> + <key>ShortCodes</key> + <array> + <string>:mechanical_leg:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🦶</string> + <key>ShortCodes</key> + <array> + <string>:foot:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🦻</string> + <key>ShortCodes</key> + <array> + <string>:ear_with_hearing_aid:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🧠</string> + <key>ShortCodes</key> + <array> + <string>:brain:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🫁</string> + <key>ShortCodes</key> + <array> + <string>:lungs:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🦴</string> + <key>ShortCodes</key> + <array> + <string>:bone:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>👁️</string> + <key>ShortCodes</key> + <array> + <string>:eye:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>👄</string> + <key>ShortCodes</key> + <array> + <string>:lips:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🦾</string> + <key>ShortCodes</key> + <array> + <string>:mechanical_arm:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🦵</string> + <key>ShortCodes</key> + <array> + <string>:leg:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>👂</string> + <key>ShortCodes</key> + <array> + <string>:ear:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>👃</string> + <key>ShortCodes</key> + <array> + <string>:nose:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🫀</string> + <key>ShortCodes</key> + <array> + <string>:anatomical_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>🦷</string> + <key>ShortCodes</key> + <array> + <string>:tooth:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>👀</string> + <key>ShortCodes</key> + <array> + <string>:eyes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>👅</string> + <key>ShortCodes</key> + <array> + <string>:tongue:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Body</string> + <string>Body Parts</string> + </array> + </map> + <map> + <key>Character</key> + <string>👶</string> + <key>ShortCodes</key> + <array> + <string>:baby:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👦</string> + <key>ShortCodes</key> + <array> + <string>:boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑</string> + <key>ShortCodes</key> + <array> + <string>:adult:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨</string> + <key>ShortCodes</key> + <array> + <string>:man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧔♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_beard:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🦰</string> + <key>ShortCodes</key> + <array> + <string>:red_haired_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🦳</string> + <key>ShortCodes</key> + <array> + <string>:white_haired_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩</string> + <key>ShortCodes</key> + <array> + <string>:woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🦰</string> + <key>ShortCodes</key> + <array> + <string>:person_red_hair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🦱</string> + <key>ShortCodes</key> + <array> + <string>:person_curly_hair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🦳</string> + <key>ShortCodes</key> + <array> + <string>:person_white_hair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🦲</string> + <key>ShortCodes</key> + <array> + <string>:person_bald:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👱♂️</string> + <key>ShortCodes</key> + <array> + <string>:blond_haired_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👴</string> + <key>ShortCodes</key> + <array> + <string>:older_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧒</string> + <key>ShortCodes</key> + <array> + <string>:child:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👧</string> + <key>ShortCodes</key> + <array> + <string>:girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👱</string> + <key>ShortCodes</key> + <array> + <string>:blond_haired_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧔</string> + <key>ShortCodes</key> + <array> + <string>:bearded_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧔♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_beard:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🦱</string> + <key>ShortCodes</key> + <array> + <string>:curly_haired_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🦲</string> + <key>ShortCodes</key> + <array> + <string>:bald_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🦰</string> + <key>ShortCodes</key> + <array> + <string>:red_haired_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🦱</string> + <key>ShortCodes</key> + <array> + <string>:curly_haired_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🦳</string> + <key>ShortCodes</key> + <array> + <string>:white_haired_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🦲</string> + <key>ShortCodes</key> + <array> + <string>:bald_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👱♀️</string> + <key>ShortCodes</key> + <array> + <string>:blond_haired_woman:</string> + <string>:blonde_woman:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧓</string> + <key>ShortCodes</key> + <array> + <string>:older_adult:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👵</string> + <key>ShortCodes</key> + <array> + <string>:older_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙍</string> + <key>ShortCodes</key> + <array> + <string>:frowning_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙍♀️</string> + <key>ShortCodes</key> + <array> + <string>:frowning_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙎♂️</string> + <key>ShortCodes</key> + <array> + <string>:pouting_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙅</string> + <key>ShortCodes</key> + <array> + <string>:no_good:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙅♀️</string> + <key>ShortCodes</key> + <array> + <string>:ng_woman:</string> + <string>:no_good_woman:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙆♂️</string> + <key>ShortCodes</key> + <array> + <string>:ok_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💁</string> + <key>ShortCodes</key> + <array> + <string>:information_desk_person:</string> + <string>:tipping_hand_person:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💁♀️</string> + <key>ShortCodes</key> + <array> + <string>:sassy_woman:</string> + <string>:tipping_hand_woman:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙋♂️</string> + <key>ShortCodes</key> + <array> + <string>:raising_hand_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧏</string> + <key>ShortCodes</key> + <array> + <string>:deaf_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧏♀️</string> + <key>ShortCodes</key> + <array> + <string>:deaf_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙇♂️</string> + <key>ShortCodes</key> + <array> + <string>:bowing_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤦</string> + <key>ShortCodes</key> + <array> + <string>:facepalm:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤦♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_facepalming:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤷♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_shrugging:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙍♂️</string> + <key>ShortCodes</key> + <array> + <string>:frowning_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙎</string> + <key>ShortCodes</key> + <array> + <string>:pouting_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙎♀️</string> + <key>ShortCodes</key> + <array> + <string>:pouting_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙅♂️</string> + <key>ShortCodes</key> + <array> + <string>:ng_man:</string> + <string>:no_good_man:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙆</string> + <key>ShortCodes</key> + <array> + <string>:ok_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙆♀️</string> + <key>ShortCodes</key> + <array> + <string>:ok_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💁♂️</string> + <key>ShortCodes</key> + <array> + <string>:sassy_man:</string> + <string>:tipping_hand_man:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙋</string> + <key>ShortCodes</key> + <array> + <string>:raising_hand:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙋♀️</string> + <key>ShortCodes</key> + <array> + <string>:raising_hand_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧏♂️</string> + <key>ShortCodes</key> + <array> + <string>:deaf_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙇</string> + <key>ShortCodes</key> + <array> + <string>:bow:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🙇♀️</string> + <key>ShortCodes</key> + <array> + <string>:bowing_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤦♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_facepalming:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤷</string> + <key>ShortCodes</key> + <array> + <string>:shrug:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤷♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_shrugging:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Gesture</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑⚕️</string> + <key>ShortCodes</key> + <array> + <string>:health_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩⚕️</string> + <key>ShortCodes</key> + <array> + <string>:woman_health_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🎓</string> + <key>ShortCodes</key> + <array> + <string>:man_student:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🏫</string> + <key>ShortCodes</key> + <array> + <string>:teacher:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🏫</string> + <key>ShortCodes</key> + <array> + <string>:woman_teacher:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨⚖️</string> + <key>ShortCodes</key> + <array> + <string>:man_judge:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🌾</string> + <key>ShortCodes</key> + <array> + <string>:farmer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🌾</string> + <key>ShortCodes</key> + <array> + <string>:woman_farmer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🍳</string> + <key>ShortCodes</key> + <array> + <string>:man_cook:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🔧</string> + <key>ShortCodes</key> + <array> + <string>:mechanic:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🔧</string> + <key>ShortCodes</key> + <array> + <string>:woman_mechanic:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🏭</string> + <key>ShortCodes</key> + <array> + <string>:man_factory_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑💼</string> + <key>ShortCodes</key> + <array> + <string>:office_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩💼</string> + <key>ShortCodes</key> + <array> + <string>:woman_office_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🔬</string> + <key>ShortCodes</key> + <array> + <string>:man_scientist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑💻</string> + <key>ShortCodes</key> + <array> + <string>:technologist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩💻</string> + <key>ShortCodes</key> + <array> + <string>:woman_technologist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🎤</string> + <key>ShortCodes</key> + <array> + <string>:man_singer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🎨</string> + <key>ShortCodes</key> + <array> + <string>:artist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🎨</string> + <key>ShortCodes</key> + <array> + <string>:woman_artist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨✈️</string> + <key>ShortCodes</key> + <array> + <string>:man_pilot:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🚀</string> + <key>ShortCodes</key> + <array> + <string>:astronaut:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🚀</string> + <key>ShortCodes</key> + <array> + <string>:woman_astronaut:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🚒</string> + <key>ShortCodes</key> + <array> + <string>:man_firefighter:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👮</string> + <key>ShortCodes</key> + <array> + <string>:cop:</string> + <string>:police_officer:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👮♀️</string> + <key>ShortCodes</key> + <array> + <string>:policewoman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕵️♂️</string> + <key>ShortCodes</key> + <array> + <string>:male_detective:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💂</string> + <key>ShortCodes</key> + <array> + <string>:guard:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💂♀️</string> + <key>ShortCodes</key> + <array> + <string>:guardswoman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👷</string> + <key>ShortCodes</key> + <array> + <string>:construction_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👷♀️</string> + <key>ShortCodes</key> + <array> + <string>:construction_worker_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👸</string> + <key>ShortCodes</key> + <array> + <string>:princess:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👳♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_with_turban:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👲</string> + <key>ShortCodes</key> + <array> + <string>:man_with_gua_pi_mao:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤵</string> + <key>ShortCodes</key> + <array> + <string>:person_in_tuxedo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤵♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_in_tuxedo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👰♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_with_veil:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤰</string> + <key>ShortCodes</key> + <array> + <string>:pregnant_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🍼</string> + <key>ShortCodes</key> + <array> + <string>:woman_feeding_baby:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🍼</string> + <key>ShortCodes</key> + <array> + <string>:person_feeding_baby:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨⚕️</string> + <key>ShortCodes</key> + <array> + <string>:man_health_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🎓</string> + <key>ShortCodes</key> + <array> + <string>:student:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🎓</string> + <key>ShortCodes</key> + <array> + <string>:woman_student:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🏫</string> + <key>ShortCodes</key> + <array> + <string>:man_teacher:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑⚖️</string> + <key>ShortCodes</key> + <array> + <string>:judge:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩⚖️</string> + <key>ShortCodes</key> + <array> + <string>:woman_judge:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🌾</string> + <key>ShortCodes</key> + <array> + <string>:man_farmer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🍳</string> + <key>ShortCodes</key> + <array> + <string>:cook:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🍳</string> + <key>ShortCodes</key> + <array> + <string>:woman_cook:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🔧</string> + <key>ShortCodes</key> + <array> + <string>:man_mechanic:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🏭</string> + <key>ShortCodes</key> + <array> + <string>:factory_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🏭</string> + <key>ShortCodes</key> + <array> + <string>:woman_factory_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨💼</string> + <key>ShortCodes</key> + <array> + <string>:man_office_worker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🔬</string> + <key>ShortCodes</key> + <array> + <string>:scientist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🔬</string> + <key>ShortCodes</key> + <array> + <string>:woman_scientist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨💻</string> + <key>ShortCodes</key> + <array> + <string>:man_technologist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🎤</string> + <key>ShortCodes</key> + <array> + <string>:singer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🎤</string> + <key>ShortCodes</key> + <array> + <string>:woman_singer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🎨</string> + <key>ShortCodes</key> + <array> + <string>:man_artist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑✈️</string> + <key>ShortCodes</key> + <array> + <string>:pilot:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩✈️</string> + <key>ShortCodes</key> + <array> + <string>:woman_pilot:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🚀</string> + <key>ShortCodes</key> + <array> + <string>:man_astronaut:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🚒</string> + <key>ShortCodes</key> + <array> + <string>:firefighter:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🚒</string> + <key>ShortCodes</key> + <array> + <string>:woman_firefighter:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👮♂️</string> + <key>ShortCodes</key> + <array> + <string>:policeman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕵️</string> + <key>ShortCodes</key> + <array> + <string>:detective:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕵️♀️</string> + <key>ShortCodes</key> + <array> + <string>:female_detective:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💂♂️</string> + <key>ShortCodes</key> + <array> + <string>:guardsman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥷</string> + <key>ShortCodes</key> + <array> + <string>:ninja:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👷♂️</string> + <key>ShortCodes</key> + <array> + <string>:construction_worker_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤴</string> + <key>ShortCodes</key> + <array> + <string>:prince:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👳</string> + <key>ShortCodes</key> + <array> + <string>:person_with_turban:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👳♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_with_turban:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧕</string> + <key>ShortCodes</key> + <array> + <string>:woman_with_headscarf:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤵♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_in_tuxedo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👰</string> + <key>ShortCodes</key> + <array> + <string>:person_with_veil:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👰♀️</string> + <key>ShortCodes</key> + <array> + <string>:bride_with_veil:</string> + <string>:woman_with_veil:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤱</string> + <key>ShortCodes</key> + <array> + <string>:breast_feeding:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🍼</string> + <key>ShortCodes</key> + <array> + <string>:man_feeding_baby:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Role</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👼</string> + <key>ShortCodes</key> + <array> + <string>:angel:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤶</string> + <key>ShortCodes</key> + <array> + <string>:mrs_claus:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦸</string> + <key>ShortCodes</key> + <array> + <string>:superhero:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦸♀️</string> + <key>ShortCodes</key> + <array> + <string>:superhero_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦹♂️</string> + <key>ShortCodes</key> + <array> + <string>:supervillain_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧙</string> + <key>ShortCodes</key> + <array> + <string>:mage:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧙♀️</string> + <key>ShortCodes</key> + <array> + <string>:mage_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧚♂️</string> + <key>ShortCodes</key> + <array> + <string>:fairy_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧛</string> + <key>ShortCodes</key> + <array> + <string>:vampire:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧛♀️</string> + <key>ShortCodes</key> + <array> + <string>:vampire_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧜♂️</string> + <key>ShortCodes</key> + <array> + <string>:merman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧝</string> + <key>ShortCodes</key> + <array> + <string>:elf:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧝♀️</string> + <key>ShortCodes</key> + <array> + <string>:elf_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧞♂️</string> + <key>ShortCodes</key> + <array> + <string>:genie_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧟</string> + <key>ShortCodes</key> + <array> + <string>:zombie:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧟♀️</string> + <key>ShortCodes</key> + <array> + <string>:zombie_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🎅</string> + <key>ShortCodes</key> + <array> + <string>:santa:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🎄</string> + <key>ShortCodes</key> + <array> + <string>:mx_claus:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦸♂️</string> + <key>ShortCodes</key> + <array> + <string>:superhero_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦹</string> + <key>ShortCodes</key> + <array> + <string>:supervillain:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦹♀️</string> + <key>ShortCodes</key> + <array> + <string>:supervillain_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧙♂️</string> + <key>ShortCodes</key> + <array> + <string>:mage_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧚</string> + <key>ShortCodes</key> + <array> + <string>:fairy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧚♀️</string> + <key>ShortCodes</key> + <array> + <string>:fairy_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧛♂️</string> + <key>ShortCodes</key> + <array> + <string>:vampire_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧜</string> + <key>ShortCodes</key> + <array> + <string>:merperson:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧜♀️</string> + <key>ShortCodes</key> + <array> + <string>:mermaid:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧝♂️</string> + <key>ShortCodes</key> + <array> + <string>:elf_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧞</string> + <key>ShortCodes</key> + <array> + <string>:genie:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧞♀️</string> + <key>ShortCodes</key> + <array> + <string>:genie_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧟♂️</string> + <key>ShortCodes</key> + <array> + <string>:zombie_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Fantasy</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💆</string> + <key>ShortCodes</key> + <array> + <string>:massage:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💆♀️</string> + <key>ShortCodes</key> + <array> + <string>:massage_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💇♂️</string> + <key>ShortCodes</key> + <array> + <string>:haircut_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚶</string> + <key>ShortCodes</key> + <array> + <string>:walking:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚶♀️</string> + <key>ShortCodes</key> + <array> + <string>:walking_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧍♂️</string> + <key>ShortCodes</key> + <array> + <string>:standing_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧎</string> + <key>ShortCodes</key> + <array> + <string>:kneeling_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧎♀️</string> + <key>ShortCodes</key> + <array> + <string>:kneeling_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🦯</string> + <key>ShortCodes</key> + <array> + <string>:man_with_probing_cane:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🦼</string> + <key>ShortCodes</key> + <array> + <string>:person_in_motorized_wheelchair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🦼</string> + <key>ShortCodes</key> + <array> + <string>:woman_in_motorized_wheelchair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🦽</string> + <key>ShortCodes</key> + <array> + <string>:man_in_manual_wheelchair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏃</string> + <key>ShortCodes</key> + <array> + <string>:runner:</string> + <string>:running:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏃♀️</string> + <key>ShortCodes</key> + <array> + <string>:running_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕺</string> + <key>ShortCodes</key> + <array> + <string>:man_dancing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👯</string> + <key>ShortCodes</key> + <array> + <string>:dancers:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👯♀️</string> + <key>ShortCodes</key> + <array> + <string>:dancing_women:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧖♂️</string> + <key>ShortCodes</key> + <array> + <string>:sauna_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧗</string> + <key>ShortCodes</key> + <array> + <string>:climbing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧗♀️</string> + <key>ShortCodes</key> + <array> + <string>:climbing_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💆♂️</string> + <key>ShortCodes</key> + <array> + <string>:massage_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💇</string> + <key>ShortCodes</key> + <array> + <string>:haircut:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💇♀️</string> + <key>ShortCodes</key> + <array> + <string>:haircut_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚶♂️</string> + <key>ShortCodes</key> + <array> + <string>:walking_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧍</string> + <key>ShortCodes</key> + <array> + <string>:standing_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧍♀️</string> + <key>ShortCodes</key> + <array> + <string>:standing_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧎♂️</string> + <key>ShortCodes</key> + <array> + <string>:kneeling_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🦯</string> + <key>ShortCodes</key> + <array> + <string>:person_with_probing_cane:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🦯</string> + <key>ShortCodes</key> + <array> + <string>:woman_with_probing_cane:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👨🦼</string> + <key>ShortCodes</key> + <array> + <string>:man_in_motorized_wheelchair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🦽</string> + <key>ShortCodes</key> + <array> + <string>:person_in_manual_wheelchair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👩🦽</string> + <key>ShortCodes</key> + <array> + <string>:woman_in_manual_wheelchair:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏃♂️</string> + <key>ShortCodes</key> + <array> + <string>:running_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💃</string> + <key>ShortCodes</key> + <array> + <string>:dancer:</string> + <string>:woman_dancing:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕴️</string> + <key>ShortCodes</key> + <array> + <string>:business_suit_levitating:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👯♂️</string> + <key>ShortCodes</key> + <array> + <string>:dancing_men:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧖</string> + <key>ShortCodes</key> + <array> + <string>:sauna_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧖♀️</string> + <key>ShortCodes</key> + <array> + <string>:sauna_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧗♂️</string> + <key>ShortCodes</key> + <array> + <string>:climbing_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Activity</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤺</string> + <key>ShortCodes</key> + <array> + <string>:person_fencing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>⛷️</string> + <key>ShortCodes</key> + <array> + <string>:skier:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏌️</string> + <key>ShortCodes</key> + <array> + <string>:golfing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏌️♀️</string> + <key>ShortCodes</key> + <array> + <string>:golfing_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏄♂️</string> + <key>ShortCodes</key> + <array> + <string>:surfing_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚣</string> + <key>ShortCodes</key> + <array> + <string>:rowboat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚣♀️</string> + <key>ShortCodes</key> + <array> + <string>:rowing_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏊♂️</string> + <key>ShortCodes</key> + <array> + <string>:swimming_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>⛹️</string> + <key>ShortCodes</key> + <array> + <string>:bouncing_ball_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>⛹️♀️</string> + <key>ShortCodes</key> + <array> + <string>:basketball_woman:</string> + <string>:bouncing_ball_woman:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏋️♂️</string> + <key>ShortCodes</key> + <array> + <string>:weight_lifting_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚴</string> + <key>ShortCodes</key> + <array> + <string>:bicyclist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚴♀️</string> + <key>ShortCodes</key> + <array> + <string>:biking_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚵♂️</string> + <key>ShortCodes</key> + <array> + <string>:mountain_biking_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤸</string> + <key>ShortCodes</key> + <array> + <string>:cartwheeling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤸♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_cartwheeling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤼♂️</string> + <key>ShortCodes</key> + <array> + <string>:men_wrestling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤽</string> + <key>ShortCodes</key> + <array> + <string>:water_polo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤽♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_playing_water_polo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤾♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_playing_handball:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤹</string> + <key>ShortCodes</key> + <array> + <string>:juggling_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤹♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_juggling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏇</string> + <key>ShortCodes</key> + <array> + <string>:horse_racing:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏂</string> + <key>ShortCodes</key> + <array> + <string>:snowboarder:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏌️♂️</string> + <key>ShortCodes</key> + <array> + <string>:golfing_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏄</string> + <key>ShortCodes</key> + <array> + <string>:surfer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏄♀️</string> + <key>ShortCodes</key> + <array> + <string>:surfing_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚣♂️</string> + <key>ShortCodes</key> + <array> + <string>:rowing_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏊</string> + <key>ShortCodes</key> + <array> + <string>:swimmer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏊♀️</string> + <key>ShortCodes</key> + <array> + <string>:swimming_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>⛹️♂️</string> + <key>ShortCodes</key> + <array> + <string>:basketball_man:</string> + <string>:bouncing_ball_man:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏋️</string> + <key>ShortCodes</key> + <array> + <string>:weight_lifting:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏋️♀️</string> + <key>ShortCodes</key> + <array> + <string>:weight_lifting_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚴♂️</string> + <key>ShortCodes</key> + <array> + <string>:biking_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚵</string> + <key>ShortCodes</key> + <array> + <string>:mountain_bicyclist:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🚵♀️</string> + <key>ShortCodes</key> + <array> + <string>:mountain_biking_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤸♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_cartwheeling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤼</string> + <key>ShortCodes</key> + <array> + <string>:wrestling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤼♀️</string> + <key>ShortCodes</key> + <array> + <string>:women_wrestling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤽♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_playing_water_polo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤾</string> + <key>ShortCodes</key> + <array> + <string>:handball_person:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤾♀️</string> + <key>ShortCodes</key> + <array> + <string>:woman_playing_handball:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🤹♂️</string> + <key>ShortCodes</key> + <array> + <string>:man_juggling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Sport</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧘</string> + <key>ShortCodes</key> + <array> + <string>:lotus_position:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Resting</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧘♀️</string> + <key>ShortCodes</key> + <array> + <string>:lotus_position_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Resting</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🛌</string> + <key>ShortCodes</key> + <array> + <string>:sleeping_bed:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Resting</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧘♂️</string> + <key>ShortCodes</key> + <array> + <string>:lotus_position_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Resting</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🛀</string> + <key>ShortCodes</key> + <array> + <string>:bath:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Resting</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧑🤝🧑</string> + <key>ShortCodes</key> + <array> + <string>:people_holding_hands:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👫</string> + <key>ShortCodes</key> + <array> + <string>:couple:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>💏</string> + <key>ShortCodes</key> + <array> + <string>:couplekiss:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨❤️💋👨</string> + <key>ShortCodes</key> + <array> + <string>:couplekiss_man_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>💑</string> + <key>ShortCodes</key> + <array> + <string>:couple_with_heart:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨❤️👨</string> + <key>ShortCodes</key> + <array> + <string>:couple_with_heart_man_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👪</string> + <key>ShortCodes</key> + <array> + <string>:family:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👩👧</string> + <key>ShortCodes</key> + <array> + <string>:family_man_woman_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👩👦👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_woman_boy_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👨👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_man_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👨👧👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_man_girl_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👨👧👧</string> + <key>ShortCodes</key> + <array> + <string>:family_man_man_girl_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👩👧</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_woman_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👩👦👦</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_woman_boy_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👧</string> + <key>ShortCodes</key> + <array> + <string>:family_man_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👧👧</string> + <key>ShortCodes</key> + <array> + <string>:family_man_girl_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👦👦</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_boy_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👧👦</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_girl_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👭</string> + <key>ShortCodes</key> + <array> + <string>:two_women_holding_hands:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👬</string> + <key>ShortCodes</key> + <array> + <string>:two_men_holding_hands:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩❤️💋👨</string> + <key>ShortCodes</key> + <array> + <string>:couplekiss_man_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩❤️💋👩</string> + <key>ShortCodes</key> + <array> + <string>:couplekiss_woman_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩❤️👨</string> + <key>ShortCodes</key> + <array> + <string>:couple_with_heart_woman_man:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩❤️👩</string> + <key>ShortCodes</key> + <array> + <string>:couple_with_heart_woman_woman:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👩👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_woman_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👩👧👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_woman_girl_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👩👧👧</string> + <key>ShortCodes</key> + <array> + <string>:family_man_woman_girl_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👨👧</string> + <key>ShortCodes</key> + <array> + <string>:family_man_man_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👨👦👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_man_boy_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👩👦</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_woman_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👩👧👦</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_woman_girl_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👩👧👧</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_woman_girl_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👦👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_boy_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👨👧👦</string> + <key>ShortCodes</key> + <array> + <string>:family_man_girl_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👦</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_boy:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👧</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>👩👧👧</string> + <key>ShortCodes</key> + <array> + <string>:family_woman_girl_girl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person</string> + <string>Family</string> + </array> + </map> + <map> + <key>Character</key> + <string>🗣️</string> + <key>ShortCodes</key> + <array> + <string>:speaking_head:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Symbol</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👥</string> + <key>ShortCodes</key> + <array> + <string>:busts_in_silhouette:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Symbol</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👣</string> + <key>ShortCodes</key> + <array> + <string>:footprints:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Symbol</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>👤</string> + <key>ShortCodes</key> + <array> + <string>:bust_in_silhouette:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Symbol</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🫂</string> + <key>ShortCodes</key> + <array> + <string>:people_hugging:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Person Symbol</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐵</string> + <key>ShortCodes</key> + <array> + <string>:monkey_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦍</string> + <key>ShortCodes</key> + <array> + <string>:gorilla:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐶</string> + <key>ShortCodes</key> + <array> + <string>:dog:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦮</string> + <key>ShortCodes</key> + <array> + <string>:guide_dog:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐩</string> + <key>ShortCodes</key> + <array> + <string>:poodle:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦊</string> + <key>ShortCodes</key> + <array> + <string>:fox_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐱</string> + <key>ShortCodes</key> + <array> + <string>:cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐈⬛</string> + <key>ShortCodes</key> + <array> + <string>:black_cat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐯</string> + <key>ShortCodes</key> + <array> + <string>:tiger:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐆</string> + <key>ShortCodes</key> + <array> + <string>:leopard:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐎</string> + <key>ShortCodes</key> + <array> + <string>:racehorse:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦓</string> + <key>ShortCodes</key> + <array> + <string>:zebra:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦬</string> + <key>ShortCodes</key> + <array> + <string>:bison:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐂</string> + <key>ShortCodes</key> + <array> + <string>:ox:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐄</string> + <key>ShortCodes</key> + <array> + <string>:cow2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐖</string> + <key>ShortCodes</key> + <array> + <string>:pig2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐽</string> + <key>ShortCodes</key> + <array> + <string>:pig_nose:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐑</string> + <key>ShortCodes</key> + <array> + <string>:sheep:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐪</string> + <key>ShortCodes</key> + <array> + <string>:dromedary_camel:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦙</string> + <key>ShortCodes</key> + <array> + <string>:llama:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐘</string> + <key>ShortCodes</key> + <array> + <string>:elephant:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦏</string> + <key>ShortCodes</key> + <array> + <string>:rhinoceros:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐭</string> + <key>ShortCodes</key> + <array> + <string>:mouse:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐀</string> + <key>ShortCodes</key> + <array> + <string>:rat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐰</string> + <key>ShortCodes</key> + <array> + <string>:rabbit:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐿️</string> + <key>ShortCodes</key> + <array> + <string>:chipmunk:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦔</string> + <key>ShortCodes</key> + <array> + <string>:hedgehog:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐻</string> + <key>ShortCodes</key> + <array> + <string>:bear:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐨</string> + <key>ShortCodes</key> + <array> + <string>:koala:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦥</string> + <key>ShortCodes</key> + <array> + <string>:sloth:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦨</string> + <key>ShortCodes</key> + <array> + <string>:skunk:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦡</string> + <key>ShortCodes</key> + <array> + <string>:badger:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐒</string> + <key>ShortCodes</key> + <array> + <string>:monkey:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦧</string> + <key>ShortCodes</key> + <array> + <string>:orangutan:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐕</string> + <key>ShortCodes</key> + <array> + <string>:dog2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐕🦺</string> + <key>ShortCodes</key> + <array> + <string>:service_dog:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐺</string> + <key>ShortCodes</key> + <array> + <string>:wolf:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦝</string> + <key>ShortCodes</key> + <array> + <string>:raccoon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐈</string> + <key>ShortCodes</key> + <array> + <string>:cat2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦁</string> + <key>ShortCodes</key> + <array> + <string>:lion:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐅</string> + <key>ShortCodes</key> + <array> + <string>:tiger2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐴</string> + <key>ShortCodes</key> + <array> + <string>:horse:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦄</string> + <key>ShortCodes</key> + <array> + <string>:unicorn:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦌</string> + <key>ShortCodes</key> + <array> + <string>:deer:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐮</string> + <key>ShortCodes</key> + <array> + <string>:cow:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐃</string> + <key>ShortCodes</key> + <array> + <string>:water_buffalo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐷</string> + <key>ShortCodes</key> + <array> + <string>:pig:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐗</string> + <key>ShortCodes</key> + <array> + <string>:boar:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐏</string> + <key>ShortCodes</key> + <array> + <string>:ram:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐐</string> + <key>ShortCodes</key> + <array> + <string>:goat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐫</string> + <key>ShortCodes</key> + <array> + <string>:camel:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦒</string> + <key>ShortCodes</key> + <array> + <string>:giraffe:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦣</string> + <key>ShortCodes</key> + <array> + <string>:mammoth:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦛</string> + <key>ShortCodes</key> + <array> + <string>:hippopotamus:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐁</string> + <key>ShortCodes</key> + <array> + <string>:mouse2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐹</string> + <key>ShortCodes</key> + <array> + <string>:hamster:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐇</string> + <key>ShortCodes</key> + <array> + <string>:rabbit2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦫</string> + <key>ShortCodes</key> + <array> + <string>:beaver:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦇</string> + <key>ShortCodes</key> + <array> + <string>:bat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐻❄️</string> + <key>ShortCodes</key> + <array> + <string>:polar_bear:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐼</string> + <key>ShortCodes</key> + <array> + <string>:panda_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦦</string> + <key>ShortCodes</key> + <array> + <string>:otter:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦘</string> + <key>ShortCodes</key> + <array> + <string>:kangaroo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐾</string> + <key>ShortCodes</key> + <array> + <string>:feet:</string> + <string>:paw_prints:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Mammal</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦃</string> + <key>ShortCodes</key> + <array> + <string>:turkey:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐓</string> + <key>ShortCodes</key> + <array> + <string>:rooster:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐤</string> + <key>ShortCodes</key> + <array> + <string>:baby_chick:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐦</string> + <key>ShortCodes</key> + <array> + <string>:bird:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕊️</string> + <key>ShortCodes</key> + <array> + <string>:dove:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦆</string> + <key>ShortCodes</key> + <array> + <string>:duck:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦉</string> + <key>ShortCodes</key> + <array> + <string>:owl:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🪶</string> + <key>ShortCodes</key> + <array> + <string>:feather:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦚</string> + <key>ShortCodes</key> + <array> + <string>:peacock:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐔</string> + <key>ShortCodes</key> + <array> + <string>:chicken:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐣</string> + <key>ShortCodes</key> + <array> + <string>:hatching_chick:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐥</string> + <key>ShortCodes</key> + <array> + <string>:hatched_chick:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐧</string> + <key>ShortCodes</key> + <array> + <string>:penguin:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦅</string> + <key>ShortCodes</key> + <array> + <string>:eagle:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦢</string> + <key>ShortCodes</key> + <array> + <string>:swan:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦤</string> + <key>ShortCodes</key> + <array> + <string>:dodo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦩</string> + <key>ShortCodes</key> + <array> + <string>:flamingo:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦜</string> + <key>ShortCodes</key> + <array> + <string>:parrot:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bird</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐸</string> + <key>ShortCodes</key> + <array> + <string>:frog:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Amphibian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐊</string> + <key>ShortCodes</key> + <array> + <string>:crocodile:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦎</string> + <key>ShortCodes</key> + <array> + <string>:lizard:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐲</string> + <key>ShortCodes</key> + <array> + <string>:dragon_face:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦕</string> + <key>ShortCodes</key> + <array> + <string>:sauropod:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐢</string> + <key>ShortCodes</key> + <array> + <string>:turtle:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐍</string> + <key>ShortCodes</key> + <array> + <string>:snake:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐉</string> + <key>ShortCodes</key> + <array> + <string>:dragon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦖</string> + <key>ShortCodes</key> + <array> + <string>:t-rex:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Reptile</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐳</string> + <key>ShortCodes</key> + <array> + <string>:whale:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐬</string> + <key>ShortCodes</key> + <array> + <string>:dolphin:</string> + <string>:flipper:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐟</string> + <key>ShortCodes</key> + <array> + <string>:fish:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐡</string> + <key>ShortCodes</key> + <array> + <string>:blowfish:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐙</string> + <key>ShortCodes</key> + <array> + <string>:octopus:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐋</string> + <key>ShortCodes</key> + <array> + <string>:whale2:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦭</string> + <key>ShortCodes</key> + <array> + <string>:seal:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐠</string> + <key>ShortCodes</key> + <array> + <string>:tropical_fish:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦈</string> + <key>ShortCodes</key> + <array> + <string>:shark:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐚</string> + <key>ShortCodes</key> + <array> + <string>:shell:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Marine</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐌</string> + <key>ShortCodes</key> + <array> + <string>:snail:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐛</string> + <key>ShortCodes</key> + <array> + <string>:bug:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐝</string> + <key>ShortCodes</key> + <array> + <string>:bee:</string> + <string>:honeybee:</string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐞</string> + <key>ShortCodes</key> + <array> + <string>:lady_beetle:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🪳</string> + <key>ShortCodes</key> + <array> + <string>:cockroach:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕸️</string> + <key>ShortCodes</key> + <array> + <string>:spider_web:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦟</string> + <key>ShortCodes</key> + <array> + <string>:mosquito:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🪱</string> + <key>ShortCodes</key> + <array> + <string>:worm:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦋</string> + <key>ShortCodes</key> + <array> + <string>:butterfly:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🐜</string> + <key>ShortCodes</key> + <array> + <string>:ant:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🪲</string> + <key>ShortCodes</key> + <array> + <string>:beetle:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦗</string> + <key>ShortCodes</key> + <array> + <string>:cricket:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🕷️</string> + <key>ShortCodes</key> + <array> + <string>:spider:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦂</string> + <key>ShortCodes</key> + <array> + <string>:scorpion:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🪰</string> + <key>ShortCodes</key> + <array> + <string>:fly:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🦠</string> + <key>ShortCodes</key> + <array> + <string>:microbe:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Animal Bug</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💐</string> + <key>ShortCodes</key> + <array> + <string>:bouquet:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>💮</string> + <key>ShortCodes</key> + <array> + <string>:white_flower:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌹</string> + <key>ShortCodes</key> + <array> + <string>:rose:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌺</string> + <key>ShortCodes</key> + <array> + <string>:hibiscus:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌼</string> + <key>ShortCodes</key> + <array> + <string>:blossom:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌸</string> + <key>ShortCodes</key> + <array> + <string>:cherry_blossom:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🏵️</string> + <key>ShortCodes</key> + <array> + <string>:rosette:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥀</string> + <key>ShortCodes</key> + <array> + <string>:wilted_flower:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌻</string> + <key>ShortCodes</key> + <array> + <string>:sunflower:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌷</string> + <key>ShortCodes</key> + <array> + <string>:tulip:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Flower</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌱</string> + <key>ShortCodes</key> + <array> + <string>:seedling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌲</string> + <key>ShortCodes</key> + <array> + <string>:evergreen_tree:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌴</string> + <key>ShortCodes</key> + <array> + <string>:palm_tree:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌾</string> + <key>ShortCodes</key> + <array> + <string>:ear_of_rice:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>☘️</string> + <key>ShortCodes</key> + <array> + <string>:shamrock:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍁</string> + <key>ShortCodes</key> + <array> + <string>:maple_leaf:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍃</string> + <key>ShortCodes</key> + <array> + <string>:leaves:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🪴</string> + <key>ShortCodes</key> + <array> + <string>:potted_plant:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌳</string> + <key>ShortCodes</key> + <array> + <string>:deciduous_tree:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌵</string> + <key>ShortCodes</key> + <array> + <string>:cactus:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌿</string> + <key>ShortCodes</key> + <array> + <string>:herb:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍀</string> + <key>ShortCodes</key> + <array> + <string>:four_leaf_clover:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍂</string> + <key>ShortCodes</key> + <array> + <string>:fallen_leaf:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Plant Other</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍇</string> + <key>ShortCodes</key> + <array> + <string>:grapes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍉</string> + <key>ShortCodes</key> + <array> + <string>:watermelon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍋</string> + <key>ShortCodes</key> + <array> + <string>:lemon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍍</string> + <key>ShortCodes</key> + <array> + <string>:pineapple:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍎</string> + <key>ShortCodes</key> + <array> + <string>:apple:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍐</string> + <key>ShortCodes</key> + <array> + <string>:pear:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍒</string> + <key>ShortCodes</key> + <array> + <string>:cherries:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🫐</string> + <key>ShortCodes</key> + <array> + <string>:blueberries:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍅</string> + <key>ShortCodes</key> + <array> + <string>:tomato:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥥</string> + <key>ShortCodes</key> + <array> + <string>:coconut:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍈</string> + <key>ShortCodes</key> + <array> + <string>:melon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍊</string> + <key>ShortCodes</key> + <array> + <string>:mandarin:</string> + <string>:orange:</string> + <string>:tangerine:</string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍌</string> + <key>ShortCodes</key> + <array> + <string>:banana:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥭</string> + <key>ShortCodes</key> + <array> + <string>:mango:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍏</string> + <key>ShortCodes</key> + <array> + <string>:green_apple:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍑</string> + <key>ShortCodes</key> + <array> + <string>:peach:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍓</string> + <key>ShortCodes</key> + <array> + <string>:strawberry:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥝</string> + <key>ShortCodes</key> + <array> + <string>:kiwi_fruit:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🫒</string> + <key>ShortCodes</key> + <array> + <string>:olive:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Fruit</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥑</string> + <key>ShortCodes</key> + <array> + <string>:avocado:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥔</string> + <key>ShortCodes</key> + <array> + <string>:potato:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌽</string> + <key>ShortCodes</key> + <array> + <string>:corn:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🫑</string> + <key>ShortCodes</key> + <array> + <string>:bell_pepper:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥬</string> + <key>ShortCodes</key> + <array> + <string>:leafy_green:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧄</string> + <key>ShortCodes</key> + <array> + <string>:garlic:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍄</string> + <key>ShortCodes</key> + <array> + <string>:mushroom:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌰</string> + <key>ShortCodes</key> + <array> + <string>:chestnut:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍆</string> + <key>ShortCodes</key> + <array> + <string>:eggplant:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥕</string> + <key>ShortCodes</key> + <array> + <string>:carrot:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌶️</string> + <key>ShortCodes</key> + <array> + <string>:hot_pepper:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥒</string> + <key>ShortCodes</key> + <array> + <string>:cucumber:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥦</string> + <key>ShortCodes</key> + <array> + <string>:broccoli:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧅</string> + <key>ShortCodes</key> + <array> + <string>:onion:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥜</string> + <key>ShortCodes</key> + <array> + <string>:peanuts:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Vegetable</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍞</string> + <key>ShortCodes</key> + <array> + <string>:bread:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥖</string> + <key>ShortCodes</key> + <array> + <string>:baguette_bread:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥨</string> + <key>ShortCodes</key> + <array> + <string>:pretzel:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥞</string> + <key>ShortCodes</key> + <array> + <string>:pancakes:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧀</string> + <key>ShortCodes</key> + <array> + <string>:cheese:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍗</string> + <key>ShortCodes</key> + <array> + <string>:poultry_leg:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥓</string> + <key>ShortCodes</key> + <array> + <string>:bacon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍟</string> + <key>ShortCodes</key> + <array> + <string>:fries:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌭</string> + <key>ShortCodes</key> + <array> + <string>:hotdog:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌮</string> + <key>ShortCodes</key> + <array> + <string>:taco:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🫔</string> + <key>ShortCodes</key> + <array> + <string>:tamale:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧆</string> + <key>ShortCodes</key> + <array> + <string>:falafel:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍳</string> + <key>ShortCodes</key> + <array> + <string>:fried_egg:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍲</string> + <key>ShortCodes</key> + <array> + <string>:stew:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥣</string> + <key>ShortCodes</key> + <array> + <string>:bowl_with_spoon:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍿</string> + <key>ShortCodes</key> + <array> + <string>:popcorn:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧂</string> + <key>ShortCodes</key> + <array> + <string>:salt:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥐</string> + <key>ShortCodes</key> + <array> + <string>:croissant:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🫓</string> + <key>ShortCodes</key> + <array> + <string>:flatbread:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥯</string> + <key>ShortCodes</key> + <array> + <string>:bagel:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧇</string> + <key>ShortCodes</key> + <array> + <string>:waffle:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍖</string> + <key>ShortCodes</key> + <array> + <string>:meat_on_bone:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥩</string> + <key>ShortCodes</key> + <array> + <string>:cut_of_meat:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍔</string> + <key>ShortCodes</key> + <array> + <string>:hamburger:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍕</string> + <key>ShortCodes</key> + <array> + <string>:pizza:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥪</string> + <key>ShortCodes</key> + <array> + <string>:sandwich:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🌯</string> + <key>ShortCodes</key> + <array> + <string>:burrito:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥙</string> + <key>ShortCodes</key> + <array> + <string>:stuffed_flatbread:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥚</string> + <key>ShortCodes</key> + <array> + <string>:egg:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥘</string> + <key>ShortCodes</key> + <array> + <string>:shallow_pan_of_food:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🫕</string> + <key>ShortCodes</key> + <array> + <string>:fondue:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥗</string> + <key>ShortCodes</key> + <array> + <string>:green_salad:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🧈</string> + <key>ShortCodes</key> + <array> + <string>:butter:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥫</string> + <key>ShortCodes</key> + <array> + <string>:canned_food:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Prepared</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍱</string> + <key>ShortCodes</key> + <array> + <string>:bento:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍙</string> + <key>ShortCodes</key> + <array> + <string>:rice_ball:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍛</string> + <key>ShortCodes</key> + <array> + <string>:curry:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍝</string> + <key>ShortCodes</key> + <array> + <string>:spaghetti:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍢</string> + <key>ShortCodes</key> + <array> + <string>:oden:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍤</string> + <key>ShortCodes</key> + <array> + <string>:fried_shrimp:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥮</string> + <key>ShortCodes</key> + <array> + <string>:moon_cake:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥟</string> + <key>ShortCodes</key> + <array> + <string>:dumpling:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥡</string> + <key>ShortCodes</key> + <array> + <string>:takeout_box:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍘</string> + <key>ShortCodes</key> + <array> + <string>:rice_cracker:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍚</string> + <key>ShortCodes</key> + <array> + <string>:rice:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍜</string> + <key>ShortCodes</key> + <array> + <string>:ramen:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍠</string> + <key>ShortCodes</key> + <array> + <string>:sweet_potato:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍣</string> + <key>ShortCodes</key> + <array> + <string>:sushi:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍥</string> + <key>ShortCodes</key> + <array> + <string>:fish_cake:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🍡</string> + <key>ShortCodes</key> + <array> + <string>:dango:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + <map> + <key>Character</key> + <string>🥠</string> + <key>ShortCodes</key> + <array> + <string>:fortune_cookie:</string> + <string></string> + <string></string> + </array> + <key>Categories</key> + <array> + <string>Food Asian</string> + <string></string> + </array> + </map> + </array> +</llsd> diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 4678d65b85..c6776ad039 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -1908,7 +1908,7 @@ Only large parcels can be listed in search. </text> <check_box height="16" - label="Obscure MOAP" + label="Restrict MOAP to this parcel" layout="topleft" left="110" left_pad="0" 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..e9ea8f4de7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_emoji_complete.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_close="false" + can_dock="false" + can_drag_on_left="false" + can_minimize="false" + can_resize="false" + can_tear_off="false" + header_height="0" + layout="topleft" + legacy_header_height="0" + height="40" + single_instance="true" + width="240" + > + <emoji_complete + autosize="true" + height="30" + follows="top|left" + layout="topleft" + left="5" + max_emoji="7" + name="emoji_complete_ctrl" + top="5" + width="230" + > + </emoji_complete> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_how_to.xml b/indra/newview/skins/default/xui/en/floater_how_to.xml index baff8e1bc0..19e42798af 100644 --- a/indra/newview/skins/default/xui/en/floater_how_to.xml +++ b/indra/newview/skins/default/xui/en/floater_how_to.xml @@ -3,7 +3,6 @@ legacy_header_height="18" can_resize="false" can_minimize="false" - can_close="false" height="525" layout="topleft" name="floater_how_to" 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 15f02ab9c3..da84fbeea6 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -221,20 +221,6 @@ right="-1" bottom="-1"> <layout_panel - auto_resize="false" - height="26" - name="translate_chat_checkbox_lp"> - <check_box - top="10" - control_name="TranslateChat" - enabled="true" - height="16" - label="Translate chat" - left="5" - name="translate_chat_checkbox" - width="230" /> - </layout_panel> - <layout_panel name="chat_holder"> <chat_history font="SansSerifSmall" diff --git a/indra/newview/skins/default/xui/en/floater_outfit_snapshot.xml b/indra/newview/skins/default/xui/en/floater_outfit_snapshot.xml deleted file mode 100644 index 15c480f144..0000000000 --- a/indra/newview/skins/default/xui/en/floater_outfit_snapshot.xml +++ /dev/null @@ -1,351 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<floater - positioning="cascading" - legacy_header_height="18" - can_minimize="true" - can_resize="false" - can_close="true" - height="455" - layout="topleft" - name="outfit_snapshot" - single_instance="true" - help_topic="snapshot" - save_rect="true" - save_visibility="false" - title="OUTFIT SNAPSHOT" - width="624" - min_height="455"> - <floater.string - name="unknown"> - unknown - </floater.string> - <string - name="inventory_progress_str"> - Saving to Inventory - </string> - <string - name="inventory_succeeded_str"> - Saved to Inventory! - </string> - <string - name="inventory_failed_str"> - Failed to save to inventory. - </string> - <button - follows="left|top" - height="25" - image_overlay="Refresh_Off" - image_hover_unselected="Toolbar_Middle_Over" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - image_overlay_alignment="left" - imgoverlay_label_space="5" - pad_bottom="0" - halign="left" - layout="topleft" - left="10" - label="REFRESH" - name="new_snapshot_btn" - top_pad="26" - width="167" /> - <button - follows="left|top" - control_name="AdvanceOutfitSnapshot" - invisibility_control="AdvanceOutfitSnapshot" - height="25" - is_toggle="true" - layout="topleft" - image_hover_unselected="Toolbar_Middle_Over" - image_selected="Toolbar_Middle_Off" - image_unselected="Toolbar_Middle_Off" - image_overlay="Conv_toolbar_expand" - name="retract_btn" - left_pad="1" - top_delta="0" - width="31" /> - <button - follows="left|top" - control_name="AdvanceOutfitSnapshot" - visibility_control="AdvanceOutfitSnapshot" - height="25" - is_toggle="true" - layout="topleft" - image_overlay="Conv_toolbar_collapse" - image_hover_unselected="Toolbar_Middle_Over" - image_selected="Toolbar_Middle_Off" - image_unselected="Toolbar_Middle_Off" - name="extend_btn" - left_delta="0" - top_delta="0" - width="31" /> - <panel - height="154" - layout="topleft" - follows="top|left" - left="0" - name="advanced_options_panel" - top_pad="-6" - width="210"> - <view_border - bevel_style="in" - follows="left|top|right" - height="1" - left="10" - layout="topleft" - name="advanced_options_hr" - right="-1" - top_pad="5" - /> - <text - type="string" - length="1" - follows="left|top" - height="13" - layout="topleft" - left="10" - name="layer_type_label" - top_pad="10" - width="100"> - Capture: - </text> - <check_box - label="Interface" - layout="topleft" - left="30" - height="16" - top_pad="8" - width="180" - name="ui_check" /> - <check_box - label="HUDs" - layout="topleft" - height="16" - left="30" - top_pad="1" - width="180" - name="hud_check" /> - <check_box - label="Freeze frame (fullscreen)" - layout="topleft" - height="16" - left="10" - top_pad="1" - width="180" - name="freeze_frame_check" /> - <check_box - label="Auto-refresh" - layout="topleft" - height="16" - left="10" - top_pad="1" - width="180" - name="auto_snapshot_check" /> - <text - type="string" - length="1" - follows="left|top" - height="13" - layout="topleft" - left="10" - name="filter_list_label" - top_pad="10" - width="50"> - Filter: - </text> - <combo_box - control_name="PhotoFilters" - follows="left|right|top" - name="filters_combobox" - tool_tip="Image filters" - top_delta="-3" - left="50" - right="-1" - height="21" - width="135"> - <combo_box.item - label="No Filter" - name="NoFilter" - value="NoFilter" /> - </combo_box> - <view_border - bevel_style="in" - follows="left|top|right" - height="1" - left="10" - layout="topleft" - name="advanced_options_hr" - right="-1" - top_pad="7" - /> - </panel> - <panel - class="llpaneloutfitsnapshotinventory" - follows="left|top" - height="230" - layout="topleft" - left="0" - name="panel_outfit_snapshot_inventory" - filename="panel_outfit_snapshot_inventory.xml" - top_pad="10" - width="215" - /> - <view_border - bevel_style="in" - follows="left|top" - height="1" - left="10" - layout="topleft" - name="status_hr" - width="199" - top_pad="-16"/> - <panel - background_visible="false" - follows="left|top" - font="SansSerifLarge" - halign="center" - height="20" - layout="topleft" - left="10" - length="1" - name="succeeded_panel" - width="198" - top_pad="1" - type="string" - visible="false"> - <text - follows="all" - font="SansSerif" - halign="center" - height="18" - layout="topleft" - left="1" - length="1" - name="succeeded_lbl" - right="-1" - text_color="0.2 0.85 0.2 1" - top="4" - translate="false" - type="string"> - Succeeded - </text> - </panel> - <panel - background_visible="false" - follows="left|top" - font="SansSerifLarge" - halign="center" - height="20" - layout="topleft" - left="10" - length="1" - name="failed_panel" - width="198" - top_delta="0" - type="string" - visible="false"> - <text - follows="all" - font="SansSerif" - halign="center" - height="18" - layout="topleft" - left="1" - length="1" - name="failed_lbl" - right="-1" - text_color="0.95 0.4 0.4 1" - top="4" - translate="false" - type="string"> - Failed - </text> - </panel> - <loading_indicator - follows="left|top" - height="24" - layout="topleft" - name="working_indicator" - left="10" - top_delta="0" - visible="false" - width="24" /> - <text - follows="left|top" - font="SansSerifBold" - height="14" - layout="topleft" - left_pad="3" - length="1" - halign="left" - name="working_lbl" - top_delta="5" - translate="false" - type="string" - visible="false" - width="162"> - Working - </text> - <text - follows="left|top" - font="SansSerifBold" - halign="left" - height="18" - layout="topleft" - left="10" - length="1" - name="refresh_lbl" - text_color="0.95 0.4 0.4 1" - top_delta="0" - translate="false" - type="string" - visible="false" - width="130"> - Refresh to save. - </text> - <ui_ctrl - layout="topleft" - name="thumbnail_placeholder" - top="23" - left="215" - width="400" - height="400" - follows="top|left"/> - <view_border - bevel_style="in" - height="21" - layout="topleft" - name="img_info_border" - top_pad="0" - right="-10" - follows="left|top|right" - left_delta="0"/> - <text - type="string" - font="SansSerifSmall" - length="1" - follows="left|top|right" - height="14" - layout="topleft" - left="220" - right="-20" - halign="left" - name="image_res_text" - top_delta="5" - width="200"> - [WIDTH]px (width) x [HEIGHT]px (height) - </text> - <text - follows="right|top" - font="SansSerifSmall" - height="14" - layout="topleft" - left="-65" - length="1" - halign="right" - name="file_size_label" - top_delta="0" - type="string" - width="50"> - [SIZE] KB - </text> -</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/floater_simple_outfit_snapshot.xml b/indra/newview/skins/default/xui/en/floater_simple_outfit_snapshot.xml new file mode 100644 index 0000000000..5ece7b85d5 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_simple_outfit_snapshot.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + positioning="cascading" + legacy_header_height="18" + can_minimize="true" + can_resize="false" + can_close="true" + height="305" + layout="topleft" + name="simple_outfit_snapshot" + single_instance="true" + help_topic="snapshot" + save_rect="true" + save_visibility="false" + title="OUTFIT SNAPSHOT" + width="351"> + <ui_ctrl + layout="topleft" + name="thumbnail_placeholder" + top="18" + left="22" + width="335" + height="200" + follows="top|left"/> + <button + follows="left|bottom" + height="22" + layout="topleft" + left="29" + label="Take photo" + name="new_snapshot_btn" + bottom="-15" + width="90" /> + <button + follows="left|bottom" + height="22" + layout="topleft" + left_pad="10" + label="Save (L$[UPLOAD_COST])" + name="save_btn" + width="90" /> + <button + follows="left|bottom" + height="22" + layout="topleft" + left_pad="10" + label="Cancel" + name="cancel_btn" + width="90" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_tos.xml b/indra/newview/skins/default/xui/en/floater_tos.xml index a8db0aca4e..7e172e138a 100644 --- a/indra/newview/skins/default/xui/en/floater_tos.xml +++ b/indra/newview/skins/default/xui/en/floater_tos.xml @@ -76,7 +76,7 @@ word_wrap="true" text_readonly_color="LabelDisabledColor" width="552"> - I have read and agree to the Second Life Terms and Conditions, Privacy Policy, and Terms of Service, including the dispute resolution requirements. + I consent to the Linden Lab Terms of Service, Second Life Terms and Conditions, and acknowledge receipt of the Privacy Policy. </text> <button enabled="false" 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/main_view.xml b/indra/newview/skins/default/xui/en/main_view.xml index 842184de88..bab37c6258 100644 --- a/indra/newview/skins/default/xui/en/main_view.xml +++ b/indra/newview/skins/default/xui/en/main_view.xml @@ -8,16 +8,6 @@ tab_stop="false" name="main_view" width="1024"> - - <!-- At the moment layout_stack is not an LLUICtrl, - but Tab requires focus_root to function and focus_root - functionality is implemented in LLUICtrl --> - <panel follows="all" - height="768" - name="menu_tab_wrapper" - mouse_opaque="false" - focus_root="true" - top="0"> <layout_stack border_size="0" follows="all" mouse_opaque="false" @@ -25,31 +15,35 @@ name="menu_stack" orientation="vertical" top="0"> + <!-- Menu, nav bar and status bar need common focus_root--> <layout_panel mouse_opaque="true" follows="left|right|top" name="status_bar_container" + focus_root="true" height="19" left="0" top="0" width="1024" auto_resize="false" - default_tab_group="1" visible="true"> <view mouse_opaque="false" - follows="all" + follows="top|left|right" name="menu_bar_holder" + tab_group="1" left="0" top="0" width="1024" - tab_group="1" height="19"/> + <view mouse_opaque="false" + follows="top|left|right" + name="nav_bar_container" + tab_group="3" + left="0" + top="19" + width="1024" + height="34" + visible="false"/> </layout_panel> - <layout_panel auto_resize="false" - height="34" - mouse_opaque="false" - name="nav_bar_container" - width="1024" - visible="false"/> <layout_panel auto_resize="true" follows="all" height="500" @@ -109,7 +103,6 @@ tab_stop="false"/> </layout_panel> </layout_stack> - </panel> <!--menu_tab_wrapper--> <panel top="0" follows="all" 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 58584345a9..66b70512a6 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3484,6 +3484,18 @@ function="World.EnvPreset" parameter="https://cryptic-ridge-1632.herokuapp.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 diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 1ddec93668..d1a99133f0 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -9689,7 +9689,7 @@ Do you wish to continue? The selected object affects the navmesh. Changing it to a Flexible Path will remove it from the navmesh. <tag>confirm</tag> <usetemplate - ignoretext="The selected object affects the navmesh. Changing it to a Flexible Path will remove it from the navmesh." + ignoretext="The selected object affects the navmesh. Changing it to a Flexible Path will remove it from the navmesh." name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -11914,4 +11914,20 @@ Unpacking: [UNPACK_TIME]s [USIZE]KB yestext="OK"/> </notification> + <notification + icon="alertmodal.tga" + name="RiggedMeshAttachedToHUD" + type="alertmodal"> + An object "[NAME]" attached to HUD point "[POINT]" contains rigged mesh. + +Rigged mesh objects are designed for attachment to the avatar. You will see this object but no one else will. + +If you want others to see this object, remove it and re-attach it to an avatar attachment point. + <tag>confirm</tag> + <usetemplate + ignoretext="Warn me when rigged mesh is attached to HUD point." + name="okignore" + yestext="OK"/> + </notification> + </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml index 4de56b424e..edc1fb21e7 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml @@ -18,26 +18,6 @@ orientation="vertical" width="242"> <layout_panel - auto_resize="false" - height="26" - layout="topleft" - left_delta="0" - name="translate_chat_checkbox_lp" - top_delta="0" - visible="true" - width="230"> - <check_box - top="10" - control_name="TranslateChat" - enabled="true" - height="16" - label="Translate chat" - layout="topleft" - left="5" - name="translate_chat_checkbox" - width="230" /> - </layout_panel> - <layout_panel auto_resize="true" height="138" left_delta="0" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml index 42a34d171a..ab2e9c72f3 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -322,154 +322,161 @@ name="enable_voice_check" width="110"/> <!-- --> - <text - follows="left|top" - layout="topleft" - height="15" - left="0" - top_pad="3" - width="120" - halign="right" - name="media_autoplay_label"> - Media auto-play - </text> - <combo_box + <text + type="string" + length="1" + follows="left|top" + layout="topleft" + left="23" + top_delta="22" + name="Listen media from" + height="15" + word_wrap="true" + width="112"> + Hear media and sounds from: + </text> + <radio_group + control_name="MediaSoundsEarLocation" + follows="left|top" + top_delta="-6" + layout="topleft" + left_pad="20" + width="360" + height="40" + name="media_ear_location"> + <radio_item + height="19" + label="Camera position" + follows="left|top" + layout="topleft" + name="0" + width="200"/> + <radio_item + height="19" + follows="left|top" + label="Avatar position" + layout="topleft" + left_delta="0" + name="1" + top_delta ="18" + width="200" /> + </radio_group> + <check_box + name="media_show_on_others_btn" + control_name="MediaShowOnOthers" + value="true" + follows="left|top" + layout="topleft" + height="15" + top_pad="8" + tool_tip="Uncheck this to hide media attached to other avatars nearby" + label="Play media attached to other avatars" + left="20" + width="230"/> + <text + follows="left|top" + layout="topleft" + height="15" + left="23" + top_pad="8" + width="120" + name="media_autoplay_label"> + Auto-play media + </text> + <combo_box control_name="ParcelMediaAutoPlayEnable" enabled_control="AudioStreamingMedia" follows="left|top" layout="topleft" height="23" - left_pad="7" + left_pad="-15" top_delta="-4" name="media_auto_play_combo" - width="100"> - <item - label="No" - name="autoplay_disabled" - value="0"/> - <item - label="Yes" - name="autoplay_enabled" - value="1"/> - <item - label="Ask" - name="autoplay_ask" - value="2"/> - </combo_box> - <check_box - name="media_show_on_others_btn" - control_name="MediaShowOnOthers" - value="true" - follows="left|bottom|right" - height="15" - tool_tip="Uncheck this to hide media attached to other avatars nearby" - label="Play media attached to other avatars" - left="25" - width="230"/> - <check_box - name="gesture_audio_play_btn" - control_name="EnableGestureSounds" - disabled_control="MuteAudio" - value="true" - follows="left|bottom|right" - height="15" - tool_tip="Check this to hear sounds from gestures" - label="Play sounds from gestures" - top_pad="1" - left="25"/> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="25" - name="voice_chat_settings" - width="200" - top_pad="16"> - Voice Chat Settings - </text> - <text - type="string" - length="1" - follows="left|top" - layout="topleft" - left="46" - top_delta="16" - name="Listen from" - width="112"> - Listen from: - </text> - <icon - follows="left|top" - height="18" - image_name="Cam_FreeCam_Off" - layout="topleft" - name="camera_icon" - mouse_opaque="false" - visible="true" - width="18" - left_pad="-4" - top_delta="-5"/> - <icon - follows="left|top" - height="18" - image_name="Move_Walk_Off" - layout="topleft" - left_pad="170" - name="avatar_icon" - mouse_opaque="false" - visible="true" - width="18" - top_delta="0" /> - <radio_group - enabled_control="EnableVoiceChat" - control_name="VoiceEarLocation" - follows="left|top" - layout="topleft" - left_delta="-168" - width="360" - height="20" - name="ear_location"> - <radio_item - height="19" - label="Camera position" - follows="left|top" - layout="topleft" - name="0" - width="200"/> - <radio_item - height="19" - follows="left|top" - label="Avatar position" - layout="topleft" - left_pad="-16" - name="1" - top_delta ="0" - width="200" /> - </radio_group> + width="115"> + <item + label="Never" + name="autoplay_disabled" + value="0"/> + <item + label="Always" + name="autoplay_enabled" + value="1"/> + <item + label="Ask" + name="autoplay_ask" + value="2"/> + </combo_box> + <text + type="string" + length="1" + follows="left|top" + layout="topleft" + left="23" + top_delta="30" + name="Listen from" + width="112"> + Hear voice from: + </text> + <radio_group + enabled_control="EnableVoiceChat" + control_name="VoiceEarLocation" + follows="left|top" + layout="topleft" + left_pad="20" + top_delta="-6" + width="360" + height="40" + name="ear_location"> + <radio_item + height="19" + label="Camera position" + follows="left|top" + layout="topleft" + name="0" + width="200"/> + <radio_item + height="19" + follows="left|top" + label="Avatar position" + layout="topleft" + left_delta="0" + name="1" + top_delta ="18" + width="200" /> + </radio_group> <check_box control_name="LipSyncEnabled" follows="left|top" height="15" label="Move avatar lips when speaking" layout="topleft" - left="44" + left="20" name="enable_lip_sync" - top_pad="5" + top_pad="10" width="237"/> - <check_box - follows="top|left" - enabled_control="EnableVoiceChat" - control_name="PushToTalkToggle" - height="15" - label="Toggle speak on/off when I press button in toolbar" - layout="topleft" - left="44" - name="push_to_talk_toggle_check" - width="237" - tool_tip="When in toggle mode, press and release the trigger key ONCE to switch your microphone on or off. When not in toggle mode, the microphone broadcasts your voice only while the trigger is being held down." - top_pad="3"/> + <check_box + follows="top|left" + enabled_control="EnableVoiceChat" + control_name="PushToTalkToggle" + height="15" + label="Toggle speak on/off when I press button in toolbar" + layout="topleft" + left="20" + name="push_to_talk_toggle_check" + width="237" + tool_tip="When in toggle mode, press and release the trigger key ONCE to switch your microphone on or off. When not in toggle mode, the microphone broadcasts your voice only while the trigger is being held down." + top_pad="5"/> + <check_box + name="gesture_audio_play_btn" + control_name="EnableGestureSounds" + disabled_control="MuteAudio" + value="true" + follows="left|bottom|right" + height="15" + tool_tip="Check this to hear sounds from gestures" + label="Play sounds from gestures" + top_pad="5" + left="20"/> <button control_name="ShowDeviceSettings" follows="left|top" @@ -478,7 +485,7 @@ label="Voice Input/Output devices" layout="topleft" left="20" - top_pad="6" + top_pad="9" name="device_settings_btn" width="230"> </button> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 6f95e282ca..cf5d98aa9a 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -4300,6 +4300,10 @@ name="Command_360_Capture_Tooltip">Capture a 360 equirectangular image</string> <string name="Mav_Details_MAV_CONFIRMATION_DATA_MISMATCH"> The physics shape contains bad confirmation data. Try to correct the physics model. </string> + <!-- MAV_BLOCK_MISSING means server didn't get and couldn't substitute physics_convex, high_lod or BoundingVerts--> + <string name="Mav_Details_MAV_BLOCK_MISSING"> + Missing data. Make sure high lod is present and valid. Set the physics model if not set. + </string> <string name="Mav_Details_MAV_UNKNOWN_VERSION"> The physics shape does not have correct version. Set the correct version for the physics model. </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/emoji_complete.xml b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml new file mode 100644 index 0000000000..370f1d174e --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/emoji_complete.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<emoji_complete + autosize="false" + font="EmojiHuge" + hover_image="ListItem_Over" + max_emoji="7" + padding="8" + selected_image="ListItem_Select" + > +</emoji_complete> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 59eb18b734..60f8cfc26b 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") @@ -517,6 +517,10 @@ class WindowsManifest(ViewerManifest): self.path("OpenAL32.dll") self.path("alut.dll") + # For ICU4C + self.path("icudt48.dll") + self.path("icuuc48.dll") + # For textures self.path("openjpeg.dll") |