diff --git a/.gitignore b/.gitignore index f94d376ae5..5fa4a04a62 100755 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,4 @@ my_autobuild.xml compile_commands.json # ignore tracy for now -indra/tracy \ No newline at end of file +indra/tracy diff --git a/autobuild.xml b/autobuild.xml index 3fa8fe8865..0c2b1db6b7 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -261,6 +261,36 @@ version 1.2.15 + Tracy + + copyright + Copyright (c) 2017-2021, Bartosz Taudul + description + A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications. + license + BSD + license_file + LICENSES/Tracy.txt + name + Tracy + platforms + + common + + archive + + hash + b0a0fc6b7bda02fe14cccea2dd342058 + url + http://3p.firestormviewer.org/Tracy-0.7.6-windows-210902110.tar.bz2 + + name + common + + + version + 0.7.6 + apr_suite copyright @@ -796,9 +826,9 @@ archive hash - 7599bb6b05a5e48ce78180735e5d3355 + 9a586d333b3c35389b29c7e8c7ebcc8f url - http://3p.firestormviewer.org/dullahan-1.8.0.202007261348_81.3.10_gb223419_chromium-81.0.4044.138-windows-202081313.tar.bz2 + http://3p.firestormviewer.org/dullahan-1.8.0.202011211324_81.3.10_gb223419_chromium-81.0.4044.138-windows-203261227.tar.bz2 name windows @@ -808,9 +838,9 @@ archive hash - 92ced00cd0f89d0154ce2bcc6da086e0 + 698e79226d0fb3b935509769a36bffd4 url - http://3p.firestormviewer.org/dullahan-1.8.0.202007261348_81.3.10_gb223419_chromium-81.0.4044.138-windows64-202081325.tar.bz2 + http://3p.firestormviewer.org/dullahan-1.8.0.202011211324_81.3.10_gb223419_chromium-81.0.4044.138-windows64-203261222.tar.bz2 name windows64 @@ -1824,9 +1854,9 @@ archive hash - eba43dcfeb1c14aab25f588d9f625839 + dce2cc624e216eb991412ec0c5538cf9 url - file:///opt/firestorm/kdu-8.0.6-linux64-202302217.tar.bz2 + file:///opt/firestorm/kdu-8.1-linux64-210652339.tar.bz2 name linux64 @@ -1836,9 +1866,9 @@ archive hash - f5c682b490672aadf97ec9b938b351a9 + 1cd284c85b5f05fff6b286e85b55ebf2 url - file:///c:/cygwin/opt/firestorm/kdu-8.0.6-windows-202302209.tar.bz2 + file:///c:/cygwin/opt/firestorm/kdu-8.1-windows-210862059.tar.bz2 name windows @@ -1848,9 +1878,9 @@ archive hash - f5c682b490672aadf97ec9b938b351a9 + 1cd284c85b5f05fff6b286e85b55ebf2 url - file:///c:/cygwin/opt/firestorm/kdu-8.0.6-windows-202302209.tar.bz2 + file:///c:/cygwin/opt/firestorm/kdu-8.1-windows-210862059.tar.bz2 name windows64 diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index d8f6db9003..0c02ae3425 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -31,20 +31,20 @@ if (OPENSIM) add_definitions(-DOPENSIM=1) if (SINGLEGRID) add_definitions(-DSINGLEGRID=1 -DSINGLEGRID_URI=\"${SINGLEGRID_URI}\") - message("compiling with OpenSim support - Single Grid version (${SINGLEGRID_URI})") + message(STATUS "Compiling with OpenSim support - Single Grid version (${SINGLEGRID_URI})") else (SINGLEGRID) - message("compiling with OpenSim support") + message(STATUS "Compiling with OpenSim support") endif (SINGLEGRID) else (OPENSIM) if (SINGLEGRID) - message( WARNING "Value for SINGLEGRID is unused during Havok/SecondLife builds" ) + message(WARNING "Value for SINGLEGRID is unused during Havok/SecondLife builds" ) endif() - message("compiling without OpenSim support") + message(STATUS "Compiling without OpenSim support") endif (OPENSIM) if (HAVOK_TPV) add_definitions(-DHAVOK_TPV=1) - message("compiling with Havok libraries") + message(STATUS "Compiling with Havok libraries") endif (HAVOK_TPV) # @@ -52,7 +52,7 @@ endif (HAVOK_TPV) option(TESTBUILD "Generating test build" OFF) if(TESTBUILD AND TESTBUILDPERIOD) add_definitions(-DTESTBUILD=1 -DTESTBUILDPERIOD=${TESTBUILDPERIOD}) - message("creating test build version; test period: ${TESTBUILDPERIOD} days") + message(STATUS "Creating test build version; test period: ${TESTBUILDPERIOD} days") endif(TESTBUILD AND TESTBUILDPERIOD) # @@ -64,18 +64,27 @@ if (USE_AVX_OPTIMIZATION) message(FATAL_ERROR "You cannot use AVX and AVX2 at the same time!") else (USE_AVX2_OPTIMIZATION) add_definitions(-DUSE_AVX_OPTIMIZATION=1) - message("compiling with AVX optimizations") + message(STATUS "Compiling with AVX optimizations") endif (USE_AVX2_OPTIMIZATION) elseif (USE_AVX2_OPTIMIZATION) add_definitions(-DUSE_AVX2_OPTIMIZATION=1) - message("compiling with AVX2 optimizations") + message(STATUS "Compiling with AVX2 optimizations") else (USE_AVX_OPTIMIZATION) - message("compiling without AVX optimizations") + message(STATUS "Compiling without AVX optimizations") endif (USE_AVX_OPTIMIZATION) # [AVX Optimization] add_subdirectory(cmake) +# Tracy Profiler support +option(USE_TRACY_PROFILER "Tracy Profiler support" OFF) +if (USE_TRACY_PROFILER) + message(STATUS "Compiling with Tracy profiler") +else (USE_TRACY_PROFILER) + message(STATUS "Compiling without Tracy profiler") +endif (USE_TRACY_PROFILER) +# Tracy Profiler support + add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llappearance) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index ce8b396cc1..8e62e95bac 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -60,6 +60,15 @@ if (WINDOWS) # Don't build DLLs. set(BUILD_SHARED_LIBS OFF) + if( USE_COMPILERCACHE ) + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_REELASE}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + endif() + # for "backwards compatibility", cmake sneaks in the Zm1000 option which royally # screws incredibuild. this hack disables it. # for details see: http://connect.microsoft.com/VisualStudio/feedback/details/368107/clxx-fatal-error-c1027-inconsistent-values-for-ym-between-creation-and-use-of-precompiled-headers @@ -139,7 +148,6 @@ if (WINDOWS) # /arch:SSE2 /fp:fast ) - # Nicky: x64 implies SSE2 if( ADDRESS_SIZE EQUAL 32 ) add_definitions( /arch:SSE2 ) diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake index 86e42fa0ba..3406e5fd15 100644 --- a/indra/cmake/BuildVersion.cmake +++ b/indra/cmake/BuildVersion.cmake @@ -12,7 +12,7 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n if (DEFINED ENV{revision}) set(VIEWER_VERSION_REVISION $ENV{revision}) - message("Revision (from environment): ${VIEWER_VERSION_REVISION}") + message(STATUS "Revision (from environment): ${VIEWER_VERSION_REVISION}") elseif (DEFINED ENV{AUTOBUILD_BUILD_ID}) set(VIEWER_VERSION_REVISION $ENV{AUTOBUILD_BUILD_ID}) @@ -38,23 +38,23 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n OUTPUT_STRIP_TRAILING_WHITESPACE ) if ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") - message("Revision (from hg) ${VIEWER_VERSION_REVISION}") + message(STATUS "Revision (from hg) ${VIEWER_VERSION_REVISION}") else ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") - message("Revision not set (repository not found?); using 0") + message(STATUS "Revision not set (repository not found?); using 0") set(VIEWER_VERSION_REVISION 0 ) endif ("${VIEWER_VERSION_REVISION}" MATCHES "^[0-9]+$") else (DEFINED MERCURIAL AND DEFINED SED) - message("Revision not set: 'hg' or 'sed' not found; using 0") + message(STATUS "Revision not set: 'hg' or 'sed' not found; using 0") set(VIEWER_VERSION_REVISION 0) endif (DEFINED MERCURIAL AND DEFINED SED) endif (DEFINED ENV{revision}) - message("Building '${VIEWER_CHANNEL}' Version ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") + message(STATUS "Building '${VIEWER_CHANNEL}' Version ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}") else ( EXISTS ${VIEWER_VERSION_BASE_FILE} ) message(SEND_ERROR "Cannot get viewer version from '${VIEWER_VERSION_BASE_FILE}'") endif ( EXISTS ${VIEWER_VERSION_BASE_FILE} ) if ("${VIEWER_VERSION_REVISION}" STREQUAL "") - message("Ultimate fallback, revision was blank or not set: will use 0") + message(STATUS "Ultimate fallback, revision was blank or not set: will use 0") set(VIEWER_VERSION_REVISION 0) endif ("${VIEWER_VERSION_REVISION}" STREQUAL "") diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 7819669385..74d32defea 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -86,6 +86,7 @@ set(cmake_SOURCE_FILES PulseAudio.cmake Python.cmake TemplateCheck.cmake + Tracy.cmake Tut.cmake UI.cmake UnixInstall.cmake diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index 8900419f9b..8c209fdb7e 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -4,12 +4,21 @@ include(APR) include(Boost) include(EXPAT) include(ZLIB) +include(Tracy) # Tracy profiler +# Add Tracy profiler support +#set(LLCOMMON_INCLUDE_DIRS +# ${LIBS_OPEN_DIR}/llcommon +# ${APRUTIL_INCLUDE_DIR} +# ${APR_INCLUDE_DIR} +# ) set(LLCOMMON_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llcommon ${APRUTIL_INCLUDE_DIR} ${APR_INCLUDE_DIR} + ${TRACY_INCLUDE_DIR} ) +# set(LLCOMMON_SYSTEM_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ) diff --git a/indra/cmake/Tracy.cmake b/indra/cmake/Tracy.cmake new file mode 100644 index 0000000000..b3bfb6fc65 --- /dev/null +++ b/indra/cmake/Tracy.cmake @@ -0,0 +1,16 @@ +# Tracy Profiler support. +if (USE_TRACY_PROFILER) + include(Prebuilt) + use_prebuilt_binary(Tracy) + if (WINDOWS) + # set(TRACY_LIBRARIES + # ${ARCH_PREBUILT_DIRS_RELEASE}/Tracy.lib + add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS -DWINVER=0x0601 ) + set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy) + elseif (DARWIN) + message(FATAL_ERROR "Tracy is not yet implemented in FS for OSX.") + else (WINDOWS) + add_definitions(-DTRACY_ENABLE=1 -DTRACY_NO_FASTTIMERS ) + set(TRACY_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tracy) + endif (WINDOWS) +endif (USE_TRACY_PROFILER) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 3790107fd1..259bea4b67 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -804,7 +804,7 @@ bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename) return false; } - if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB)) + if (!gDirUtilp->fileExists(filename)) { // File not found, abort. return false; diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index e24ac88849..66cb19479f 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -69,6 +69,7 @@ set(llcommon_SOURCE_FILES llinitparam.cpp llinitdestroyclass.cpp llinstancetracker.cpp + llkeybind.cpp llleap.cpp llleaplistener.cpp llliveappconfig.cpp @@ -180,6 +181,7 @@ set(llcommon_HEADER_FILES llinitdestroyclass.h llinitparam.h llinstancetracker.h + llkeybind.h llkeythrottle.h llleap.h llleaplistener.h @@ -254,6 +256,11 @@ set(llcommon_HEADER_FILES ) # Add all nd* files. memory pool, intrinsics, ... +# Tracy Profiler support +list(APPEND llcommon_SOURCE_FILES fstelemetry.cpp) +if (USE_TRACY_PROFILER) + list(APPEND llcommon_SOURCE_FILES fstracyclient.cpp) +endif() SET( llcommon_ND_SOURCE_FILES nd/ndexceptions.cpp diff --git a/indra/llcommon/fstelemetry.cpp b/indra/llcommon/fstelemetry.cpp new file mode 100644 index 0000000000..c2bc80b66a --- /dev/null +++ b/indra/llcommon/fstelemetry.cpp @@ -0,0 +1,31 @@ +/** + * @file fstelemetry.cpp + * @brief fstelemetry Telemetry abstraction for FS + * + * $LicenseInfo:firstyear=2021&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2021, The Phoenix Firestorm Project, 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 + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ +#include "fstelemetry.h" +namespace FSTelemetry +{ + bool active{false}; +} \ No newline at end of file diff --git a/indra/llcommon/fstelemetry.h b/indra/llcommon/fstelemetry.h new file mode 100644 index 0000000000..87ec75b00a --- /dev/null +++ b/indra/llcommon/fstelemetry.h @@ -0,0 +1,69 @@ +#pragma once +#ifndef FS_TELEMETRY_H_INCLUDED +#define FS_TELEMETRY_H_INCLUDED +/** + * @file fstelemetry.h + * @brief fstelemetry Telemetry abstraction for FS + * + * $LicenseInfo:firstyear=2021&license=fsviewerlgpl$ + * Phoenix Firestorm Viewer Source Code + * Copyright (C) 2021, The Phoenix Firestorm Project, 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 + * + * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA + * http://www.firestormviewer.org + * $/LicenseInfo$ + */ + +// define a simple set of empty macros that allow us to build without the Tracy profiler installed in 3p +// this is similar to the profiler abstraction used by LL but as they have no plans to release that any time soon we'll replace it +// Just a minimal set at the moment will add locks/gpu/memory and other stuff later. + +// generic switch (in case we ever add others or incorporate commercial tools like RAD Games if LL were to share a license) +// turn off in the else statement below. +#define FS_HAS_TELEMETRY_SUPPORT + +#ifdef TRACY_ENABLE // (Tracy open source telemetry) +#include "Tracy.hpp" + +#define FSZone ZoneNamed( ___tracy_scoped_zone, FSTelemetry::active) +#define FSZoneN( name ) ZoneNamedN( ___tracy_scoped_zone, name, FSTelemetry::active) +#define FSZoneC( color ) ZoneNamedC( ___tracy_scoped_zone, color, FSTelemetry::active) +#define FSZoneNC( name, color ) ZoneNamedNC( ___tracy_scoped_zone, name, color, FSTelemetry::active) +#define FSPlot( name, value ) TracyPlot( name, value) +#define FSFrameMark FrameMark +#define FSTelemetryIsConnected TracyIsConnected + +#else // (no telemetry) + +// No we don't want no stinkin' telemetry. move along +#undef FS_HAS_TELEMETRY_SUPPORT + +#define FSZone +#define FSZoneN( name ) +#define FSZoneC( color ) +#define FSZoneNC( name, color ) +#define FSPlot( name, value ) +#define FSFrameMark +#define FSTelemetryIsConnected +#endif // TRACY_ENABLE + +namespace FSTelemetry +{ + extern bool active; +} + +#endif \ No newline at end of file diff --git a/indra/llcommon/fstracyclient.cpp b/indra/llcommon/fstracyclient.cpp new file mode 100644 index 0000000000..f21c8fd197 --- /dev/null +++ b/indra/llcommon/fstracyclient.cpp @@ -0,0 +1,53 @@ +// +// Tracy profiler +// ---------------- +// +// For fast integration, compile and +// link with this source file (and none +// other) in your executable (or in the +// main DLL / shared object on multi-DLL +// projects). +// + +// Define TRACY_ENABLE to enable profiler. +#include "common/TracySystem.cpp" + +#ifdef TRACY_ENABLE + +#ifdef LL_WINDOWS +# pragma warning(push, 0) +#endif + +#include "common/tracy_lz4.cpp" +#include "client/TracyProfiler.cpp" +#include "client/TracyCallstack.cpp" +#include "client/TracySysTime.cpp" + +#include "client/TracySysTrace.cpp" + +#include "common/TracySocket.cpp" +#include "client/tracy_rpmalloc.cpp" +#include "client/TracyDxt1.cpp" + +#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 +# include "libbacktrace/alloc.cpp" +# include "libbacktrace/dwarf.cpp" +# include "libbacktrace/fileline.cpp" +# include "libbacktrace/mmapio.cpp" +# include "libbacktrace/posix.cpp" +# include "libbacktrace/sort.cpp" +# include "libbacktrace/state.cpp" +# if TRACY_HAS_CALLSTACK == 4 +# include "libbacktrace/macho.cpp" +# else +# include "libbacktrace/elf.cpp" +# endif +#endif + +#ifdef LL_WINDOWS +# pragma comment(lib, "ws2_32.lib") +# pragma comment(lib, "dbghelp.lib") +# pragma warning(pop) +#endif + +#endif \ No newline at end of file diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index 65a937570f..937f1a5501 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -57,6 +57,17 @@ enum ETerrainBrushType E_LANDBRUSH_INVALID = 6 }; +enum EMouseClickType{ + CLICK_NONE = -1, + CLICK_LEFT = 0, + CLICK_MIDDLE, + CLICK_RIGHT, + CLICK_BUTTON4, + CLICK_BUTTON5, + CLICK_DOUBLELEFT, + CLICK_COUNT // 'size', CLICK_NONE does not counts +}; + // keys // Bit masks for various keyboard modifier keys. const MASK MASK_NONE = 0x0000; diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp index ab93713b44..4f6afd17fd 100644 --- a/indra/llcommon/llfasttimer.cpp +++ b/indra/llcommon/llfasttimer.cpp @@ -60,7 +60,6 @@ namespace LLTrace ////////////////////////////////////////////////////////////////////////////// // statics - bool BlockTimer::sLog = false; std::string BlockTimer::sLogName = ""; bool BlockTimer::sMetricLog = false; diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index f3405ea993..2374483c73 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -38,7 +38,27 @@ #define LL_FAST_TIMER_ON 1 #define LL_FASTTIMER_USE_RDTSC 1 -#define LL_RECORD_BLOCK_TIME(timer_stat) const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +// Add Tracy profiler support +/* +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +*/ +#include "fstelemetry.h" +#ifdef TRACY_ENABLE +// #undef TRACY_NO_FASTTIMERS // Uncomment if you want FASTTIMERS as well. +#ifdef TRACY_NO_FASTTIMERS +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +FSZoneN( #timer_stat ); +#else // TRACY_NO_FASTTIMERS +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +FSZoneN( #timer_stat ); \ +const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +#endif // TRACY_NO_FASTTIMERS +#else // TRACY_ENABLE +#define LL_RECORD_BLOCK_TIME(timer_stat) \ +const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__); +#endif // TRACY_ENABLE +// namespace LLTrace { diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp new file mode 100644 index 0000000000..38696c2258 --- /dev/null +++ b/indra/llcommon/llkeybind.cpp @@ -0,0 +1,395 @@ +/** + * @file llkeybind.cpp + * @brief Information about key combinations. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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 "llkeybind.h" + +#include "llsd.h" +#include "llsdutil.h" + +LLKeyData::LLKeyData() + : + mMouse(CLICK_NONE), + mKey(KEY_NONE), + mMask(MASK_NONE), + mIgnoreMasks(false) +{ +} + +LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask) + : + mMouse(mouse), + mKey(key), + mMask(mask), + mIgnoreMasks(false) +{ +} + +LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask) + : + mMouse(mouse), + mKey(key), + mMask(MASK_NONE), + mIgnoreMasks(ignore_mask) +{ +} + +LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask) + : + mMouse(mouse), + mKey(key), + mMask(mask), + mIgnoreMasks(ignore_mask) +{ +} + +LLKeyData::LLKeyData(const LLSD &key_data) +{ + if (key_data.has("mouse")) + { + mMouse = (EMouseClickType)key_data["mouse"].asInteger(); + } + if (key_data.has("key")) + { + mKey = key_data["key"].asInteger(); + } + if (key_data.has("ignore_accelerators")) + { + mIgnoreMasks = key_data["ignore_accelerators"]; + } + if (key_data.has("mask")) + { + mMask = key_data["mask"].asInteger(); + } +} + +LLSD LLKeyData::asLLSD() const +{ + LLSD data; + data["mouse"] = (LLSD::Integer)mMouse; + data["key"] = (LLSD::Integer)mKey; + data["mask"] = (LLSD::Integer)mMask; + if (mIgnoreMasks) + { + data["ignore_accelerators"] = (LLSD::Boolean)mIgnoreMasks; + } + return data; +} + +bool LLKeyData::isEmpty() const +{ + return mMouse == CLICK_NONE && mKey == KEY_NONE; +} + +void LLKeyData::reset() +{ + mMouse = CLICK_NONE; + mKey = KEY_NONE; + mMask = MASK_NONE; + mIgnoreMasks = false; +} + +LLKeyData& LLKeyData::operator=(const LLKeyData& rhs) +{ + mMouse = rhs.mMouse; + mKey = rhs.mKey; + mMask = rhs.mMask; + mIgnoreMasks = rhs.mIgnoreMasks; + return *this; +} + +bool LLKeyData::operator==(const LLKeyData& rhs) +{ + if (mMouse != rhs.mMouse) return false; + if (mKey != rhs.mKey) return false; + if (mMask != rhs.mMask) return false; + if (mIgnoreMasks != rhs.mIgnoreMasks) return false; + return true; +} + +bool LLKeyData::operator!=(const LLKeyData& rhs) +{ + if (mMouse != rhs.mMouse) return true; + if (mKey != rhs.mKey) return true; + if (mMask != rhs.mMask) return true; + if (mIgnoreMasks != rhs.mIgnoreMasks) return true; + return false; +} + +bool LLKeyData::canHandle(const LLKeyData& data) const +{ + if (data.mKey == mKey + && data.mMouse == mMouse + && ((mIgnoreMasks && (data.mMask & mMask) == mMask) || data.mMask == mMask)) + { + return true; + } + return false; +} + +bool LLKeyData::canHandle(EMouseClickType mouse, KEY key, MASK mask) const +{ + if (mouse == mMouse + && key == mKey + && ((mIgnoreMasks && (mask & mMask) == mMask) || mask == mMask)) + { + return true; + } + return false; +} + +// LLKeyBind + +LLKeyBind::LLKeyBind(const LLSD &key_bind) +{ + if (key_bind.isArray()) + { + for (LLSD::array_const_iterator data = key_bind.beginArray(), endLists = key_bind.endArray(); + data != endLists; + data++ + ) + { + mData.push_back(LLKeyData(*data)); + } + } +} + +bool LLKeyBind::operator==(const LLKeyBind& rhs) +{ + U32 size = mData.size(); + if (size != rhs.mData.size()) return false; + + for (U32 i = 0; i < size; i++) + { + if (mData[i] != rhs.mData[i]) return false; + } + + return true; +} + +bool LLKeyBind::operator!=(const LLKeyBind& rhs) +{ + U32 size = mData.size(); + if (size != rhs.mData.size()) return true; + + for (U32 i = 0; i < size; i++) + { + if (mData[i] != rhs.mData[i]) return true; + } + + return false; +} + +bool LLKeyBind::isEmpty() const +{ + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (!iter->isEmpty()) return false; + } + return true; +} + +LLSD LLKeyBind::asLLSD() const +{ + S32 last = mData.size() - 1; + while (mData[last].empty()) + { + last--; + } + + LLSD data; + for (S32 i = 0; i <= last; ++i) + { + // append even if empty to not affect visual representation + data.append(mData[i].asLLSD()); + } + return data; +} + +bool LLKeyBind::canHandle(EMouseClickType mouse, KEY key, MASK mask) const +{ + if (mouse == CLICK_NONE && key == KEY_NONE) + { + // assume placeholder + return false; + } + + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->canHandle(mouse, key, mask)) + { + return true; + } + } + return false; +} + +bool LLKeyBind::canHandleKey(KEY key, MASK mask) const +{ + return canHandle(CLICK_NONE, key, mask); +} + +bool LLKeyBind::canHandleMouse(EMouseClickType mouse, MASK mask) const +{ + return canHandle(mouse, KEY_NONE, mask); +} + +bool LLKeyBind::hasKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const +{ + if (mouse != CLICK_NONE || key != KEY_NONE) + { + for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->mKey == key + && iter->mMask == mask + && iter->mMouse == mouse + && iter->mIgnoreMasks == ignore) + { + return true; + } + } + } + return false; +} + +bool LLKeyBind::hasKeyData(const LLKeyData& data) const +{ + return hasKeyData(data.mMouse, data.mKey, data.mMask, data.mIgnoreMasks); +} + +bool LLKeyBind::hasKeyData(U32 index) const +{ + return mData.size() > index; +} + +S32 LLKeyBind::findKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const +{ + if (mouse != CLICK_NONE || key != KEY_NONE) + { + for (S32 i = 0; i < mData.size(); ++i) + { + if (mData[i].mKey == key + && mData[i].mMask == mask + && mData[i].mMouse == mouse + && mData[i].mIgnoreMasks == ignore) + { + return i; + } + } + } + return -1; +} + +S32 LLKeyBind::findKeyData(const LLKeyData& data) const +{ + return findKeyData(data.mMouse, data.mKey, data.mMask, data.mIgnoreMasks); +} + +LLKeyData LLKeyBind::getKeyData(U32 index) const +{ + if (mData.size() > index) + { + return mData[index]; + } + return LLKeyData(); +} + +bool LLKeyBind::addKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) +{ + if (!hasKeyData(mouse, key, mask, ignore)) + { + mData.push_back(LLKeyData(mouse, key, mask, ignore)); + return true; + } + return false; +} + +bool LLKeyBind::addKeyData(const LLKeyData& data) +{ + if (!hasKeyData(data)) + { + mData.push_back(data); + return true; + } + return false; +} + +void LLKeyBind::replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore, U32 index) +{ + replaceKeyData(LLKeyData(mouse, key, mask, ignore), index); +} + +void LLKeyBind::replaceKeyData(const LLKeyData& data, U32 index) +{ + if (!data.isEmpty()) + { + // if both click and key are none (isEmpty()), we are inserting a placeholder, we don't want to reset anything + // otherwise reset identical key + for (data_vector_t::iterator iter = mData.begin(); iter != mData.end(); iter++) + { + if (iter->mKey == data.mKey + && iter->mMouse == data.mMouse + && iter->mIgnoreMasks == data.mIgnoreMasks + && iter->mMask == data.mMask) + { + // Replacing only fully equal combinations even in case 'ignore' is set + // Reason: Simplicity and user might decide to do a 'move' command as W and Shift+Ctrl+W, and 'run' as Shift+W + iter->reset(); + break; + } + } + } + if (mData.size() <= index) + { + mData.resize(index + 1); + } + mData[index] = data; +} + +void LLKeyBind::resetKeyData(S32 index) +{ + if (mData.size() > index) + { + mData[index].reset(); + } +} + +void LLKeyBind::trimEmpty() +{ + S32 last = mData.size() - 1; + while (last >= 0 && mData[last].empty()) + { + mData.erase(mData.begin() + last); + last--; + } +} + +U32 LLKeyBind::getDataCount() +{ + return mData.size(); +} + diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h new file mode 100644 index 0000000000..c6b4bd970f --- /dev/null +++ b/indra/llcommon/llkeybind.h @@ -0,0 +1,106 @@ +/** + * @file llkeybind.h + * @brief Information about key combinations. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_KEYBIND_H +#define LL_KEYBIND_H + +#include "indra_constants.h" + +// KeyData - single key combination (mouse/mask/keyboard) +class LL_COMMON_API LLKeyData +{ +public: + LLKeyData(); + LLKeyData(EMouseClickType mouse, KEY key, MASK mask); + LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask); + LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask); + LLKeyData(const LLSD &key_data); + + LLSD asLLSD() const; + bool isEmpty() const; + bool empty() const { return isEmpty(); }; + void reset(); + LLKeyData& operator=(const LLKeyData& rhs); + bool operator==(const LLKeyData& rhs); + bool operator!=(const LLKeyData& rhs); + + bool canHandle(const LLKeyData& data) const; + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const; + + EMouseClickType mMouse; + KEY mKey; + MASK mMask; + // Either to expect exact match or ignore not expected masks as long as expected mask-bit is present + bool mIgnoreMasks; +}; + +// One function can bind to multiple Key options +class LLKeyBind +{ +public: + LLKeyBind() {} + LLKeyBind(const LLSD &key_bind); + + bool operator==(const LLKeyBind& rhs); + bool operator!=(const LLKeyBind& rhs); + bool isEmpty() const; + bool empty() const { return isEmpty(); }; + + LLSD asLLSD() const; + + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const; + bool canHandleKey(KEY key, MASK mask) const; + bool canHandleMouse(EMouseClickType mouse, MASK mask) const; + + // contains specified combination + bool hasKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const; + bool hasKeyData(const LLKeyData& data) const; + bool hasKeyData(U32 index) const; + + // index of contained LLKeyData + S32 findKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const; + S32 findKeyData(const LLKeyData& data) const; + + LLKeyData getKeyData(U32 index) const; + + // these methods enshure there will be no repeats + bool addKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore); + bool addKeyData(const LLKeyData& data); + void replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore, U32 index); + void replaceKeyData(const LLKeyData& data, U32 index); + void resetKeyData(S32 index); + void clear() { mData.clear(); } + // if there any empty LLKeyData in the end of the array, remove them + void trimEmpty(); + U32 getDataCount(); + +private: + typedef std::vector data_vector_t; + data_vector_t mData; +}; + + +#endif // LL_KEYBIND_H diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index c952cb2f1a..6d3b7511a5 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -57,6 +57,7 @@ LLFileSystem::~LLFileSystem() // static bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type) { + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -85,6 +86,7 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/) // { + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -102,6 +104,7 @@ bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType fi bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, const LLUUID& new_file_id, const LLAssetType::EType new_file_type) { + FSZoneC(tracy::Color::Gold); // measure cache performance std::string old_id_str; old_file_id.toString(old_id_str); const std::string extra_info = ""; @@ -132,6 +135,7 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp // static S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; file_id.toString(id_str); const std::string extra_info = ""; @@ -157,6 +161,7 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi BOOL LLFileSystem::read(U8* buffer, S32 bytes) { + FSZoneC(tracy::Color::Gold); // measure cache performance BOOL success = TRUE; std::string id; @@ -215,16 +220,19 @@ BOOL LLFileSystem::read(U8* buffer, S32 bytes) S32 LLFileSystem::getLastBytesRead() { + FSZoneC(tracy::Color::Gold); // measure cache performance return mBytesRead; } BOOL LLFileSystem::eof() { + FSZoneC(tracy::Color::Gold); // measure cache performance return mPosition >= getSize(); } BOOL LLFileSystem::write(const U8* buffer, S32 bytes) { + FSZoneC(tracy::Color::Gold); // measure cache performance std::string id_str; mFileID.toString(id_str); const std::string extra_info = ""; @@ -334,6 +342,7 @@ BOOL LLFileSystem::write(const U8* buffer, S32 bytes) BOOL LLFileSystem::seek(S32 offset, S32 origin) { + FSZoneC(tracy::Color::Gold); // measure cache performance if (-1 == origin) { origin = mPosition; @@ -364,22 +373,26 @@ BOOL LLFileSystem::seek(S32 offset, S32 origin) S32 LLFileSystem::tell() const { + FSZoneC(tracy::Color::Gold); // measure cache performance return mPosition; } S32 LLFileSystem::getSize() { + FSZoneC(tracy::Color::Gold); // measure cache performance return LLFileSystem::getFileSize(mFileID, mFileType); } S32 LLFileSystem::getMaxSize() { + FSZoneC(tracy::Color::Gold); // measure cache performance // offer up a huge size since we don't care what the max is return INT_MAX; } BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_type) { + FSZoneC(tracy::Color::Gold); // measure cache performance LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); mFileID = new_id; @@ -390,6 +403,7 @@ BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_typ BOOL LLFileSystem::remove() { + FSZoneC(tracy::Color::Gold); // measure cache performance LLFileSystem::removeFile(mFileID, mFileType); return TRUE; diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index d9449db6ad..300a60c551 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1121,7 +1121,7 @@ void set_default_colour_weights(kdu_params *siz) } // Fix image encoding for KDU >= 8.0.4 -#if KDU_MAJOR_VERSION >= 8 && KDU_MINOR_VERSION >= 0 && KDU_PATCH_VERSION >= 4 +#if (KDU_MAJOR_VERSION*10000 + KDU_MINOR_VERSION*100 + KDU_PATCH_VERSION) >= 80004 cod = siz->access_cluster(ENC_params); assert(cod != NULL); #endif diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index d82189b5df..9de1159009 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -32,7 +32,7 @@ // // KDU core header files // -#ifndef LL_WINDOWS +#ifdef LL_DARWIN #define KDU_NO_THREADS #endif diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h index 44757a4bfc..28164e157e 100644 --- a/indra/llkdu/llkdumem.h +++ b/indra/llkdu/llkdumem.h @@ -28,7 +28,7 @@ #define LL_LLKDUMEM_H // Support classes for reading and writing from memory buffers in KDU -#ifndef LL_WINDOWS +#ifdef LL_DARWIN #define KDU_NO_THREADS #endif diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 952cd085ae..68f83c790c 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -19,6 +19,7 @@ include(Python) include(Tut) include(Python) include(JsonCpp) +include(LLXML) # For accessing settings include_directories (${CMAKE_CURRENT_SOURCE_DIR}) @@ -26,6 +27,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOREHTTP_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} # For accessing settings ${LLMESSAGE_INCLUDE_DIRS} ${LLFILESYSTEM_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} @@ -217,6 +219,7 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} @@ -235,6 +238,7 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} @@ -265,6 +269,7 @@ if (LINUX) ${WINDOWS_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} ${LLCOMMON_LIBRARIES} @@ -281,6 +286,7 @@ else (LINUX) ${WINDOWS_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLXML_LIBRARIES} # For accessing settings ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 795860e9f7..d9f8d49b6d 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -48,8 +48,9 @@ #include #include -#include "../newview/lggcontactsets.h" -#include "../llxml/llcontrol.h" + +#include "llcontrol.h" // Optional legacy name cache expiration + // Time-to-live for a temp cache entry. const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; // Maximum time an unrefreshed cache entry is allowed. @@ -213,13 +214,15 @@ void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD &data, const LLSD &h LLAvatarName av_name; // Contact sets alias - if (LGGContactSets::getInstance()->hasPseudonym(agent_id)) + bool dn_removed; + std::string pseudonym; + if (mCustomNameCheckCallback && mCustomNameCheckCallback(agent_id, dn_removed, pseudonym)) { LLSD info(row); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id) + info["is_display_name_default"] = dn_removed; + info["display_name"] = dn_removed ? (info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) - : LGGContactSets::getInstance()->getPseudonym(agent_id); + : pseudonym; av_name.fromLLSD(info); } else @@ -647,13 +650,15 @@ bool LLAvatarNameCache::getName(const LLUUID& agent_id, LLAvatarName *av_name) { *av_name = it->second; // Contact sets alias - if (LGGContactSets::getInstance()->hasPseudonym(agent_id)) + bool dn_removed; + std::string pseudonym; + if (mCustomNameCheckCallback && mCustomNameCheckCallback(agent_id, dn_removed, pseudonym)) { LLSD info = av_name->asLLSD(); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id) + info["is_display_name_default"] = dn_removed; + info["display_name"] = dn_removed ? (info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) - : LGGContactSets::getInstance()->getPseudonym(agent_id); + : pseudonym; av_name->fromLLSD(info); } // Contact sets alias @@ -711,14 +716,15 @@ LLAvatarNameCache::callback_connection_t LLAvatarNameCache::getNameCallback(cons LLSD test = av_name.asLLSD(); // Contact sets alias - if (LGGContactSets::getInstance()->hasPseudonym(agent_id)) + bool dn_removed; + std::string pseudonym; + if (mCustomNameCheckCallback && mCustomNameCheckCallback(agent_id, dn_removed, pseudonym)) { - LL_DEBUGS("AvNameCache") << "DN cache hit via alias " << agent_id << LL_ENDL; LLSD info = av_name.asLLSD(); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id) + info["is_display_name_default"] = dn_removed; + info["display_name"] = dn_removed ? (info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) - : LGGContactSets::getInstance()->getPseudonym(agent_id); + : pseudonym; av_name.fromLLSD(info); } // Contact sets alias diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 9663981faf..6b7aecad87 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -44,6 +44,14 @@ public: typedef boost::signals2::signal use_display_name_signal_t; typedef boost::function account_name_changed_callback_t; + // Contact sets + typedef boost::function custom_name_check_callback_t; + void setCustomNameCheckCallback(custom_name_check_callback_t cb) + { + mCustomNameCheckCallback = cb; + } + // + // Import/export the name cache to file. bool importFile(std::istream& istr); void exportFile(std::ostream& ostr); @@ -200,6 +208,9 @@ private: // Time when unrefreshed cached names were checked last. F64 mLastExpireCheck; + + // Contact sets + custom_name_check_callback_t mCustomNameCheckCallback; }; // Parse a cache-control header to get the max-age delta-seconds. diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index d774f9276d..548913ecaf 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -446,7 +446,7 @@ void LLModel::generateNormals(F32 angle_cutoff) if (vol_face.mNumIndices > 65535) { - LL_WARNS() << "Too many vertices for normal generation to work." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Too many vertices for normal generation to work." << LL_ENDL; continue; } @@ -1115,7 +1115,7 @@ bool LLModel::loadModel(std::istream& is) { if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024)) { - LL_WARNS() << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; return false; } } @@ -1147,7 +1147,7 @@ bool LLModel::loadModel(std::istream& is) if (header[lod_name[lod]]["offset"].asInteger() == -1 || header[lod_name[lod]]["size"].asInteger() == 0 ) { //cannot load requested LOD - LL_WARNS() << "LoD data is invalid!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "LoD data is invalid!" << LL_ENDL; return false; } @@ -1210,7 +1210,7 @@ bool LLModel::loadModel(std::istream& is) } else { - LL_WARNS() << "unpackVolumeFaces failed!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "unpackVolumeFaces failed!" << LL_ENDL; } return false; @@ -1238,7 +1238,7 @@ bool LLModel::isMaterialListSubset( LLModel* ref ) if (!foundRef) { - LL_INFOS() << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; + LL_INFOS("MESHSKININFO") << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; return false; } } @@ -1274,7 +1274,7 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn bool isASubset = isMaterialListSubset( ref ); if ( !isASubset ) { - LL_INFOS()<<"Material of model is not a subset of reference."<= face.mNumVertices) { - LL_WARNS() << "Face has invalid index." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid index." << LL_ENDL; return false; } } if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0) { - LL_WARNS() << "Face has invalid number of indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has invalid number of indices." << LL_ENDL; return false; } @@ -1900,7 +1908,7 @@ bool validate_model(const LLModel* mdl) { if (mdl->getNumVolumeFaces() == 0) { - LL_WARNS() << "Model has no faces!" << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Model has no faces!" << LL_ENDL; return false; } @@ -1908,13 +1916,13 @@ bool validate_model(const LLModel* mdl) { if (mdl->getVolumeFace(i).mNumVertices == 0) { - LL_WARNS() << "Face has no vertices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no vertices." << LL_ENDL; return false; } if (mdl->getVolumeFace(i).mNumIndices == 0) { - LL_WARNS() << "Face has no indices." << LL_ENDL; + LL_WARNS("MESHSKININFO") << "Face has no indices." << LL_ENDL; return false; } diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index fd6787a660..e113c08eae 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -1100,6 +1100,13 @@ LLFontGL* LLFontGL::getFontOCRA() static LLFontGL* fontp = getFont(LLFontDescriptor("OCRA","Monospace",0)); return fontp; } + +//static +LLFontGL* LLFontGL::getFontCascadia() +{ + static LLFontGL* fontp = getFont(LLFontDescriptor("Cascadia", "Cascadia", 0)); + return fontp; +} // //static @@ -1152,6 +1159,10 @@ LLFontGL* LLFontGL::getFontByName(const std::string& name) { return getFontMonospace(); } + else if (name == "Cascadia") + { + return getFontCascadia(); + } // else { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index acc922b752..5b03fc67b1 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -193,6 +193,7 @@ public: // Advanced script editor static LLFontGL* getFontScripting(); static LLFontGL* getFontOCRA(); + static LLFontGL* getFontCascadia(); // static LLFontGL* getFontExtChar(); static LLFontGL* getFont(const LLFontDescriptor& desc); diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index a8cdce95e9..18f4fc4271 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -169,6 +169,8 @@ LLFontDescriptor LLFontDescriptor::normalize() const // Advanced script editor if (new_size != s_template_string && new_size.empty() && findSubString(new_name,"Scripting")) new_size = "Scripting"; + if (new_size != s_template_string && new_size.empty() && findSubString(new_name, "Cascadia")) + new_size = "Cascadia"; // if (new_size.empty()) new_size = "Medium"; diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp index 727fbe850e..4b057fa62c 100644 --- a/indra/llui/llcontainerview.cpp +++ b/indra/llui/llcontainerview.cpp @@ -47,6 +47,10 @@ LLContainerView::LLContainerView(const LLContainerView::Params& p) : LLView(p), mShowLabel(p.show_label), mLabel(p.label), + // Add background visible flag and color to container_view so we can have blank scrollview containers + mBackgroundVisible(p.background_visible), + mBackgroundColor(p.bg_color), + // mDisplayChildren(p.display_children) { mScrollContainer = NULL; @@ -111,10 +115,14 @@ BOOL LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask) void LLContainerView::draw() { + // Add background visible flag and color to container_view so we can have blank scrollview containers + if (mBackgroundVisible) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); + // Add background visible flag and color to container_view so we can have blank scrollview containers + // gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor); } // Draw the label diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h index 99267d978a..48ec36a861 100644 --- a/indra/llui/llcontainerview.h +++ b/indra/llui/llcontainerview.h @@ -47,9 +47,17 @@ public: Optional label; Optional show_label; Optional display_children; + // Add background visible flag and color to container_view so we can have blank scrollview containers + Optional background_visible; + Optional bg_color; + // Params() : label("label"), show_label("show_label", FALSE), + // Add background visible flag and color to container_view so we can have blank scrollview containers + background_visible("background_visible", true), + bg_color("bg_color", LLColor4(0.f, 0.f, 0.f, 0.25f)), + // display_children("display_children", TRUE) { changeDefault(mouse_opaque, false); @@ -90,5 +98,9 @@ public: protected: BOOL mDisplayChildren; std::string mLabel; + // Add background visible flag and color to container_view so we can have blank scrollview containers + bool mBackgroundVisible; + LLUIColor mBackgroundColor; + // }; #endif // LL_CONTAINERVIEW_ diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 2252f5f5dd..5cd3be4d69 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -223,6 +223,12 @@ LLSD LLMenuItemGL::getValue() const return getLabel(); } +//virtual +bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + return (mAcceleratorKey == key) && (mAcceleratorMask == mask); +} + //virtual BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) { @@ -284,13 +290,13 @@ BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list -BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) +BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) { - LLKeyBinding *accelerator = NULL; + LLMenuKeyboardBinding *accelerator = NULL; if (mAcceleratorKey != KEY_NONE) { - std::list::iterator list_it; + std::list::iterator list_it; for (list_it = listp->begin(); list_it != listp->end(); ++list_it) { accelerator = *list_it; @@ -314,7 +320,7 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list *listp) } if (!accelerator) { - accelerator = new LLKeyBinding; + accelerator = new LLMenuKeyboardBinding; if (accelerator) { accelerator->mKey = mAcceleratorKey; @@ -1038,6 +1044,11 @@ BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask) return TRUE; } +bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + return getBranch() && getBranch()->hasAccelerator(key, mask); +} + BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) { return getBranch() && getBranch()->handleAcceleratorKey(key, mask); @@ -1045,7 +1056,7 @@ BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask) // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list -BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) +BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list *listp) { LLMenuGL* branch = getBranch(); if (!branch) @@ -3050,6 +3061,27 @@ void LLMenuGL::updateParent(LLView* parentp) } } +bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const +{ + if (key == KEY_NONE) + { + return false; + } + // Note: checking this way because mAccelerators seems to be broken + // mAccelerators probably needs to be cleaned up or fixed + // It was used for dupplicate accelerator avoidance. + item_list_t::const_iterator item_iter; + for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) + { + LLMenuItemGL* itemp = *item_iter; + if (itemp->hasAccelerator(key, mask)) + { + return true; + } + } + return false; +} + BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask) { // don't handle if not enabled diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index e47dfd7523..8e805328b2 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -42,6 +42,13 @@ extern S32 MENU_BAR_HEIGHT; extern S32 MENU_BAR_WIDTH; +class LLMenuKeyboardBinding +{ +public: + KEY mKey; + MASK mMask; +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLMenuItemGL // @@ -92,6 +99,7 @@ public: /*virtual*/ void setValue(const LLSD& value); /*virtual*/ LLSD getValue() const; + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); } @@ -110,7 +118,7 @@ public: virtual void setBriefItem(BOOL brief); virtual BOOL isBriefItem() const; - virtual BOOL addToAcceleratorList(std::list *listp); + virtual BOOL addToAcceleratorList(std::list *listp); void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; } BOOL getAllowKeyRepeat() const { return mAllowKeyRepeat; } @@ -437,7 +445,8 @@ public: /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); /*virtual*/ void removeChild( LLView* ctrl); /*virtual*/ BOOL postBuild(); - + + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); LLMenuGL* findChildMenuByName(const std::string& name, BOOL recurse) const; @@ -633,10 +642,11 @@ public: virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual bool hasAccelerator(const KEY &key, const MASK &mask) const; virtual BOOL handleAcceleratorKey(KEY key, MASK mask); // check if we've used these accelerators already - virtual BOOL addToAcceleratorList(std::list *listp); + virtual BOOL addToAcceleratorList(std::list *listp); // called to rebuild the draw label virtual void buildDrawLabel( void ); @@ -809,7 +819,7 @@ private: void checkMenuTrigger(); - std::list mAccelerators; + std::list mAccelerators; BOOL mAltKeyTrigger; }; diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index 8000efad0e..13839da400 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -50,6 +50,10 @@ LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_ { cell = new LLScrollListDate(cell_p); } + else if (cell_p.type() == "icontext") + { + cell = new LLScrollListIconText(cell_p); + } else // default is "text" { cell = new LLScrollListText(cell_p); @@ -168,7 +172,7 @@ U32 LLScrollListText::sCount = 0; LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) : LLScrollListCell(p), - mText(p.value().asString()), + mText(p.label.isProvided() ? p.label() : p.value().asString()), mFont(p.font), mColor(p.color), mUseColor(p.color.isProvided()), @@ -192,7 +196,7 @@ LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p) void LLScrollListText::highlightText(S32 offset, S32 num_chars) { mHighlightOffset = offset; - mHighlightCount = num_chars; + mHighlightCount = llmax(0, num_chars); } //virtual @@ -292,11 +296,12 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col if (mHighlightCount > 0) { + // Highlight text S32 left = 0; switch(mFontAlignment) { case LLFontGL::LEFT: - left = mFont->getWidth(mText.getString(), 0, mHighlightOffset); + left = mFont->getWidth(mText.getString(), 1, mHighlightOffset); break; case LLFontGL::RIGHT: left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX); @@ -319,7 +324,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col switch(mFontAlignment) { case LLFontGL::LEFT: - start_x = 0.f; + start_x = 1.f; break; case LLFontGL::RIGHT: start_x = (F32)getWidth(); @@ -435,3 +440,139 @@ const LLSD LLScrollListDate::getValue() const { return mDate; } + +// +// LLScrollListIconText +// +LLScrollListIconText::LLScrollListIconText(const LLScrollListCell::Params& p) + : LLScrollListText(p), + mIcon(p.value().isUUID() ? LLUI::getUIImageByID(p.value().asUUID()) : LLUI::getUIImage(p.value().asString())), + mPad(4) +{ + mTextWidth = getWidth() - mPad /*padding*/ - mFont->getLineHeight(); +} + +LLScrollListIconText::~LLScrollListIconText() +{ +} + +const LLSD LLScrollListIconText::getValue() const +{ + if (mIcon.isNull()) + { + return LLStringUtil::null; + } + return mIcon->getName(); +} + +void LLScrollListIconText::setValue(const LLSD& value) +{ + if (value.isUUID()) + { + // don't use default image specified by LLUUID::null, use no image in that case + LLUUID image_id = value.asUUID(); + mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL); + } + else + { + std::string value_string = value.asString(); + if (LLUUID::validate(value_string)) + { + setValue(LLUUID(value_string)); + } + else if (!value_string.empty()) + { + mIcon = LLUI::getUIImage(value.asString()); + } + else + { + mIcon = NULL; + } + } +} + +void LLScrollListIconText::setWidth(S32 width) +{ + LLScrollListCell::setWidth(width); + // Assume that iamge height and width is identical to font height and width + mTextWidth = width - mPad /*padding*/ - mFont->getLineHeight(); +} + + +void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight_color) const +{ + LLColor4 display_color; + if (mUseColor) + { + display_color = mColor; + } + else + { + display_color = color; + } + + S32 icon_height = mFont->getLineHeight(); + S32 icon_space = mIcon ? (icon_height + mPad) : 0; + + if (mHighlightCount > 0) + { + S32 left = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + left = mFont->getWidth(mText.getString(), icon_space + 1, mHighlightOffset); + break; + case LLFontGL::RIGHT: + left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX) - icon_space; + break; + case LLFontGL::HCENTER: + left = (getWidth() - mFont->getWidth(mText.getString()) - icon_space) / 2; + break; + } + LLRect highlight_rect(left - 2, + mFont->getLineHeight() + 1, + left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, + 1); + mRoundedRectImage->draw(highlight_rect, highlight_color); + } + + // Try to draw the entire string + F32 right_x; + U32 string_chars = mText.length(); + F32 start_text_x = 0.f; + S32 start_icon_x = 0; + switch (mFontAlignment) + { + case LLFontGL::LEFT: + start_text_x = icon_space + 1; + start_icon_x = 1; + break; + case LLFontGL::RIGHT: + start_text_x = (F32)getWidth(); + start_icon_x = getWidth() - mFont->getWidth(mText.getString()) - icon_space; + break; + case LLFontGL::HCENTER: + F32 center = (F32)getWidth()* 0.5f; + start_text_x = center + ((F32)icon_space * 0.5f); + start_icon_x = center - (((F32)icon_space + mFont->getWidth(mText.getString())) * 0.5f); + break; + } + mFont->render(mText.getWString(), 0, + start_text_x, 0.f, + display_color, + mFontAlignment, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + string_chars, + getTextWidth(), + &right_x, + TRUE); + + if (mIcon) + { + mIcon->draw(start_icon_x, 0, icon_height, icon_height, mColor); + } +} + + diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h index d625ebddcc..19576fb247 100644 --- a/indra/llui/llscrolllistcell.h +++ b/indra/llui/llscrolllistcell.h @@ -59,7 +59,8 @@ public: visible; Optional userdata; - Optional value; + Optional value; // state of checkbox, icon id/name, date + Optional label; // description or text Optional tool_tip; Optional font; @@ -75,6 +76,7 @@ public: enabled("enabled", true), visible("visible", true), value("value"), + label("label"), tool_tip("tool_tip", ""), font("font", LLFontGL::getFontSansSerifSmall()), font_color("font_color", LLColor4::black), @@ -152,11 +154,12 @@ public: void setText(const LLStringExplicit& text); void setFontStyle(const U8 font_style); -private: +protected: LLUIString mText; S32 mTextWidth; const LLFontGL* mFont; LLColor4 mColor; + LLColor4 mHighlightColor; U8 mUseColor; LLFontGL::HAlign mFontAlignment; BOOL mVisible; @@ -169,7 +172,7 @@ private: }; /* - * Cell displaying an image. + * Cell displaying an image. AT the moment, this is specifically UI image */ class LLScrollListIcon : public LLScrollListCell { @@ -223,4 +226,26 @@ private: LLDate mDate; }; +/* +* Cell displaying icon and text. +*/ + +class LLScrollListIconText : public LLScrollListText +{ +public: + LLScrollListIconText(const LLScrollListCell::Params& p); + /*virtual*/ ~LLScrollListIconText(); + /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const; + /*virtual*/ const LLSD getValue() const; + /*virtual*/ void setValue(const LLSD& value); + + + S32 getIconWidth() const; + /*virtual*/ void setWidth(S32 width);/* { LLScrollListCell::setWidth(width); mTextWidth = width - ; }*/ + +private: + LLPointer mIcon; + S32 mPad; +}; + #endif diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 9cec673674..71b5340d11 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -117,6 +117,13 @@ struct SortScrollListItem // LLScrollListCtrl //--------------------------------------------------------------------------- +void LLScrollListCtrl::SelectionTypeNames::declareValues() +{ + declare("row", LLScrollListCtrl::ROW); + declare("cell", LLScrollListCtrl::CELL); + declare("header", LLScrollListCtrl::HEADER); +} + LLScrollListCtrl::Contents::Contents() : columns("column"), rows("row") @@ -130,9 +137,11 @@ LLScrollListCtrl::Params::Params() has_border("draw_border"), draw_heading("draw_heading"), search_column("search_column", 0), + selection_type("selection_type", ROW), sort_column("sort_column", -1), sort_ascending("sort_ascending", true), sort_lazily("sort_lazily", false), // FIRE-30732 deferred sort as a UI property + can_sort("can_sort", true), persist_sort_order("persist_sort_order", false), // Persists sort order of scroll lists primary_sort_only("primary_sort_only", false), // Option to only sort by one column mouse_wheel_opaque("mouse_wheel_opaque", false), @@ -170,8 +179,10 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) mCommitOnKeyboardMovement(p.commit_on_keyboard_movement), mCommitOnSelectionChange(p.commit_on_selection_change), mSelectionChanged(false), + mSelectionType(p.selection_type), mNeedsScroll(false), mCanSelect(true), + mCanSort(p.can_sort), mColumnsDirty(false), mMaxItemCount(INT_MAX), mBorderThickness( 2 ), @@ -932,7 +943,15 @@ BOOL LLScrollListCtrl::selectFirstItem() { if (!itemp->getSelected()) { - selectItem(itemp); + switch (mSelectionType) + { + case CELL: + selectItem(itemp, 0); + break; + case HEADER: + case ROW: + selectItem(itemp, -1); + } } success = TRUE; mOriginalSelection = 0; @@ -1001,7 +1020,8 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) { if( itemp->getEnabled() ) { - selectItem(itemp, FALSE); + // TODO: support range selection for cells + selectItem(itemp, -1, FALSE); success = TRUE; } } @@ -1127,10 +1147,14 @@ void LLScrollListCtrl::clearHighlightedItems() void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index) { - if (mHighlightedItem != target_index) - { - mHighlightedItem = target_index; - } + if (mHighlightedItem != target_index) + { + if (mHighlightedItem >= 0 && mHighlightedItem < mItemList.size()) + { + mItemList[mHighlightedItem]->setHoverCell(-1); + } + mHighlightedItem = target_index; + } } S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) @@ -1145,7 +1169,8 @@ S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids ) { if (item->getEnabled() && (item->getUUID() == (*iditr))) { - selectItem(item,FALSE); + // TODO: support multiple selection for cells + selectItem(item, -1, FALSE); ++count; break; } @@ -1224,7 +1249,7 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) { if (prev_item) { - selectItem(prev_item, !extend_selection); + selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection); } else { @@ -1268,7 +1293,7 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection) { if (next_item) { - selectItem(next_item, !extend_selection); + selectItem(next_item, cur_item->getSelectedCell(), !extend_selection); } else { @@ -1347,7 +1372,7 @@ BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sen bool found = NULL != item; if(found) { - selectItem(item); + selectItem(item, -1); } if (mCommitOnSelectionChange) @@ -1422,7 +1447,7 @@ BOOL LLScrollListCtrl::selectItemByStringMatch(const LLWString& target, bool pre BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE; if (select) { - selectItem(item); + selectItem(item, -1); found = TRUE; break; } @@ -1473,7 +1498,7 @@ BOOL LLScrollListCtrl::selectItemByStringMatch(const LLWString& target, bool pre // find offset of matching text (might have leading whitespace) S32 offset = item_label.find(target_trimmed); cellp->highlightText(offset, target_trimmed.size()); - selectItem(item); + selectItem(item, -1); found = TRUE; break; } @@ -1568,7 +1593,7 @@ BOOL LLScrollListCtrl::setSelectedByValue(const LLSD& value, BOOL selected) { if (selected) { - selectItem(item); + selectItem(item, -1); } else { @@ -1650,7 +1675,7 @@ void LLScrollListCtrl::drawItems() S32 max_columns = 0; - LLColor4 highlight_color = LLColor4::white; + LLColor4 highlight_color = LLColor4::white; // ex: text inside cells static LLUICachedControl type_ahead_timeout ("TypeAheadTimeout", 0); highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout(), 0.4f, 0.f); @@ -1702,7 +1727,8 @@ void LLScrollListCtrl::drawItems() max_columns = llmax(max_columns, item->getNumColumns()); LLColor4 fg_color; - LLColor4 bg_color(LLColor4::transparent); + LLColor4 hover_color(LLColor4::transparent); + LLColor4 select_color(LLColor4::transparent); if( mScrollLines <= line && line < mScrollLines + num_page_lines ) { @@ -1711,45 +1737,45 @@ void LLScrollListCtrl::drawItems() { if(item->getHighlighted()) // if it's highlighted, average the colors { - bg_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f); + select_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f); } else // otherwise just select-highlight it { - bg_color = mBgSelectedColor.get(); + select_color = mBgSelectedColor.get(); } fg_color = (item->getEnabled() ? mFgSelectedColor.get() : mFgDisabledColor.get()); } - else if (mHighlightedItem == line && mCanSelect) + if (mHighlightedItem == line && mCanSelect) { if(item->getHighlighted()) // if it's highlighted, average the colors { - bg_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f); + hover_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f); } else // otherwise just hover-highlight it { - bg_color = mHoveredColor.get(); + hover_color = mHoveredColor.get(); } } else if (item->getHighlighted()) { - bg_color = mHighlightedColor.get(); + hover_color = mHighlightedColor.get(); } else { // Why no stripes in single columns? This should be decided by the skin. -Zi if (mDrawStripes && (line % 2 == 0)) // && (max_columns > 1)) { - bg_color = mBgStripeColor.get(); + hover_color = mBgStripeColor.get(); } } if (!item->getEnabled()) { - bg_color = mBgReadOnlyColor.get(); + hover_color = mBgReadOnlyColor.get(); } - item->draw(item_rect, fg_color % alpha, bg_color% alpha, highlight_color % alpha, mColumnPadding); + item->draw(item_rect, fg_color % alpha, hover_color% alpha, select_color% alpha, highlight_color % alpha, mColumnPadding); cur_y -= mLineHeight; } @@ -1925,7 +1951,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if (mLastSelected == NULL) { - selectItem(hit_item); + selectItem(hit_item, getColumnIndexFromOffset(x)); } else { @@ -1955,7 +1981,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) // Fix for FS-specific people list (radar) if (item == hit_item || item == lastSelected) { - selectItem(item, FALSE); + selectItem(item, getColumnIndexFromOffset(x), FALSE); selecting = !selecting; if (hit_item == lastSelected) { @@ -1965,7 +1991,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) } if (selecting) { - selectItem(item, FALSE); + selectItem(item, getColumnIndexFromOffset(x), FALSE); } } } @@ -1980,7 +2006,7 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) { if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)) { - selectItem(hit_item, FALSE); + selectItem(hit_item, getColumnIndexFromOffset(x), FALSE); } else { @@ -1994,12 +2020,12 @@ BOOL LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask) else { deselectAllItems(TRUE); - selectItem(hit_item); + selectItem(hit_item, getColumnIndexFromOffset(x)); } } else { - selectItem(hit_item); + selectItem(hit_item, getColumnIndexFromOffset(x)); } selection_changed = mSelectionChanged; @@ -2420,8 +2446,29 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask) { LLScrollListItem* item = hitItem(x, y); if (item) - { - mouseOverHighlightNthItem(getItemIndex(item)); + { + mouseOverHighlightNthItem(getItemIndex(item)); + switch (mSelectionType) + { + case CELL: + item->setHoverCell(getColumnIndexFromOffset(x)); + break; + case HEADER: + { + S32 cell = getColumnIndexFromOffset(x); + if (cell > 0) + { + item->setHoverCell(cell); + } + else + { + item->setHoverCell(-1); + } + break; + } + case ROW: + break; + } } else { @@ -2469,6 +2516,58 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask ) handled = TRUE; } break; + case KEY_LEFT: + if (mAllowKeyboardMovement || hasFocus()) + { + // TODO: support multi-select + LLScrollListItem *item = getFirstSelected(); + if (item) + { + S32 cell = item->getSelectedCell(); + switch (mSelectionType) + { + case CELL: + if (cell < mColumns.size()) cell++; + break; + case HEADER: + if (cell == -1) cell = 1; + else if (cell > 1 && cell < mColumns.size()) cell++; // skip header + break; + case ROW: + cell = -1; + break; + } + item->setSelectedCell(cell); + handled = TRUE; + } + } + break; + case KEY_RIGHT: + if (mAllowKeyboardMovement || hasFocus()) + { + // TODO: support multi-select + LLScrollListItem *item = getFirstSelected(); + if (item) + { + S32 cell = item->getSelectedCell(); + switch (mSelectionType) + { + case CELL: + if (cell >= 0) cell--; + break; + case HEADER: + if (cell > 1) cell--; + else if (cell == 1) cell = -1; // skip header + break; + case ROW: + cell = -1; + break; + } + item->setSelectedCell(cell); + handled = TRUE; + } + } + break; case KEY_PAGE_UP: if (mAllowKeyboardMovement || hasFocus()) { @@ -2777,7 +2876,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char) LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char) { - selectItem(item); + selectItem(item, -1); mNeedsScroll = true; cellp->highlightText(0, 1); mSearchTimer.reset(); @@ -2829,7 +2928,7 @@ BOOL LLScrollListCtrl::isRepeatedChars(const LLWString& string) const return TRUE; } -void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_item) +void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, BOOL select_single_item) { if (!itemp) return; @@ -2848,6 +2947,18 @@ void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_it deselectAllItems(TRUE); } itemp->setSelected(TRUE); + switch (mSelectionType) + { + case CELL: + itemp->setSelectedCell(cell); + break; + case HEADER: + itemp->setSelectedCell(cell <= 0 ? -1 : cell); + break; + case ROW: + itemp->setSelectedCell(-1); + break; + } mLastSelected = itemp; mSelectionChanged = true; } @@ -3125,7 +3236,7 @@ void LLScrollListCtrl::selectAll() LLScrollListItem *itemp = *iter; if( itemp->getEnabled() ) { - selectItem(itemp, FALSE); + selectItem(itemp, -1, FALSE); } } @@ -3302,6 +3413,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata) LLScrollListCtrl *parent = info->mParentCtrl; if (!parent) return; + if (!parent->mCanSort) return; + S32 column_index = info->mIndex; LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex]; @@ -3666,7 +3779,7 @@ void LLScrollListCtrl::setFilterString(const std::string& str) { if (!isFiltered(*iter)) { - selectItem(*iter); + selectItem(*iter, -1); break; } } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 9f8c93b6ad..af00f56db5 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -54,6 +54,18 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler, public LLCtrlListInterface, public LLCtrlScrollInterface { public: + typedef enum e_selection_type + { + ROW, // default + CELL, // does not support multi-selection + HEADER, // when pointing to cells in column 0 will highlight whole row, otherwise cell, no multi-select + } ESelectionType; + + struct SelectionTypeNames : public LLInitParam::TypeValuesHelper + { + static void declareValues(); + }; + struct Contents : public LLInitParam::Block { Multiple columns; @@ -100,6 +112,8 @@ public: commit_on_selection_change, mouse_wheel_opaque; + Optional selection_type; + // display flags Optional has_border, draw_heading, @@ -116,7 +130,8 @@ public: // sort and search behavior Optional search_column, sort_column; - Optional sort_ascending; + Optional sort_ascending, + can_sort; // whether user is allowed to sort Optional sort_lazily; // FIRE-30732 deferred sort as a UI property Optional persist_sort_order; // Persists sort order of scroll lists Optional primary_sort_only; // Option to only sort by one column @@ -466,7 +481,7 @@ private: void updateLineHeightInsert(LLScrollListItem* item); void reportInvalidInput(); BOOL isRepeatedChars(const LLWString& string) const; - void selectItem(LLScrollListItem* itemp, BOOL single_select = TRUE); + void selectItem(LLScrollListItem* itemp, S32 cell, BOOL single_select = TRUE); void deselectItem(LLScrollListItem* itemp); void commitIfChanged(); BOOL setSort(S32 column, BOOL ascending); @@ -495,9 +510,11 @@ private: bool mCommitOnKeyboardMovement; bool mCommitOnSelectionChange; bool mSelectionChanged; + ESelectionType mSelectionType; bool mNeedsScroll; bool mMouseWheelOpaque; bool mCanSelect; + bool mCanSort; // Whether user is allowed to sort bool mDisplayColumnHeaders; bool mColumnsDirty; bool mColumnWidthsDirty; diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp index df22c88afb..51c615dd00 100644 --- a/indra/llui/llscrolllistitem.cpp +++ b/indra/llui/llscrolllistitem.cpp @@ -40,6 +40,8 @@ LLScrollListItem::LLScrollListItem( const Params& p ) : mSelected(FALSE), mHighlighted(FALSE), + mHoverIndex(-1), + mSelectedIndex(-1), mEnabled(p.enabled), mUserdata(p.userdata), mItemValue(p.value) @@ -53,6 +55,28 @@ LLScrollListItem::~LLScrollListItem() mColumns.clear(); } +void LLScrollListItem::setSelected(BOOL b) +{ + mSelected = b; + mSelectedIndex = -1; +} + +void LLScrollListItem::setHighlighted(BOOL b) +{ + mHighlighted = b; + mHoverIndex = -1; +} + +void LLScrollListItem::setHoverCell(S32 cell) +{ + mHoverIndex = cell; +} + +void LLScrollListItem::setSelectedCell(S32 cell) +{ + mSelectedIndex = cell; +} + void LLScrollListItem::addColumn(const LLScrollListCell::Params& p) { mColumns.push_back(LLScrollListCell::create(p)); @@ -120,12 +144,21 @@ std::string LLScrollListItem::getContentsCSV() const } -void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding) +void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& hover_color, const LLColor4& select_color, const LLColor4& highlight_color, S32 column_padding) { // draw background rect gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLRect bg_rect = rect; - gl_rect_2d( bg_rect, bg_color ); + if (mSelectedIndex < 0 && getSelected()) + { + // Whole item is highlighted/selected + gl_rect_2d(bg_rect, select_color); + } + else if (mHoverIndex < 0) + { + // Whole item is highlighted/selected + gl_rect_2d(bg_rect, hover_color); + } S32 cur_x = rect.mLeft; S32 num_cols = getNumColumns(); @@ -141,6 +174,25 @@ void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const { LLUI::translate((F32) cur_x, (F32) rect.mBottom); + if (mSelectedIndex == cur_col) + { + // select specific cell + LLRect highlight_rect(0, + cell->getHeight(), + cell->getWidth(), + 0); + gl_rect_2d(highlight_rect, select_color); + } + else if (mHoverIndex == cur_col) + { + // highlight specific cell + LLRect highlight_rect(0, + cell->getHeight(), + cell->getWidth() , + 0); + gl_rect_2d(highlight_rect, hover_color); + } + cell->draw( fg_color, highlight_color ); } LLUI::popMatrix(); diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h index 13655b5873..d2c3dd7721 100644 --- a/indra/llui/llscrolllistitem.h +++ b/indra/llui/llscrolllistitem.h @@ -77,15 +77,21 @@ public: virtual ~LLScrollListItem(); - void setSelected( BOOL b ) { mSelected = b; } + void setSelected( BOOL b ); BOOL getSelected() const { return mSelected; } void setEnabled( BOOL b ) { mEnabled = b; } BOOL getEnabled() const { return mEnabled; } - void setHighlighted( BOOL b ) { mHighlighted = b; } + void setHighlighted( BOOL b ); BOOL getHighlighted() const { return mHighlighted; } + void setSelectedCell( S32 cell ); + S32 getSelectedCell() const { return mSelectedIndex; } + + void setHoverCell( S32 cell ); + S32 getHoverCell() const { return mHoverIndex; } + void setUserdata( void* userdata ) { mUserdata = userdata; } void* getUserdata() const { return mUserdata; } @@ -107,14 +113,21 @@ public: std::string getContentsCSV() const; - virtual void draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding); + virtual void draw(const LLRect& rect, + const LLColor4& fg_color, + const LLColor4& hover_color, // highlight/hover selection of whole item or cell + const LLColor4& select_color, // highlight/hover selection of whole item or cell + const LLColor4& highlight_color, // highlights contents of cells (ex: text) + S32 column_padding); protected: LLScrollListItem( const Params& ); private: BOOL mSelected; - BOOL mHighlighted; + BOOL mHighlighted; + S32 mHoverIndex; + S32 mSelectedIndex; BOOL mEnabled; void* mUserdata; LLSD mItemValue; diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index f6f6c3931c..5404ac50e5 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -327,7 +327,7 @@ BOOL LLKeyboard::keyFromString(const std::string& str, KEY *key) // static -std::string LLKeyboard::stringFromKey(KEY key) +std::string LLKeyboard::stringFromKey(KEY key, bool translate) { std::string res = get_if_there(sKeysToNames, key, std::string()); if (res.empty()) @@ -338,16 +338,60 @@ std::string LLKeyboard::stringFromKey(KEY key) res = std::string(buffer); } - LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; - if (trans != NULL) + if (translate) { - res = trans(res.c_str()); + LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; + if (trans != NULL) + { + res = trans(res.c_str()); + } } return res; } +//static +std::string LLKeyboard::stringFromAccelerator(MASK accel_mask) +{ + std::string res; + LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; + + if (trans == NULL) + { + LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL; + return res; + } + + // Append any masks +#ifdef LL_DARWIN + // Standard Mac names for modifier keys in menu equivalents + // We could use the symbol characters, but they only exist in certain fonts. + if (accel_mask & MASK_CONTROL) + { + if (accel_mask & MASK_MAC_CONTROL) + { + res.append(trans("accel-mac-control")); + } + else + { + res.append(trans("accel-mac-command")); // Symbol would be "\xE2\x8C\x98" + } + } + if (accel_mask & MASK_ALT) + res.append(trans("accel-mac-option")); // Symbol would be "\xE2\x8C\xA5" + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-mac-shift")); // Symbol would be "\xE2\x8C\xA7" +#else + if (accel_mask & MASK_CONTROL) + res.append(trans("accel-win-control")); + if (accel_mask & MASK_ALT) + res.append(trans("accel-win-alt")); + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-win-shift")); +#endif + return res; +} //static std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key ) { @@ -359,41 +403,7 @@ std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key ) return res; } - LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; - - if( trans == NULL ) - { - LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL; - return res; - } - - // Append any masks -#ifdef LL_DARWIN - // Standard Mac names for modifier keys in menu equivalents - // We could use the symbol characters, but they only exist in certain fonts. - if( accel_mask & MASK_CONTROL ) - { - if ( accel_mask & MASK_MAC_CONTROL ) - { - res.append( trans("accel-mac-control") ); - } - else - { - res.append( trans("accel-mac-command") ); // Symbol would be "\xE2\x8C\x98" - } - } - if( accel_mask & MASK_ALT ) - res.append( trans("accel-mac-option") ); // Symbol would be "\xE2\x8C\xA5" - if( accel_mask & MASK_SHIFT ) - res.append( trans("accel-mac-shift") ); // Symbol would be "\xE2\x8C\xA7" -#else - if( accel_mask & MASK_CONTROL ) - res.append( trans("accel-win-control") ); - if( accel_mask & MASK_ALT ) - res.append( trans("accel-win-alt") ); - if( accel_mask & MASK_SHIFT ) - res.append( trans("accel-win-shift") ); -#endif + res.append(stringFromAccelerator(accel_mask)); std::string key_string = LLKeyboard::stringFromKey(key); if ((accel_mask & MASK_NORMALKEYS) && (key_string[0] == '-' || key_string[0] == '=' || key_string[0] == '+')) diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index 6f2dc87317..36bd8bcbed 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -38,10 +38,10 @@ enum EKeystate { KEYSTATE_DOWN, KEYSTATE_LEVEL, - KEYSTATE_UP + KEYSTATE_UP }; -typedef boost::function LLKeyFunc; +typedef boost::function LLKeyFunc; typedef std::string (LLKeyStringTranslatorFunc)(const char *label); enum EKeyboardInsertMode @@ -50,15 +50,6 @@ enum EKeyboardInsertMode LL_KIM_OVERWRITE }; -class LLKeyBinding -{ -public: - KEY mKey; - MASK mMask; -// const char *mName; // unused - LLKeyFunc mFunction; -}; - class LLWindowCallbacks; class LLKeyboard @@ -103,7 +94,8 @@ public: static BOOL maskFromString(const std::string& str, MASK *mask); // False on failure static BOOL keyFromString(const std::string& str, KEY *key); // False on failure - static std::string stringFromKey(KEY key); + static std::string stringFromKey(KEY key, bool translate = true); + static std::string stringFromAccelerator( MASK accel_mask ); // separated for convinience, returns with "+": "Shift+" or "Shift+Alt+"... static std::string stringFromAccelerator( MASK accel_mask, KEY key ); void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; } diff --git a/indra/llwindow/llmousehandler.cpp b/indra/llwindow/llmousehandler.cpp index d5fa65fe4b..e41ebd42f3 100644 --- a/indra/llwindow/llmousehandler.cpp +++ b/indra/llwindow/llmousehandler.cpp @@ -27,7 +27,7 @@ #include "llmousehandler.h" //virtual -BOOL LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +BOOL LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL handled = FALSE; if (down) diff --git a/indra/llwindow/llmousehandler.h b/indra/llwindow/llmousehandler.h index 1dcd0348d8..d221dd117c 100644 --- a/indra/llwindow/llmousehandler.h +++ b/indra/llwindow/llmousehandler.h @@ -29,6 +29,7 @@ #include "linden_common.h" #include "llrect.h" +#include "indra_constants.h" // Mostly-abstract interface. // Intended for use via multiple inheritance. @@ -46,16 +47,7 @@ public: SHOW_ALWAYS, } EShowToolTip; - typedef enum { - CLICK_LEFT, - CLICK_MIDDLE, - CLICK_RIGHT, - CLICK_BUTTON4, - CLICK_BUTTON5, - CLICK_DOUBLELEFT - } EClickType; - - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0; virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) = 0; virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask) = 0; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e2568e5d5b..c77c14f31e 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -250,7 +250,7 @@ set(viewer_SOURCE_FILES llblockedlistitem.cpp llblocklist.cpp llbox.cpp - llbreadcrumbview.cpp + #llbreadcrumbview.cpp # Unused llbrowsernotification.cpp llbuycurrencyhtml.cpp llcallingcard.cpp @@ -487,10 +487,11 @@ set(viewer_SOURCE_FILES llinventoryobserver.cpp llinventorypanel.cpp lljoystickbutton.cpp + llkeyconflict.cpp lllandmarkactions.cpp lllandmarklist.cpp lllegacyatmospherics.cpp - lllistbrowser.cpp + #lllistbrowser.cpp # Unused lllistcontextmenu.cpp lllistview.cpp lllocalbitmaps.cpp @@ -652,7 +653,7 @@ set(viewer_SOURCE_FILES llregioninfomodel.cpp llregionposition.cpp llremoteparcelrequest.cpp - llsavedsettingsglue.cpp + #llsavedsettingsglue.cpp # Unused llsaveoutfitcombobtn.cpp llscenemonitor.cpp llsceneview.cpp @@ -668,6 +669,7 @@ set(viewer_SOURCE_FILES llsecapi.cpp llsechandler_basic.cpp llselectmgr.cpp + llsetkeybinddialog.cpp llsettingspicker.cpp llsettingsvo.cpp llshareavatarhandler.cpp @@ -747,7 +749,7 @@ set(viewer_SOURCE_FILES llurlhistory.cpp llurllineeditorctrl.cpp llurlwhitelist.cpp - llvectorperfoptions.cpp + #llvectorperfoptions.cpp # Unused llversioninfo.cpp llviewchildren.cpp llviewerassetstats.cpp @@ -774,7 +776,7 @@ set(viewer_SOURCE_FILES llviewerjointattachment.cpp llviewerjointmesh.cpp llviewerjoystick.cpp - llviewerkeyboard.cpp + llviewerinput.cpp llviewerlayer.cpp llviewermedia.cpp llviewermedia_streamingaudio.cpp @@ -1018,7 +1020,7 @@ set(viewer_HEADER_FILES llblockedlistitem.h llblocklist.h llbox.h - llbreadcrumbview.h + #llbreadcrumbview.h # Unused llbuycurrencyhtml.h llcallingcard.h llcapabilityprovider.h @@ -1256,10 +1258,11 @@ set(viewer_HEADER_FILES llinventoryobserver.h llinventorypanel.h lljoystickbutton.h + llkeyconflict.h lllandmarkactions.h lllandmarklist.h lllightconstants.h - lllistbrowser.h + #lllistbrowser.h # Unused lllistcontextmenu.h lllistview.h lllocalbitmaps.h @@ -1410,7 +1413,7 @@ set(viewer_HEADER_FILES llremoteparcelrequest.h llresourcedata.h llrootview.h - llsavedsettingsglue.h + #llsavedsettingsglue.h # Unused llsaveoutfitcombobtn.h llscenemonitor.h llsceneview.h @@ -1427,6 +1430,7 @@ set(viewer_HEADER_FILES llsecapi.h llsechandler_basic.h llselectmgr.h + llsetkeybinddialog.h llsettingspicker.h llsettingsvo.h llsidepanelappearance.h @@ -1509,7 +1513,7 @@ set(viewer_HEADER_FILES llurlhistory.h llurllineeditorctrl.h llurlwhitelist.h - llvectorperfoptions.h + #llvectorperfoptions.h # Unused llversioninfo.h llviewchildren.h llviewerassetstats.h @@ -1535,7 +1539,7 @@ set(viewer_HEADER_FILES llviewerjointattachment.h llviewerjointmesh.h llviewerjoystick.h - llviewerkeyboard.h + llviewerinput.h llviewerlayer.h llviewermedia.h llviewermediafocus.h @@ -2061,8 +2065,7 @@ set(viewer_APPSETTINGS_FILES app_settings/growl_notifications.xml app_settings/high_graphics.xml app_settings/ignorable_dialogs.xml - app_settings/keys.xml - app_settings/keys_azerty.xml + app_settings/key_bindings.xml app_settings/keywords.ini app_settings/keywords_lsl_default.xml app_settings/logcontrol.xml diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index dbe6222fdd..a0b6fce83f 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.4.17 +6.4.18 diff --git a/indra/newview/app_settings/keys.xml b/indra/newview/app_settings/key_bindings.xml similarity index 64% rename from indra/newview/app_settings/keys.xml rename to indra/newview/app_settings/key_bindings.xml index a8037fec05..18e9b97230 100644 --- a/indra/newview/app_settings/keys.xml +++ b/indra/newview/app_settings/key_bindings.xml @@ -28,34 +28,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - + @@ -64,15 +41,10 @@ - - - - - @@ -84,13 +56,8 @@ - - - - - @@ -99,20 +66,12 @@ - - - - - - - - @@ -139,28 +98,14 @@ - - - - - - - - - - - - - - @@ -179,63 +124,10 @@ + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -251,15 +143,11 @@ - - - - @@ -294,23 +182,23 @@ - - - + + + - - - + + + - - - + + + @@ -334,6 +222,8 @@ + + @@ -359,5 +249,7 @@ + + \ No newline at end of file diff --git a/indra/newview/app_settings/keys_azerty.xml b/indra/newview/app_settings/keys_azerty.xml deleted file mode 100644 index d228af0cf4..0000000000 --- a/indra/newview/app_settings/keys_azerty.xml +++ /dev/null @@ -1,363 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 4c4cc72d53..f8941bbccc 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -5613,7 +5613,7 @@ DoubleClickAutoPilot Comment - Enable double-click auto pilot + (Obsolete)Enable double-click auto pilot Persist 1 Type @@ -5621,10 +5621,10 @@ Value 0 - DoubleClickTeleport + DoubleClickTeleport Comment - Enable double-click to teleport where allowed + Enable double-click to teleport where allowed (afects minimap and people panel) Persist 1 Type @@ -11077,7 +11077,7 @@ Change of this parameter will affect the layout of buttons in notification toast PushToTalkButton Comment - Which button or keyboard key is used for push-to-talk + (Obsolete)Which button or keyboard key is used for push-to-talk Persist 1 Type @@ -19912,7 +19912,7 @@ Change of this parameter will affect the layout of buttons in notification toast ClickToWalk Comment - Click in world to walk to location + (obsolete)Click in world to walk to location Persist 1 Type @@ -21148,17 +21148,6 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 - FSUseAzertyKeyboardLayout - - Comment - Uses a keyboard layout suitable for keyboards with AZERTY layout. - Persist - 1 - Type - Boolean - Value - 0 - FSNameTagZOffsetCorrection Comment @@ -24628,6 +24617,28 @@ Change of this parameter will affect the layout of buttons in notification toast Value 0 + FSTelemetryEnableWhenConnected + + Comment + Enable telemetry as soon as a server connects + Persist + 1 + Type + Boolean + Value + 1 + + FSTelemetryActive + + Comment + Enable profiling as soon as a server connects. Off by default and non-persistent. Use with FSTelemetryEnableWhenConnected to initiate on startup + Persist + 0 + Type + Boolean + Value + 0 + FSFilterGrowlKeywordDuplicateIMs Comment diff --git a/indra/newview/fonts/CascadiaCode-Light.ttf b/indra/newview/fonts/CascadiaCode-Light.ttf new file mode 100644 index 0000000000..0abd90a372 Binary files /dev/null and b/indra/newview/fonts/CascadiaCode-Light.ttf differ diff --git a/indra/newview/fonts/CascadiaCodeFONTLOG.txt b/indra/newview/fonts/CascadiaCodeFONTLOG.txt new file mode 100644 index 0000000000..ed62cf14d2 --- /dev/null +++ b/indra/newview/fonts/CascadiaCodeFONTLOG.txt @@ -0,0 +1,80 @@ +FONTLOG for Cascadia Code +=================================== + +This file provides detailed information on the Cascadia Code font. This information should be distributed along with the Cascadia Code font and any derivative works. + +Basic Font Information +----------------------------------- + +Cascadia Code is a monospaced font shipped by Microsoft. It includes programming ligatures and is intended for use in terminal applications as well as text editors. + +Contribution Information +----------------------------------- + +Documentation and contribution guidelines can be found at: https://github.com/microsoft/cascadia-code + +ChangeLog +----------------------------------- + +09-25-2019 (Aaron Bell) Bug Fixes and Improvements + +09-18-2019 (Aaron Bell) Cascadia Code Version 1909.16 +- Initial release + +10-04-2019 (Aaron Bell) Cascadia Code Version 1910.04 +- Addition of Latin-1 characters +- Addition of box drawing glyphs +11-20-2019 (Aaron Bell) Cascadia Code Version 1911.20 +- Cyrillic, Greek, Vietnamese, and many other symbols added +- Font converted to UFO with a build workflow +- font rehinted +- many other fixes! + +04-29-2020 (Aaron Bell) April bug fixes +- Many small bug fixes from the last few months + +05-05-2020 Font vertical metrics locked + +05-15-2020 (Aaron Bell, Dustin Howett) [Version 2005.15] +Fixed WinDescent value to improve line spacing + +06-29-2020 (Aaron Bell) Variable font conversion +Converted Cascadia Code to a Variable font with a weight axis + + +06-29-2020 (Aaron Bell) [Version 2007.01] +Reworked Cascadia Code to be a variable font with 6 named variations ranging +from ExtraLight to Bold. +Split the long equals ligature (==) for earier reading +Removed the hexadeciaml 'x' alternate +Corrected malformed IJacute dioacritics +Introduced a compatibility layout for PowerLine glyphs that will +be used in GDI + +08-25-2020 (Aaron Bell) [Version 2008.25] +Started generating static TTFs and hinting them with ttfautohint +Aligned the powerline glyphs to the font metrics exactly +Realigned the box drawing glyphs to match the letters +Added anchors to all alphabetic characters (and brevecomb-cy) +added fi/fl ligatures (decomposed!) +Fixed the weight of horncomb in bold + +9-14-2020 +1) Added support for Salishan language groups +2) Remastered mark positioning for glyphs with 2 diacritics (needed for Salishan) +3) Rebuild Build Script to simplify code +4) Fixed minor other hinting issues +5) Fixed RVRN substitutions for heavy weight + +9-21-2020 +1) Correcting mark positioning for salishan + +Acknowledgements +----------------------------------- + +If you make modifications, be sure to add your name (N), email (E), web-address (if you have one) (W), and description (D). This list is in alphabetical order. + +N: Aaron Bell +E: aaron@sajatypeworks.com +W: http://sajatypeworks.com +D: Original font designer diff --git a/indra/newview/fonts/CascadiaCodeLICENSE.txt b/indra/newview/fonts/CascadiaCodeLICENSE.txt new file mode 100644 index 0000000000..40f856f6ec --- /dev/null +++ b/indra/newview/fonts/CascadiaCodeLICENSE.txt @@ -0,0 +1,94 @@ +Copyright (c) 2019 - Present, Microsoft Corporation, +with Reserved Font Name Cascadia Code. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +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 THE +COPYRIGHT HOLDER 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. diff --git a/indra/newview/fonts/fonts.xml b/indra/newview/fonts/fonts.xml index 00ce0e8b17..4d921b7a5d 100644 --- a/indra/newview/fonts/fonts.xml +++ b/indra/newview/fonts/fonts.xml @@ -32,6 +32,9 @@ STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + + + + + CascadiaCode-Light.ttf + + meiryo.TTC + YuGothR.ttc + MSGOTHIC.TTC + gulim.ttc + simhei.ttf + ArialUni.ttf + msyh.ttc + seguisym.ttf + nirmala.ttf + tahoma.ttf + Cambria.ttc + micross.ttf + malgun.ttf + + + ヒラギノ角ゴシック W3.ttc + ヒラギノ角ゴ Pro W3.otf + ヒラギノ角ゴ ProN W3.otf + ヒラギノ明朝 ProN W3.ttc + AppleGothic.dfont + AppleGothic.ttf + AppleSDGothicNeo-Regular.otf + AppleSDGothicNeo.ttc + 华文细黑.ttf + PingFang.ttc + STIXGeneral.otf + Thonburi.ttc + + + DejaVuSans.ttf + + + + + CascadiaCode-Light.ttf + + arialbd.ttf + + + Helvetica.dfont + + + + + CascadiaCode-Light.ttf + + arial.ttf + + + Helvetica.dfont + + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + SourceCodePro-Regular.ttf + + + + ocra.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + + + CascadiaCode-Light.ttf + + arial.ttf + + + arial.ttf + + + + + CascadiaCode-Light.ttf + + arialbd.ttf + + + arialbd.ttf + + + + + CascadiaCode-Light.ttf + + ariali.ttf + + + ariali.ttf + + + + + CascadiaCode-Light.ttf + + arialbi.ttf + + + arialbi.ttf + + + + + times.ttf + CascadiaCode-Light.ttf + + + + + + + + + + + diff --git a/indra/newview/fonts/fonts_celestia_medium_redux.xml b/indra/newview/fonts/fonts_celestia_medium_redux.xml index d177a69a56..eb4c726e36 100644 --- a/indra/newview/fonts/fonts_celestia_medium_redux.xml +++ b/indra/newview/fonts/fonts_celestia_medium_redux.xml @@ -32,6 +32,9 @@ STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + PingFang.ttc STIXGeneral.otf + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSansAllCaps.ttf @@ -170,6 +178,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf - + + + CascadiaCode-Light.ttf + + DroidSans.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + opendyslexic.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="9.0" /> + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + MobiSans.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + NotoSansCombined-Regular.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + Roboto-Regular.ttf @@ -172,6 +180,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + STIXGeneral.otf Thonburi.ttc + + DejaVuSans.ttf + ocra.ttf + + CascadiaCode-Light.ttf + + DejaVuSans.ttf @@ -176,6 +184,10 @@ comment="Size for monospaced font (points, or 1/72 of an inch)" size="8.0" /> + Load Firestorm's registration page from within the viewer itself + // LLWeb::loadURLExternal(LLTrans::getString("create_account_url")); + LLWeb::loadURLInternal(LLTrans::getString("create_account_url")); + // } } diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 3be82ce9be..14e09d4773 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -408,7 +408,7 @@ CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT.lnk" \ WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Create Account.url" \ "InternetShortcut" "URL" \ - "https://join.secondlife.com/" + "https://www.firestormviewer.org/join-secondlife/" WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Your Account.url" \ "InternetShortcut" "URL" \ "https://www.secondlife.com/account/" diff --git a/indra/newview/lggcontactsets.cpp b/indra/newview/lggcontactsets.cpp index 8ad4cce2df..4a1c76113f 100644 --- a/indra/newview/lggcontactsets.cpp +++ b/indra/newview/lggcontactsets.cpp @@ -1048,6 +1048,13 @@ LGGContactSets::ContactSet* LGGContactSets::getContactSet(const std::string& set return NULL; } +bool LGGContactSets::checkCustomName(const LLUUID& id, bool& dn_removed, std::string& pseudonym) +{ + dn_removed = hasDisplayNameRemoved(id); + pseudonym = getPseudonym(id); + return hasPseudonym(id); +} + // static bool LGGContactSets::handleAddContactSetCallback(const LLSD& notification, const LLSD& response) { diff --git a/indra/newview/lggcontactsets.h b/indra/newview/lggcontactsets.h index fa326210e7..76d87cdc7a 100644 --- a/indra/newview/lggcontactsets.h +++ b/indra/newview/lggcontactsets.h @@ -72,6 +72,8 @@ public: bool hasDisplayNameRemoved(const LLUUID& friend_id); bool hasDisplayNameRemoved(uuid_vec_t ids); + bool checkCustomName(const LLUUID& id, bool& dn_removed, std::string& pseudonym); + string_vec_t getFriendSets(const LLUUID& friend_id); string_vec_t getAllContactSets(); diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 23e6d443e1..b8a7bb482b 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -3011,10 +3011,19 @@ void LLAgentCamera::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate, BOOL re LLVector3 at_axis; if (!isAgentAvatarValid() || !gAgentAvatarp->getParent()) { - at_axis = LLViewerCamera::getInstance()->getAtAxis(); - at_axis.mV[VZ] = 0.f; - at_axis.normalize(); - gAgent.resetAxes(at_axis); + // In case of front view rotate agent to look into direction opposite to camera + // In case of rear view rotate agent into diraction same as camera, e t c + LLVector3 vect = getCameraOffsetInitial(); + F32 rotxy = F32(atan2(vect.mV[VY], vect.mV[VX])); + + LLCoordFrame frameCamera = *((LLCoordFrame*)LLViewerCamera::getInstance()); + // front view angle rotxy is zero, rear view rotxy angle is 180, compensate + frameCamera.yaw((180 * DEG_TO_RAD) - rotxy); + at_axis = frameCamera.getAtAxis(); + at_axis.mV[VZ] = 0.f; + at_axis.normalize(); + gAgent.resetAxes(at_axis); + gAgent.yaw(0); } } } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 6b3699de30..5611f6adfb 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -3456,7 +3456,7 @@ void LLAppearanceMgr::updateIsDirty() { LLViewerInventoryItem *item = *i; - if (item->getName() == FSLSLBridge::instance().currentFullName()) + if (FSLSLBridge::instance().isBridgeValid() && item && item->getLinkedUUID() == FSLSLBridge::instance().getBridge()->getUUID()) { cof_items.erase( i ); break; @@ -3688,6 +3688,19 @@ void update_base_outfit_after_ordering() } } + // Exclude LSL bridge or the following size check will always fail! + for (LLInventoryModel::item_array_t::iterator i = cof_item_array.begin(); i != cof_item_array.end(); ++i) + { + LLViewerInventoryItem *item = *i; + + if (FSLSLBridge::instance().isBridgeValid() && item && item->getLinkedUUID() == FSLSLBridge::instance().getBridge()->getUUID()) + { + cof_item_array.erase(i); + break; + } + } + // + if (outfit_item_array.size() != cof_item_array.size()) { return; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7fdcdfaad3..7817bf9210 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -49,6 +49,7 @@ #include "llwindow.h" #include "llviewerstats.h" #include "llviewerstatsrecorder.h" +#include "llkeyconflict.h" // for legacy keybinding support, remove later #include "llmarketplacefunctions.h" #include "llmarketplacenotifications.h" #include "llmd5.h" @@ -164,7 +165,7 @@ #include "llapr.h" #include -#include "llviewerkeyboard.h" +#include "llviewerinput.h" #include "lllfsthread.h" #include "llworkerthread.h" #include "lltexturecache.h" @@ -282,6 +283,8 @@ #include "fsradar.h" #include "fsassetblacklist.h" +#include "fstelemetry.h" // Tracy profiler support + #if (LL_LINUX || LL_SOLARIS) && LL_GTK #include "glib.h" #endif // (LL_LINUX || LL_SOLARIS) && LL_GTK @@ -1194,31 +1197,6 @@ bool LLAppViewer::init() gGLManager.getGLInfo(gDebugInfo); gGLManager.printGLInfoString(); - // Load Default bindings - // Optional AZERTY keyboard layout - //std::string key_bindings_file = gDirUtilp->findFile("keys.xml", - std::string keyBindingFileName("keys.xml"); - if (gSavedSettings.getBOOL("FSUseAzertyKeyboardLayout")) - { - keyBindingFileName = "keys_azerty.xml"; - } - std::string key_bindings_file = gDirUtilp->findFile(keyBindingFileName, - // - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - - - if (!gViewerKeyboard.loadBindingsXML(key_bindings_file)) - { - std::string key_bindings_file = gDirUtilp->findFile("keys.ini", - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - if (!gViewerKeyboard.loadBindings(key_bindings_file)) - { - LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL; - } - } - // If we don't have the right GL requirements, exit. if (!gGLManager.mHasRequirements) { @@ -1517,6 +1495,9 @@ bool LLAppViewer::init() //onChangeFrameLimit(gSavedSettings.getLLSD("FramePerSecondLimit")); // + // Load User's bindings + loadKeyBindings(); + return true; } @@ -1652,7 +1633,28 @@ bool LLAppViewer::doFrame() { LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); LLSD newFrame; - +// telemetry enabling. +// This ifdef is optional but better to avoid even low overhead code in main loop where not needed. +#ifdef FS_HAS_TELEMETRY_SUPPORT + static bool one_time{false}; + static LLCachedControl profiling_enabled_when_connected(gSavedSettings, "FSTelemetryEnableWhenConnected"); + if( !one_time && (gFrameCount % 10 == 0) ) + { + if(!FSTelemetry::active && profiling_enabled_when_connected && FSTelemetryIsConnected) + { + FSTelemetry::active = true; + gSavedSettings.setBOOL("FSTelemetryActive", TRUE); // keep the setting in sync. + one_time=true; // prevent reset race if we disable manually. + LL_INFOS() << "Profiler or collector connected" << LL_ENDL; + } + else if(!profiling_enabled_when_connected) + { + // no point in checking if we are not waiting. + one_time = true; + } + } +#endif +// // MaxFPS Viewer-Chui merge error LLTimer periodicRenderingTimer; BOOL restore_rendering_masks = FALSE; @@ -1755,6 +1757,7 @@ bool LLAppViewer::doFrame() { joystick->scanJoystick(); gKeyboard->scanKeyboard(); + gViewerInput.scanMouse(); // Chalice Yao's crouch toggle static LLCachedControl fsCrouchToggle(gSavedPerAccountSettings, "FSCrouchToggle"); static LLCachedControl fsCrouchToggleStatus(gSavedPerAccountSettings, "FSCrouchToggleStatus"); @@ -1953,7 +1956,7 @@ bool LLAppViewer::doFrame() LL_INFOS() << "Exiting main_loop" << LL_ENDL; } - + FSFrameMark; // Tracy support delineate Frame return ! LLApp::isRunning(); } @@ -5088,6 +5091,134 @@ void LLAppViewer::addOnIdleCallback(const boost::function& cb) LLDeferredTaskList::instance().addTask(cb); } +void LLAppViewer::loadKeyBindings() +{ + std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "key_bindings.xml"); +#if 1 + // Legacy support + // Remove #if-#endif section half a year after DRTVWR-501 releases. + // Mouse actions are part of keybinding file since DRTVWR-501 instead of being stored in + // settings.xml. To support legacy viewers that were storing in settings.xml we need to + // transfer old variables to new format. + // Also part of backward compatibility is present in LLKeyConflictHandler to modify + // legacy variables on changes in new system (to make sure we won't enforce + // legacy values again if user dropped to defaults in new system) + if (LLVersionInfo::getInstance()->getChannelAndVersion() != gLastRunVersion + || !gDirUtilp->fileExists(key_bindings_file)) // if file is missing, assume that there were no changes by user yet + { + // copy mouse actions and voice key changes to new file + LL_INFOS("InitInfo") << "Converting legacy mouse bindings to new format" << LL_ENDL; + // Load settings from file + LLKeyConflictHandler third_person_view(LLKeyConflictHandler::MODE_THIRD_PERSON); + LLKeyConflictHandler sitting_view(LLKeyConflictHandler::MODE_SITTING); + + // Since we are only modifying keybindings if personal file doesn't exist yet, + // it should be safe to just overwrite the value + // If key is already in use somewhere by default, LLKeyConflictHandler should resolve it. + BOOL value = gSavedSettings.getBOOL("DoubleClickAutoPilot"); + third_person_view.registerControl("walk_to", + 0, + value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + U32 index = value ? 1 : 0; // we can store multiple combinations per action, so if first is in use by doubleclick, go to second + value = gSavedSettings.getBOOL("ClickToWalk"); + third_person_view.registerControl("walk_to", + index, + value ? EMouseClickType::CLICK_LEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + value = gSavedSettings.getBOOL("DoubleClickTeleport"); + third_person_view.registerControl("teleport_to", + 0, + value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + // sitting also supports teleport + sitting_view.registerControl("teleport_to", + 0, + value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, + KEY_NONE, + MASK_NONE, + value); + + std::string key_string = gSavedSettings.getString("PushToTalkButton"); + EMouseClickType mouse = EMouseClickType::CLICK_NONE; + KEY key = KEY_NONE; + if (key_string == "MiddleMouse") + { + mouse = EMouseClickType::CLICK_MIDDLE; + } + else if (key_string == "MouseButton4") + { + mouse = EMouseClickType::CLICK_BUTTON4; + } + else if (key_string == "MouseButton5") + { + mouse = EMouseClickType::CLICK_BUTTON5; + } + else + { + LLKeyboard::keyFromString(key_string, &key); + } + + value = gSavedSettings.getBOOL("PushToTalkToggle"); + std::string control_name = value ? "toggle_voice" : "voice_follow_key"; + third_person_view.registerControl(control_name, 0, mouse, key, MASK_NONE, true); + sitting_view.registerControl(control_name, 0, mouse, key, MASK_NONE, true); + + if (third_person_view.hasUnsavedChanges()) + { + // calls loadBindingsXML() + third_person_view.saveToSettings(); + } + + if (sitting_view.hasUnsavedChanges()) + { + // calls loadBindingsXML() + sitting_view.saveToSettings(); + } + + // in case of voice we need to repeat this in other modes + + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + // edit and first person modes; MODE_SAVED_SETTINGS not in use at the moment + if (i != LLKeyConflictHandler::MODE_THIRD_PERSON && i != LLKeyConflictHandler::MODE_SITTING) + { + LLKeyConflictHandler handler((LLKeyConflictHandler::ESourceMode)i); + + handler.registerControl(control_name, 0, mouse, key, MASK_NONE, true); + + if (handler.hasUnsavedChanges()) + { + // calls loadBindingsXML() + handler.saveToSettings(); + } + } + } + } + // since something might have gone wrong or there might have been nothing to save + // (and because otherwise following code will have to be encased in else{}), + // load everything one last time +#endif + if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) + { + // Failed to load custom bindings, try default ones + key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "key_bindings.xml"); + if (!gViewerInput.loadBindingsXML(key_bindings_file)) + { + LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; + } + } +} + void LLAppViewer::purgeCache() { LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << LL_ENDL; @@ -6174,6 +6305,7 @@ void LLAppViewer::disconnectViewer() gInventory.getLibraryOwnerID()); } + LLAvatarNameCache::instance().setCustomNameCheckCallback(LLAvatarNameCache::custom_name_check_callback_t()); // Contact sets saveNameCache(); if (LLExperienceCache::instanceExists()) { diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 7c16cad534..c2e19b9103 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -208,6 +208,8 @@ public: void purgeCache(); // Clear the local cache. void purgeCacheImmediate(); //clear local cache immediately. S32 updateTextureThreads(F32 max_time); + + void loadKeyBindings(); // mute/unmute the system's master audio virtual void setMasterSystemAudioMute(bool mute); diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 0bac4cff3f..20b62a063a 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -43,7 +43,7 @@ #include "llmimetypes.h" #include "llnotifications.h" #include "llnotificationsutil.h" -#include "llsavedsettingsglue.h" +// #include "llsavedsettingsglue.h" # Unused #include "llviewertexturelist.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp index d8e45acac7..757fefbf86 100644 --- a/indra/newview/llfloatercamera.cpp +++ b/indra/newview/llfloatercamera.cpp @@ -250,6 +250,21 @@ void activate_camera_tool() /*static*/ bool LLFloaterCamera::inFreeCameraMode() { LLFloaterCamera* floater_camera = LLFloaterCamera::findInstance(); + + // Phototools camera + if (!floater_camera) + { + floater_camera = LLFloaterCamera::findPhototoolsInstance(); + } + // + + // Optional small camera floater + if (!floater_camera) + { + floater_camera = LLFloaterCamera::findSmallInstance(); + } + // + if (floater_camera && floater_camera->mCurrMode == CAMERA_CTRL_MODE_FREE_CAMERA && gAgentCamera.getCameraMode() != CAMERA_MODE_MOUSELOOK) { return true; diff --git a/indra/newview/llfloaterjoystick.cpp b/indra/newview/llfloaterjoystick.cpp index 3d5af0149e..48913cc91e 100644 --- a/indra/newview/llfloaterjoystick.cpp +++ b/indra/newview/llfloaterjoystick.cpp @@ -52,6 +52,11 @@ #include #endif +// FIRE-14344 - show joystick buttons +const std::string JOYSTICK_BUTTON_ON ( "\xE2\xAC\xA4" ); // U+2B24 BLACK LARGE CIRCLE +const std::string JOYSTICK_BUTTON_OFF( "\xE2\x97\xAF" ); // U+25EF WHITE LARGE CIRCLE +// + static LLTrace::SampleStatHandle<> sJoystickAxis0("Joystick axis 0"), sJoystickAxis1("Joystick axis 1"), sJoystickAxis2("Joystick axis 2"), @@ -68,7 +73,6 @@ static LLTrace::SampleStatHandle<>* sJoystickAxes[6] = &sJoystickAxis5 }; - #if LL_WINDOWS && !LL_MESA_HEADLESS BOOL CALLBACK di8_list_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVOID pvRef) @@ -113,10 +117,20 @@ void LLFloaterJoystick::draw() refreshListOfDevices(); } + // FIRE-14344 - If we are on the login screen, this value will be 0.0f and we know we + // have to update the joystick status by ourselves + if (gFrameIntervalSeconds.value() == 0.0f) + { + joystick->updateStatus(); + } + // + for (U32 i = 0; i < 6; i++) { F32 value = joystick->getJoystickAxis(i); - sample(*sJoystickAxes[i], value * gFrameIntervalSeconds.value()); + // FIRE-14344 - using the frame interval seems to break the graphs + // sample(*sJoystickAxes[i], value * gFrameIntervalSeconds.value()); + sample(*sJoystickAxes[i], value); if (mAxisStatsBar[i]) { F32 minbar, maxbar; @@ -129,6 +143,23 @@ void LLFloaterJoystick::draw() } } + // FIRE-14344 - show joystick buttons + std::string buttons; + for (U32 i = 0; i < 16; i++) + { + U32 value = joystick->getJoystickButton(i); + if (value == 0) + { + buttons += JOYSTICK_BUTTON_OFF; + } + else + { + buttons += JOYSTICK_BUTTON_ON; + } + } + mJoystickButtons->setText(buttons); + // + LLFloater::draw(); } @@ -137,7 +168,10 @@ BOOL LLFloaterJoystick::postBuild() center(); // Micro Save on calls to gSavedSettings //F32 range = gSavedSettings.getBOOL("Cursor3D") ? 128.f : 2.f; - F32 range = m3DCursor ? 128.f : 2.f; + // FIRE-14344 - use 1.f for the graph ranges instead of 128.f : 2.f to get better resolution - + // needs testing with an actual absolute pointer device if the range scales in a useful way when + // not starting at 128.f + F32 range = 1.f; for (U32 i = 0; i < 6; i++) { @@ -160,6 +194,9 @@ BOOL LLFloaterJoystick::postBuild() childSetAction("cancel_btn", onClickCancel, this); childSetAction("ok_btn", onClickOK, this); + // FIRE-14344 - show joystick buttons + mJoystickButtons = getChild("joystick_buttons"); + refresh(); refreshListOfDevices(); return TRUE; @@ -294,7 +331,9 @@ void LLFloaterJoystick::refreshListOfDevices() std::string desc = LLViewerJoystick::getInstance()->getDescription(); if (!desc.empty()) { - LLSD value = LLSD::Integer(0); + // FIRE-30846 - Select the first detected device, don't fall back to 0 which will select "None" + // LLSD value = LLSD::Integer(0); + LLSD value = LLSD::Integer(1); addDevice(desc, value); mHasDeviceList = true; } diff --git a/indra/newview/llfloaterjoystick.h b/indra/newview/llfloaterjoystick.h index 1d46efd3f6..5bda3a0b1e 100644 --- a/indra/newview/llfloaterjoystick.h +++ b/indra/newview/llfloaterjoystick.h @@ -97,6 +97,9 @@ private: // stats view LLStatBar* mAxisStatsBar[6]; + + // FIRE-14344 - show joystick buttons + LLTextBase* mJoystickButtons; }; #endif diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 97c7e4fde6..379b0f3eeb 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -81,8 +81,9 @@ #include "lltrans.h" #include "llviewercontrol.h" #include "llviewercamera.h" -#include "llviewerwindow.h" +#include "llviewereventrecorder.h" #include "llviewermessage.h" +#include "llviewerwindow.h" #include "llviewershadermgr.h" #include "llviewerthrottle.h" #include "llvoavatarself.h" @@ -166,15 +167,18 @@ #endif // +// FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. +#include "llfiltereditor.h" + // FIRE-6340, FIRE-6567 - Setting Bandwidth issues //const F32 BANDWIDTH_UPDATER_TIMEOUT = 0.5f; char const* const VISIBILITY_DEFAULT = "default"; char const* const VISIBILITY_HIDDEN = "hidden"; //control value for middle mouse as talk2push button -const static std::string MIDDLE_MOUSE_CV = "MiddleMouse"; // for voice client and redability -const static std::string MOUSE_BUTTON_4_CV = "MouseButton4"; -const static std::string MOUSE_BUTTON_5_CV = "MouseButton5"; +//const static std::string MIDDLE_MOUSE_CV = "MiddleMouse"; // for voice client and redability +//const static std::string MOUSE_BUTTON_4_CV = "MouseButton4"; +//const static std::string MOUSE_BUTTON_5_CV = "MouseButton5"; /// This must equal the maximum value set for the IndirectMaxComplexity slider in panel_preferences_graphics1.xml static const U32 INDIRECT_MAX_ARC_OFF = 101; // all the way to the right == disabled @@ -188,6 +192,13 @@ static const F32 MIN_ARC_LOG = log(MIN_ARC_LIMIT); static const F32 MAX_ARC_LOG = log(MAX_ARC_LIMIT); static const F32 ARC_LIMIT_MAP_SCALE = (MAX_ARC_LOG - MIN_ARC_LOG) / (MAX_INDIRECT_ARC_LIMIT - MIN_INDIRECT_ARC_LIMIT); +// FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. +// define these constants so any future changes will be easier and less error prone +static const S32 COLUMN_POPUP_SPACER = 0; +static const S32 COLUMN_POPUP_CHECKBOX = 1; +static const S32 COLUMN_POPUP_LABEL = 2; +// + struct LabelDef : public LLInitParam::Block { Mandatory name; @@ -207,87 +218,6 @@ struct LabelTable : public LLInitParam::Block {} }; -class LLVoiceSetKeyDialog : public LLModalDialog -{ -public: - LLVoiceSetKeyDialog(const LLSD& key); - ~LLVoiceSetKeyDialog(); - - /*virtual*/ BOOL postBuild(); - - void setParent(LLFloaterPreference* parent) { mParent = parent; } - - BOOL handleKeyHere(KEY key, MASK mask); - BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); - static void onCancel(void* user_data); - -private: - LLFloaterPreference* mParent; -}; - -LLVoiceSetKeyDialog::LLVoiceSetKeyDialog(const LLSD& key) - : LLModalDialog(key), - mParent(NULL) -{ -} - -//virtual -BOOL LLVoiceSetKeyDialog::postBuild() -{ - childSetAction("Cancel", onCancel, this); - getChild("Cancel")->setFocus(TRUE); - - gFocusMgr.setKeystrokesOnly(TRUE); - - return TRUE; -} - -LLVoiceSetKeyDialog::~LLVoiceSetKeyDialog() -{ -} - -BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask) -{ - BOOL result = TRUE; - - if (key == 'Q' && mask == MASK_CONTROL) - { - result = FALSE; - } - else if (mParent) - { - mParent->setKey(key); - } - closeFloater(); - return result; -} - -BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) -{ - BOOL result = FALSE; - if (down - && (clicktype == LLMouseHandler::CLICK_MIDDLE || clicktype == LLMouseHandler::CLICK_BUTTON4 || clicktype == LLMouseHandler::CLICK_BUTTON5) - && mask == 0) - { - mParent->setMouse(clicktype); - result = TRUE; - closeFloater(); - } - else - { - result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); - } - - return result; -} - -//static -void LLVoiceSetKeyDialog::onCancel(void* user_data) -{ - LLVoiceSetKeyDialog* self = (LLVoiceSetKeyDialog*)user_data; - self->closeFloater(); -} - // global functions @@ -474,37 +404,6 @@ bool callback_pick_debug_search(const LLSD& notification, const LLSD& response) } // -/*bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option && floater ) - { - if ( floater ) - { - floater->setAllIgnored(); - // LLFirstUse::disableFirstUse(); - floater->buildPopupLists(); - } - } - return false; -} - -bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if ( 0 == option && floater ) - { - if ( floater ) - { - floater->resetAllIgnored(); - //LLFirstUse::resetFirstUse(); - floater->buildPopupLists(); - } - } - return false; -} -*/ - void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator) { numerator = 0; @@ -529,8 +428,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mGotPersonalInfo(false), mOriginalIMViaEmail(false), mLanguageChanged(false), - mAvatarDataInitialized(false), - mClickActionDirty(false) + mAvatarDataInitialized(false) { LLConversationLog::instance().addObserver(this); @@ -539,7 +437,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) static bool registered_dialog = false; if (!registered_dialog) { - LLFloaterReg::add("voice_set_key", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("keybind_dialog", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); registered_dialog = true; } @@ -558,13 +456,14 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.ResetCache", boost::bind(&LLFloaterPreference::onClickResetCache, this)); // mCommitCallbackRegistrar.add("Pref.ClickSkin", boost::bind(&LLFloaterPreference::onClickSkin, this,_1, _2)); // mCommitCallbackRegistrar.add("Pref.SelectSkin", boost::bind(&LLFloaterPreference::onSelectSkin, this)); - mCommitCallbackRegistrar.add("Pref.VoiceSetKey", boost::bind(&LLFloaterPreference::onClickSetKey, this)); - mCommitCallbackRegistrar.add("Pref.VoiceSetClearKey", boost::bind(&LLFloaterPreference::onClickClearKey, this)); // FIRE-3803: Clear voice toggle button - mCommitCallbackRegistrar.add("Pref.VoiceSetMiddleMouse", boost::bind(&LLFloaterPreference::onClickSetMiddleMouse, this)); // Handled centrally now // mCommitCallbackRegistrar.add("Pref.SetSounds", boost::bind(&LLFloaterPreference::onClickSetSounds, this)); - mCommitCallbackRegistrar.add("Pref.ClickEnablePopup", boost::bind(&LLFloaterPreference::onClickEnablePopup, this)); - mCommitCallbackRegistrar.add("Pref.ClickDisablePopup", boost::bind(&LLFloaterPreference::onClickDisablePopup, this)); + // FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. + // mCommitCallbackRegistrar.add("Pref.ClickEnablePopup", boost::bind(&LLFloaterPreference::onClickEnablePopup, this)); + // mCommitCallbackRegistrar.add("Pref.ClickDisablePopup", boost::bind(&LLFloaterPreference::onClickDisablePopup, this)); + mCommitCallbackRegistrar.add("Pref.SelectPopup", boost::bind(&LLFloaterPreference::onSelectPopup, this)); + mCommitCallbackRegistrar.add("Pref.UpdatePopupFilter", boost::bind(&LLFloaterPreference::onUpdatePopupFilter, this)); + // mCommitCallbackRegistrar.add("Pref.LogPath", boost::bind(&LLFloaterPreference::onClickLogPath, this)); mCommitCallbackRegistrar.add("Pref.RenderExceptions", boost::bind(&LLFloaterPreference::onClickRenderExceptions, this)); mCommitCallbackRegistrar.add("Pref.HardwareDefaults", boost::bind(&LLFloaterPreference::setHardwareDefaults, this)); @@ -889,6 +788,12 @@ BOOL LLFloaterPreference::postBuild() #endif // + // FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. + mPopupList = getChild("all_popups"); + mPopupList->setFilterColumn(COLUMN_POPUP_LABEL); + mPopupFilter = getChild("popup_filter"); + // + return TRUE; } @@ -984,25 +889,18 @@ LLFloaterPreference::~LLFloaterPreference() LLConversationLog::instance().removeObserver(this); } -void LLFloaterPreference::draw() -{ - // Performance improvement - //BOOL has_first_selected = (getChildRef("disabled_popups").getFirstSelected()!=NULL); - //gSavedSettings.setBOOL("FirstSelectedDisabledPopups", has_first_selected); - // - //has_first_selected = (getChildRef("enabled_popups").getFirstSelected()!=NULL); - //gSavedSettings.setBOOL("FirstSelectedEnabledPopups", has_first_selected); - - static LLScrollListCtrl* disabled_popups = getChild("disabled_popups"); - static LLScrollListCtrl* enabled_popups = getChild("enabled_popups"); - BOOL has_first_selected = disabled_popups->getFirstSelected() != NULL; - gSavedSettings.setBOOL("FirstSelectedDisabledPopups", has_first_selected); - has_first_selected = enabled_popups->getFirstSelected() != NULL; - gSavedSettings.setBOOL("FirstSelectedEnabledPopups", has_first_selected); - // - - LLFloater::draw(); -} +// FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. +// void LLFloaterPreference::draw() +// { +// BOOL has_first_selected = (getChildRef("disabled_popups").getFirstSelected()!=NULL); +// gSavedSettings.setBOOL("FirstSelectedDisabledPopups", has_first_selected); +// +// has_first_selected = (getChildRef("enabled_popups").getFirstSelected()!=NULL); +// gSavedSettings.setBOOL("FirstSelectedEnabledPopups", has_first_selected); +// +// LLFloater::draw(); +//} +// void LLFloaterPreference::saveSettings() { @@ -1087,12 +985,6 @@ void LLFloaterPreference::apply() saveAvatarProperties(); - if (mClickActionDirty) - { - updateClickActionSettings(); - mClickActionDirty = false; - } - // Fix resetting graphics preset on cancel; Save preset here because cancel() gets called in either way! saveGraphicsPreset(gSavedSettings.getString("PresetGraphicActive")); } @@ -1127,11 +1019,7 @@ void LLFloaterPreference::cancel() // reverts any changes to current skin //gSavedSettings.setString("SkinCurrent", sSkin); - if (mClickActionDirty) - { - updateClickActionControls(); - mClickActionDirty = false; - } + updateClickActionViews(); LLFloaterPreferenceProxy * advanced_proxy_settings = LLFloaterReg::findTypedInstance("prefs_proxy"); if (advanced_proxy_settings) @@ -1242,15 +1130,21 @@ void LLFloaterPreference::onOpen(const LLSD& key) onChangeAnimationFolder(); // Load (double-)click to walk/teleport settings. - updateClickActionControls(); + updateClickActionViews(); // Load UI Sounds tabs settings. updateUISoundsControls(); // Enabled/disabled popups, might have been changed by user actions // while preferences floater was closed. - buildPopupLists(); + // FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. + // buildPopupLists(); + mPopupFilter->setText(LLStringExplicit("")); + mPopupList->setFilterString(LLStringExplicit("")); + + buildPopupList(); + // //get the options that were checked // [CHUI MERGE] @@ -2046,16 +1940,79 @@ void LLFloaterPreference::refreshSkin(void* data) self->getChild("skin_selection", true)->setValue(sSkin); } */ -void LLFloaterPreference::buildPopupLists() + +// FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. +// void LLFloaterPreference::buildPopupLists() +// { +// LLScrollListCtrl& disabled_popups = +// getChildRef("disabled_popups"); +// LLScrollListCtrl& enabled_popups = +// getChildRef("enabled_popups"); +// +// disabled_popups.deleteAllItems(); +// enabled_popups.deleteAllItems(); +// +// 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; +// +// LLSD row; +// row["columns"][0]["value"] = formp->getIgnoreMessage(); +// row["columns"][0]["font"] = "SANSSERIF_SMALL"; +// row["columns"][0]["width"] = 400; +// +// LLScrollListItem* item = NULL; +// +// bool show_popup = !formp->getIgnored(); +// if (!show_popup) +// { +// if (ignore == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) +// { +// // Default responses are declared in "ignores" settings group, see llnotifications.cpp +// //LLSD last_response = LLUI::getInstance()->mSettingGroups["config"]->getLLSD("Default" + templatep->mName); +// LLSD last_response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + templatep->mName); +// // +// if (!last_response.isUndefined()) +// { +// for (LLSD::map_const_iterator it = last_response.beginMap(); +// it != last_response.endMap(); +// ++it) +// { +// if (it->second.asBoolean()) +// { +// row["columns"][1]["value"] = formp->getElement(it->first)["ignore"].asString(); +// row["columns"][1]["font"] = "SANSSERIF_SMALL"; +// row["columns"][1]["width"] = 360; +// break; +// } +// } +// } +// } +// item = disabled_popups.addElement(row); +// } +// else +// { +// item = enabled_popups.addElement(row); +// } +// +// if (item) +// { +// item->setUserdata((void*)&iter->first); +// } +// } +// } + +void LLFloaterPreference::buildPopupList() { - LLScrollListCtrl& disabled_popups = - getChildRef("disabled_popups"); - LLScrollListCtrl& enabled_popups = - getChildRef("enabled_popups"); - - disabled_popups.deleteAllItems(); - enabled_popups.deleteAllItems(); - + mPopupList->deleteAllItems(); + for (LLNotifications::TemplateMap::const_iterator iter = LLNotifications::instance().templatesBegin(); iter != LLNotifications::instance().templatesEnd(); ++iter) @@ -2066,63 +2023,44 @@ void LLFloaterPreference::buildPopupLists() LLNotificationForm::EIgnoreType ignore = formp->getIgnoreType(); if (ignore <= LLNotificationForm::IGNORE_NO) continue; - - LLSD row; - row["columns"][0]["value"] = formp->getIgnoreMessage(); - row["columns"][0]["font"] = "SANSSERIF_SMALL"; - row["columns"][0]["width"] = 400; - - LLScrollListItem* item = NULL; - + bool show_popup = !formp->getIgnored(); - if (!show_popup) - { -// Don't show chosen option for ignored dialogs in the list. There is only one -// notification that makes use of it ("ReplaceAttachment") and it would make the -// list appear truncated. -#if 0 - if (ignore == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) - { - // Default responses are declared in "ignores" settings group, see llnotifications.cpp - //LLSD last_response = LLUI::getInstance()->mSettingGroups["config"]->getLLSD("Default" + templatep->mName); - LLSD last_response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + templatep->mName); - // - if (!last_response.isUndefined()) - { - for (LLSD::map_const_iterator it = last_response.beginMap(); - it != last_response.endMap(); - ++it) - { - if (it->second.asBoolean()) - { - row["columns"][1]["value"] = formp->getElement(it->first)["ignore"].asString(); - row["columns"][1]["font"] = "SANSSERIF_SMALL"; - row["columns"][1]["width"] = 360; - break; - } - } - } - } -#endif - item = disabled_popups.addElement(row); - } - else - { - item = enabled_popups.addElement(row); - } - + + LLSD row; + + // column COLUMN_POPUP_SPACER makes things look good, since "halign" and "center" or LLFontGL::HCENTER don't work -Zi + row["columns"][COLUMN_POPUP_CHECKBOX]["type"] = "checkbox"; + row["columns"][COLUMN_POPUP_CHECKBOX]["column"] = "alert_enabled_check"; + row["columns"][COLUMN_POPUP_CHECKBOX]["value"] = show_popup; + row["columns"][COLUMN_POPUP_LABEL]["column"] = "alert_label"; + row["columns"][COLUMN_POPUP_LABEL]["value"] = formp->getIgnoreMessage(); + + LLScrollListItem* item = mPopupList->addElement(row); + if (item) { item->setUserdata((void*)&iter->first); } } - - // Let's sort it so we can find stuff! - enabled_popups.sortByColumnIndex(0, TRUE); - disabled_popups.sortByColumnIndex(0, TRUE); - // } +void LLFloaterPreference::onSelectPopup() +{ + LLScrollListItem* last = mPopupList->getLastSelectedItem(); + for (auto popup : mPopupList->getAllSelected()) + { + LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)(popup->getUserdata())); + std::string notification_name = templatep->mName; + LLUI::getInstance()->mSettingGroups["ignores"]->setBOOL(notification_name, last->getColumn(COLUMN_POPUP_CHECKBOX)->getValue().asBoolean()); + } +} + +void LLFloaterPreference::onUpdatePopupFilter() +{ + mPopupList->setFilterString(mPopupFilter->getValue().asString()); +} +// + void LLFloaterPreference::refreshEnabledState() { // Improved graphics preferences @@ -2744,6 +2682,7 @@ void LLFloaterPreference::refresh() { advanced->refresh(); } + updateClickActionViews(); } void LLFloaterPreferenceGraphicsAdvanced::refresh() @@ -2785,79 +2724,6 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data) refresh(); } -void LLFloaterPreference::onClickSetKey() -{ - LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance("voice_set_key", LLSD(), TRUE); - if (dialog) - { - dialog->setParent(this); - } -} - -// FIRE-3803: Clear voice toggle button -void LLFloaterPreference::onClickClearKey() -{ - gSavedSettings.setString("PushToTalkButton", ""); -} -// - -void LLFloaterPreference::setKey(KEY key) -{ - getChild("modifier_combo")->setValue(LLKeyboard::stringFromKey(key)); - // update the control right away since we no longer wait for apply - getChild("modifier_combo")->onCommit(); -} - -void LLFloaterPreference::setMouse(LLMouseHandler::EClickType click) -{ - std::string bt_name; - std::string ctrl_value; - switch (click) - { - case LLMouseHandler::CLICK_MIDDLE: - bt_name = "middle_mouse"; - ctrl_value = MIDDLE_MOUSE_CV; - break; - case LLMouseHandler::CLICK_BUTTON4: - bt_name = "button4_mouse"; - ctrl_value = MOUSE_BUTTON_4_CV; - break; - case LLMouseHandler::CLICK_BUTTON5: - bt_name = "button5_mouse"; - ctrl_value = MOUSE_BUTTON_5_CV; - break; - default: - break; - } - - if (!ctrl_value.empty()) - { - LLUICtrl* p2t_line_editor = getChild("modifier_combo"); - // We are using text control names for readability and compatibility with voice - p2t_line_editor->setControlValue(ctrl_value); - // Fix crash "Failed to find string middle_mouse in panel Media Voice tab loaded from file" - //LLPanel* advanced_preferences = dynamic_cast(p2t_line_editor->getParent()); - LLPanel* advanced_preferences = dynamic_cast(p2t_line_editor->getParent()->getParent()->getParent()); - // - if (advanced_preferences) - { - p2t_line_editor->setValue(advanced_preferences->getString(bt_name)); - } - } -} - -void LLFloaterPreference::onClickSetMiddleMouse() -{ - LLUICtrl* p2t_line_editor = getChild("modifier_combo"); - - // update the control right away since we no longer wait for apply - p2t_line_editor->setControlValue(MIDDLE_MOUSE_CV); - - //push2talk button "middle mouse" control value is in English, need to localize it for presentation - LLPanel* audioPanel=getChild("audio"); - p2t_line_editor->setValue(audioPanel->getString("middle_mouse")); -} - // Handled centrally now /* void LLFloaterPreference::onClickSetSounds() @@ -2877,49 +2743,39 @@ void LLFloaterPreference::onClickPreviewUISound(const LLSD& ui_sound_id) } // FIRE-8190: Preview function for "UI Sounds" Panel -/* -void LLFloaterPreference::onClickSkipDialogs() -{ - LLNotificationsUtil::add("SkipShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_skip_dialogs, _1, _2, this)); -} +// FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. +// void LLFloaterPreference::onClickEnablePopup() +// { +// LLScrollListCtrl& disabled_popups = getChildRef("disabled_popups"); +// +// std::vector items = disabled_popups.getAllSelected(); +// std::vector::iterator itor; +// for (itor = items.begin(); itor != items.end(); ++itor) +// { +// LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)((*itor)->getUserdata())); +// //gSavedSettings.setWarning(templatep->mName, TRUE); +// std::string notification_name = templatep->mName; +// LLUI::getInstance()->mSettingGroups["ignores"]->setBOOL(notification_name, TRUE); +// } +// +// buildPopupLists(); +// } -void LLFloaterPreference::onClickResetDialogs() -{ - LLNotificationsUtil::add("ResetShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_reset_dialogs, _1, _2, this)); -} - */ - -void LLFloaterPreference::onClickEnablePopup() -{ - LLScrollListCtrl& disabled_popups = getChildRef("disabled_popups"); - - std::vector items = disabled_popups.getAllSelected(); - std::vector::iterator itor; - for (itor = items.begin(); itor != items.end(); ++itor) - { - LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)((*itor)->getUserdata())); - //gSavedSettings.setWarning(templatep->mName, TRUE); - std::string notification_name = templatep->mName; - LLUI::getInstance()->mSettingGroups["ignores"]->setBOOL(notification_name, TRUE); - } - - buildPopupLists(); -} - -void LLFloaterPreference::onClickDisablePopup() -{ - LLScrollListCtrl& enabled_popups = getChildRef("enabled_popups"); - - std::vector items = enabled_popups.getAllSelected(); - std::vector::iterator itor; - for (itor = items.begin(); itor != items.end(); ++itor) - { - LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)((*itor)->getUserdata())); - templatep->mForm->setIgnored(true); - } - - buildPopupLists(); -} +// void LLFloaterPreference::onClickDisablePopup() +// { +// LLScrollListCtrl& enabled_popups = getChildRef("enabled_popups"); +// +// std::vector items = enabled_popups.getAllSelected(); +// std::vector::iterator itor; +// for (itor = items.begin(); itor != items.end(); ++itor) +// { +// LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)((*itor)->getUserdata())); +// templatep->mForm->setIgnored(true); +// } +// +// buildPopupLists(); +// } +// void LLFloaterPreference::resetAllIgnored() { @@ -3517,7 +3373,7 @@ void LLFloaterPreference::onClickAdvanced() void LLFloaterPreference::onClickActionChange() { - mClickActionDirty = true; + updateClickActionControls(); } void LLFloaterPreference::onClickPermsDefault() @@ -3557,26 +3413,6 @@ void LLFloaterPreference::onLogChatHistorySaved() } } -void LLFloaterPreference::updateClickActionSettings() -{ - const int single_clk_action = getChild("single_click_action_combo")->getValue().asInteger(); - const int double_clk_action = getChild("double_click_action_combo")->getValue().asInteger(); - - gSavedSettings.setBOOL("ClickToWalk", single_clk_action == 1); - gSavedSettings.setBOOL("DoubleClickAutoPilot", double_clk_action == 1); - gSavedSettings.setBOOL("DoubleClickTeleport", double_clk_action == 2); -} - -void LLFloaterPreference::updateClickActionControls() -{ - const bool click_to_walk = gSavedSettings.getBOOL("ClickToWalk"); - const bool dbl_click_to_walk = gSavedSettings.getBOOL("DoubleClickAutoPilot"); - const bool dbl_click_to_teleport = gSavedSettings.getBOOL("DoubleClickTeleport"); - - getChild("single_click_action_combo")->setValue((int)click_to_walk); - getChild("double_click_action_combo")->setValue(dbl_click_to_teleport ? 2 : (int)dbl_click_to_walk); -} - // Load UI Sounds tabs settings void LLFloaterPreference::updateUISoundsControls() { @@ -3600,6 +3436,86 @@ void LLFloaterPreference::updateUISoundsControls() } // +void LLFloaterPreference::updateClickActionControls() +{ + const int single_clk_action = getChild("single_click_action_combo")->getValue().asInteger(); + const int double_clk_action = getChild("double_click_action_combo")->getValue().asInteger(); + + // Todo: This is a very ugly way to get access to keybindings. + // Reconsider possible options. + // Potential option: make constructor of LLKeyConflictHandler private + // but add a getter that will return shared pointer for specific + // mode, pointer should only exist so long as there are external users. + // In such case we won't need to do this 'dynamic_cast' nightmare. + // updateTable() can also be avoided + LLTabContainer* tabcontainer = getChild("pref core"); + for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); + iter != tabcontainer->getChildList()->end(); ++iter) + { + LLView* view = *iter; + LLPanelPreferenceControls* panel = dynamic_cast(view); + if (panel) + { + panel->setKeyBind("walk_to", + EMouseClickType::CLICK_LEFT, + KEY_NONE, + MASK_NONE, + single_clk_action == 1); + + panel->setKeyBind("walk_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE, + double_clk_action == 1); + + panel->setKeyBind("teleport_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE, + double_clk_action == 2); + + panel->updateAndApply(); + } + } +} + +void LLFloaterPreference::updateClickActionViews() +{ + bool click_to_walk = false; + bool dbl_click_to_walk = false; + bool dbl_click_to_teleport = false; + + // Todo: This is a very ugly way to get access to keybindings. + // Reconsider possible options. + LLTabContainer* tabcontainer = getChild("pref core"); + for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); + iter != tabcontainer->getChildList()->end(); ++iter) + { + LLView* view = *iter; + LLPanelPreferenceControls* panel = dynamic_cast(view); + if (panel) + { + click_to_walk = panel->canKeyBindHandle("walk_to", + EMouseClickType::CLICK_LEFT, + KEY_NONE, + MASK_NONE); + + dbl_click_to_walk = panel->canKeyBindHandle("walk_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE); + + dbl_click_to_teleport = panel->canKeyBindHandle("teleport_to", + EMouseClickType::CLICK_DOUBLELEFT, + KEY_NONE, + MASK_NONE); + } + } + + getChild("single_click_action_combo")->setValue((int)click_to_walk); + getChild("double_click_action_combo")->setValue(dbl_click_to_teleport ? 2 : (int)dbl_click_to_walk); +} + void LLFloaterPreference::applyUIColor(LLUICtrl* ctrl, const LLSD& param) { LLUIColorTable::instance().setColor(param.asString(), LLColor4(ctrl->getValue())); @@ -3865,24 +3781,6 @@ BOOL LLPanelPreference::postBuild() getChild("mute_chb_label")->setClickedCallback(boost::bind(&toggleMuteWhenMinimized)); } - //////////////////////PanelAdvanced /////////////////// - if (hasChild("modifier_combo", TRUE)) - { - //localizing if push2talk button is set to middle mouse - std::string modifier_value = getChild("modifier_combo")->getValue().asString(); - if (MIDDLE_MOUSE_CV == modifier_value) - { - getChild("modifier_combo")->setValue(getString("middle_mouse")); - } - else if (MOUSE_BUTTON_4_CV == modifier_value) - { - getChild("modifier_combo")->setValue(getString("button4_mouse")); - } - else if (MOUSE_BUTTON_5_CV == modifier_value) - { - getChild("modifier_combo")->setValue(getString("button5_mouse")); - } - } // Panel Setup (Network) -WoLf if (hasChild("connection_port_enabled")) { @@ -4528,6 +4426,585 @@ void LLPanelPreferenceGraphics::setHardwareDefaults() LLPanelPreference::setHardwareDefaults(); } +//------------------------LLPanelPreferenceControls-------------------------------- +static LLPanelInjector t_pref_contrls("panel_preference_controls"); + +LLPanelPreferenceControls::LLPanelPreferenceControls() + :LLPanelPreference(), + mEditingColumn(-1), + mEditingMode(0) +{ + // MODE_COUNT - 1 because there are currently no settings assigned to 'saved settings'. + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + mConflictHandler[i].setLoadMode((LLKeyConflictHandler::ESourceMode)i); + } +} + +LLPanelPreferenceControls::~LLPanelPreferenceControls() +{ +} + +BOOL LLPanelPreferenceControls::postBuild() +{ + // populate list of controls + pControlsTable = getChild("controls_list"); + pKeyModeBox = getChild("key_mode"); + + pControlsTable->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onListCommit, this)); + pKeyModeBox->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onModeCommit, this)); + getChild("restore_defaults")->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsBtn, this)); + + return TRUE; +} + +void LLPanelPreferenceControls::regenerateControls() +{ + mEditingMode = pKeyModeBox->getValue().asInteger(); + mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); + populateControlTable(); +} + +bool LLPanelPreferenceControls::addControlTableColumns(const std::string &filename) +{ + LLXMLNodePtr xmlNode; + LLScrollListCtrl::Contents contents; + if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + { + LL_WARNS() << "Failed to load " << filename << LL_ENDL; + return false; + } + LLXUIParser parser; + parser.readXUI(xmlNode, contents, filename); + + if (!contents.validateBlock()) + { + return false; + } + + for (LLInitParam::ParamIterator::const_iterator col_it = contents.columns.begin(); + col_it != contents.columns.end(); + ++col_it) + { + pControlsTable->addColumn(*col_it); + } + + return true; +} + +bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) +{ + LLXMLNodePtr xmlNode; + LLScrollListCtrl::Contents contents; + if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + { + LL_WARNS() << "Failed to load " << filename << LL_ENDL; + return false; + } + LLXUIParser parser; + parser.readXUI(xmlNode, contents, filename); + + if (!contents.validateBlock()) + { + return false; + } + + LLScrollListCell::Params cell_params; + // init basic cell params + cell_params.font = LLFontGL::getFontSansSerif(); + cell_params.font_halign = LLFontGL::LEFT; + cell_params.column = ""; + cell_params.value = ""; + + + for (LLInitParam::ParamIterator::const_iterator row_it = contents.rows.begin(); + row_it != contents.rows.end(); + ++row_it) + { + std::string control = row_it->value.getValue().asString(); + if (!control.empty() && control != "menu_separator") + { + bool show = true; + bool enabled = mConflictHandler[mEditingMode].canAssignControl(control); + if (!enabled) + { + // If empty: this is a placeholder to make sure user won't assign + // value by accident, don't show it + // If not empty: predefined control combination user should see + // to know that combination is reserved + show = !mConflictHandler[mEditingMode].isControlEmpty(control); + // example: teleport_to and walk_to in first person view, and + // sitting related functions, see generatePlaceholders() + } + + if (show) + { + // At the moment viewer is hardcoded to assume that columns are named as lst_ctrl%d + LLScrollListItem::Params item_params(*row_it); + item_params.enabled.setValue(enabled); + + S32 num_columns = pControlsTable->getNumColumns(); + for (S32 col = 1; col < num_columns; col++) + { + cell_params.column = llformat("lst_ctrl%d", col); + cell_params.value = mConflictHandler[mEditingMode].getControlString(control, col - 1); + item_params.columns.add(cell_params); + } + pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM); + } + } + else + { + // Separator example: + // + // + // + pControlsTable->addRow(*row_it, EAddPosition::ADD_BOTTOM); + } + } + return true; +} + +void LLPanelPreferenceControls::addControlTableSeparator() +{ + LLScrollListItem::Params separator_params; + separator_params.enabled(false); + LLScrollListCell::Params column_params; + column_params.type = "icon"; + column_params.value = "menu_separator"; + column_params.column = "lst_action"; + column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); + column_params.font_halign = LLFontGL::HCENTER; + separator_params.columns.add(column_params); + pControlsTable->addRow(separator_params, EAddPosition::ADD_BOTTOM); +} + +void LLPanelPreferenceControls::populateControlTable() +{ + pControlsTable->clearRows(); + pControlsTable->clearColumns(); + + // Add columns + std::string filename; + switch ((LLKeyConflictHandler::ESourceMode)mEditingMode) + { + case LLKeyConflictHandler::MODE_THIRD_PERSON: + case LLKeyConflictHandler::MODE_FIRST_PERSON: + case LLKeyConflictHandler::MODE_EDIT_AVATAR: + case LLKeyConflictHandler::MODE_SITTING: + filename = "control_table_contents_columns_basic.xml"; + break; + default: + // Either unknown mode or MODE_SAVED_SETTINGS + // It doesn't have UI or actual settings yet + LL_INFOS() << "Unimplemented mode" << LL_ENDL; + return; + } + addControlTableColumns(filename); + + // Add rows. + // Each file represents individual visual group (movement/camera/media...) + if (mEditingMode == LLKeyConflictHandler::MODE_FIRST_PERSON) + { + // Don't display whole camera and editing groups + addControlTableRows("control_table_contents_movement.xml"); + addControlTableSeparator(); + addControlTableRows("control_table_contents_media.xml"); + } + // MODE_THIRD_PERSON; MODE_EDIT_AVATAR; MODE_SITTING + else if (mEditingMode < LLKeyConflictHandler::MODE_SAVED_SETTINGS) + { + // In case of 'sitting' mode, movements still apply due to vehicles + // but walk_to is not supported and will be hidden by addControlTableRows + addControlTableRows("control_table_contents_movement.xml"); + addControlTableSeparator(); + + addControlTableRows("control_table_contents_camera.xml"); + addControlTableSeparator(); + + addControlTableRows("control_table_contents_editing.xml"); + addControlTableSeparator(); + + addControlTableRows("control_table_contents_media.xml"); + } + else + { + LL_INFOS() << "Unimplemented mode" << LL_ENDL; + return; + } +} + +void LLPanelPreferenceControls::updateTable() +{ + mEditingControl.clear(); + std::vector list = pControlsTable->getAllData(); + for (S32 i = 0; i < list.size(); ++i) + { + std::string control = list[i]->getValue(); + if (!control.empty()) + { + LLScrollListCell* cell = NULL; + + S32 num_columns = pControlsTable->getNumColumns(); + for (S32 col = 1; col < num_columns; col++) + { + cell = list[i]->getColumn(col); + cell->setValue(mConflictHandler[mEditingMode].getControlString(control, col - 1)); + } + } + } + pControlsTable->deselectAllItems(); +} + +void LLPanelPreferenceControls::apply() +{ + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].saveToSettings(); + } + } +} + +void LLPanelPreferenceControls::cancel() +{ + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].clear(); + } + } + pControlsTable->clearRows(); + pControlsTable->clearColumns(); +} + +void LLPanelPreferenceControls::saveSettings() +{ + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].hasUnsavedChanges()) + { + mConflictHandler[i].saveToSettings(); + mConflictHandler[i].clear(); + } + } + + S32 mode = pKeyModeBox->getValue().asInteger(); + if (mConflictHandler[mode].empty() || pControlsTable->isEmpty()) + { + regenerateControls(); + } +} + +void LLPanelPreferenceControls::resetDirtyChilds() +{ + regenerateControls(); +} + +void LLPanelPreferenceControls::onListCommit() +{ + LLScrollListItem* item = pControlsTable->getFirstSelected(); + if (item == NULL) + { + return; + } + + std::string control = item->getValue(); + + if (control.empty()) + { + pControlsTable->deselectAllItems(); + return; + } + + if (!mConflictHandler[mEditingMode].canAssignControl(control)) + { + pControlsTable->deselectAllItems(); + return; + } + + S32 cell_ind = item->getSelectedCell(); + if (cell_ind <= 0) + { + pControlsTable->deselectAllItems(); + return; + } + + // List does not tell us what cell was clicked, so we have to figure it out manually, but + // fresh mouse coordinates are not yet accessible during onCommit() and there are other issues, + // so we cheat: remember item user clicked at, trigger 'key dialog' on hover that comes next, + // use coordinates from hover to calculate cell + + LLScrollListCell* cell = item->getColumn(cell_ind); + if (cell) + { + LLSetKeyBindDialog* dialog = LLFloaterReg::getTypedInstance("keybind_dialog", LLSD()); + if (dialog) + { + mEditingControl = control; + mEditingColumn = cell_ind; + dialog->setParent(this, pControlsTable, DEFAULT_KEY_FILTER); + + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (root_floater) + root_floater->addDependentFloater(dialog); + dialog->openFloater(); + dialog->setFocus(TRUE); + } + } + else + { + pControlsTable->deselectAllItems(); + } +} + +void LLPanelPreferenceControls::onModeCommit() +{ + mEditingMode = pKeyModeBox->getValue().asInteger(); + if (mConflictHandler[mEditingMode].empty()) + { + // opening for first time + mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); + } + populateControlTable(); +} + +void LLPanelPreferenceControls::onRestoreDefaultsBtn() +{ + LLNotificationsUtil::add("PreferenceControlsDefaults", LLSD(), LLSD(), boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsResponse, this, _1, _2)); +} + +void LLPanelPreferenceControls::onRestoreDefaultsResponse(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch(option) + { + case 0: // All + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + mConflictHandler[i].resetToDefaults(); + // Apply changes to viewer as 'temporary' + mConflictHandler[i].saveToSettings(true); + + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } + + updateTable(); + break; + case 1: // Current + mConflictHandler[mEditingMode].resetToDefaults(); + // Apply changes to viewer as 'temporary' + mConflictHandler[mEditingMode].saveToSettings(true); + + if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON) + { + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } + + updateTable(); + break; + case 2: // Cancel + default: + //exit; + break; + } +} + +// Bypass to let Move & view read values without need to create own key binding handler +// Assumes third person view +// Might be better idea to just move whole mConflictHandler into LLFloaterPreference +bool LLPanelPreferenceControls::canKeyBindHandle(const std::string &control, EMouseClickType click, KEY key, MASK mask) +{ + S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; + if (mConflictHandler[mode].empty()) + { + // opening for first time + mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); + } + + return mConflictHandler[mode].canHandleControl(control, click, key, mask); +} + +// Bypass to let Move & view modify values without need to create own key binding handler +// Assumes third person view +// Might be better idea to just move whole mConflictHandler into LLFloaterPreference +void LLPanelPreferenceControls::setKeyBind(const std::string &control, EMouseClickType click, KEY key, MASK mask, bool set) +{ + S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; + if (mConflictHandler[mode].empty()) + { + // opening for first time + mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); + } + + if (!mConflictHandler[mode].canAssignControl(mEditingControl)) + { + return; + } + + bool already_recorded = mConflictHandler[mode].canHandleControl(control, click, key, mask); + if (set) + { + if (already_recorded) + { + // nothing to do + return; + } + + // find free spot to add data, if no free spot, assign to first + S32 index = 0; + for (S32 i = 0; i < 3; i++) + { + if (mConflictHandler[mode].getControl(control, i).isEmpty()) + { + index = i; + break; + } + } + mConflictHandler[mode].registerControl(control, index, click, key, mask, true); + } + else if (!set) + { + if (!already_recorded) + { + // nothing to do + return; + } + + // find specific control and reset it + for (S32 i = 0; i < 3; i++) + { + LLKeyData data = mConflictHandler[mode].getControl(control, i); + if (data.mMouse == click && data.mKey == key && data.mMask == mask) + { + mConflictHandler[mode].clearControl(control, i); + } + } + } +} + +void LLPanelPreferenceControls::updateAndApply() +{ + S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; + mConflictHandler[mode].saveToSettings(true); + updateTable(); +} + +// from LLSetKeybindDialog's interface +bool LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) +{ + if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) + { + return true; + } + + if ( mEditingColumn > 0) + { + if (all_modes) + { + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].empty()) + { + mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); + } + mConflictHandler[i].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); + // Apply changes to viewer as 'temporary' + mConflictHandler[i].saveToSettings(true); + } + } + else + { + mConflictHandler[mEditingMode].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); + // Apply changes to viewer as 'temporary' + mConflictHandler[mEditingMode].saveToSettings(true); + } + } + + updateTable(); + + if ((mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) + && (mEditingControl == "walk_to" + || mEditingControl == "teleport_to" + || click == CLICK_LEFT + || click == CLICK_DOUBLELEFT)) + { + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } + + return true; +} + +void LLPanelPreferenceControls::onDefaultKeyBind(bool all_modes) +{ + if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) + { + return; + } + + if (mEditingColumn > 0) + { + if (all_modes) + { + for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) + { + if (mConflictHandler[i].empty()) + { + mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); + } + mConflictHandler[i].resetToDefault(mEditingControl, mEditingColumn - 1); + // Apply changes to viewer as 'temporary' + mConflictHandler[i].saveToSettings(true); + } + } + else + { + mConflictHandler[mEditingMode].resetToDefault(mEditingControl, mEditingColumn - 1); + // Apply changes to viewer as 'temporary' + mConflictHandler[mEditingMode].saveToSettings(true); + } + } + updateTable(); + + if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) + { + // notify comboboxes in move&view about potential change + LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); + if (instance) + { + instance->updateClickActionViews(); + } + } +} + +void LLPanelPreferenceControls::onCancelKeyBind() +{ + pControlsTable->deselectAllItems(); +} + LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const LLSD& key) : LLFloater(key) { diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 2a3b075167..2f0bec02e7 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -37,6 +37,8 @@ #include "llavatarpropertiesprocessor.h" #include "llconversationlog.h" #include "llsearcheditor.h" +#include "llsetkeybinddialog.h" +#include "llkeyconflict.h" #include "llaudioengine.h" // Output device selection @@ -45,13 +47,18 @@ class LLPanelPreference; class LLPanelLCD; class LLPanelDebug; class LLMessageSystem; +class LLComboBox; class LLScrollListCtrl; +class LLScrollListCell; class LLSliderCtrl; class LLSD; class LLTextBox; class LLComboBox; class LLLineEditor; +// FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. +class LLFilterEditor; + namespace ll { namespace prefs @@ -80,7 +87,9 @@ public: void apply(); void cancel(); - /*virtual*/ void draw(); + // FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. + // /*virtual*/ void draw(); + // /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void onClose(bool app_quitting); @@ -108,6 +117,8 @@ public: void selectPrivacyPanel(); void selectChatPanel(); void getControlNames(std::vector& names); + // updates click/double-click action controls depending on values from settings.xml + void updateClickActionViews(); // Make onBtnOk() public for settings backup panel //protected: @@ -146,10 +157,9 @@ protected: // callback for commit in the "Single click on land" and "Double click on land" comboboxes. void onClickActionChange(); - // updates click/double-click action settings depending on controls values - void updateClickActionSettings(); - // updates click/double-click action controls depending on values from settings.xml + // updates click/double-click action keybindngs depending on view values void updateClickActionControls(); + // updates UI Sounds controls depending on values from settings.xml void updateUISoundsControls(); @@ -166,7 +176,6 @@ protected: // Group Notices and chiclets location setting conversion BOOL => S32 void onShowGroupNoticesTopRightChanged(); - // Dynamic texture memory calculation void handleDynamicTextureMemoryChanged(); @@ -198,11 +207,6 @@ public: void onClickBrowseSettingsDir(); void onClickSkin(LLUICtrl* ctrl,const LLSD& userdata); void onSelectSkin(); - void onClickSetKey(); - void onClickClearKey(); // FIRE-3803: Clear voice toggle button - void setKey(KEY key); - void setMouse(LLMouseHandler::EClickType click); - void onClickSetMiddleMouse(); // void onClickSetSounds(); // Handled centrally now void onClickPreviewUISound(const LLSD& ui_sound_id); // FIRE-8190: Preview function for "UI Sounds" Panel void setPreprocInclude(); @@ -211,8 +215,12 @@ public: void setExternalEditor(); void changeExternalEditorPath(const std::vector& filenames); // - void onClickEnablePopup(); - void onClickDisablePopup(); + // FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. + // void onClickEnablePopup(); + // void onClickDisablePopup(); + void onSelectPopup(); + void onUpdatePopupFilter(); + // void resetAllIgnored(); void setAllIgnored(); void onClickLogPath(); @@ -271,7 +279,12 @@ public: void applyUIColor(LLUICtrl* ctrl, const LLSD& param); void getUIColor(LLUICtrl* ctrl, const LLSD& param); void onLogChatHistorySaved(); - void buildPopupLists(); + + // FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. + // void buildPopupLists(); + void buildPopupList(); + // + static void refreshSkin(void* data); void selectPanel(const LLSD& name); // Build fix @@ -289,7 +302,6 @@ private: static std::string sSkin; notifications_map mNotificationOptions; - bool mClickActionDirty; ///< Set to true when the click/double-click options get changed by user. bool mGotPersonalInfo; bool mOriginalIMViaEmail; bool mLanguageChanged; @@ -308,6 +320,11 @@ private: void onUpdateFilterTerm( bool force = false ); void collectSearchableItems(); + + // FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search. + LLScrollListCtrl* mPopupList; + LLFilterEditor* mPopupFilter; + // }; class LLPanelPreference : public LLPanel @@ -399,6 +416,60 @@ private: LOG_CLASS(LLPanelPreferenceGraphics); }; +class LLPanelPreferenceControls : public LLPanelPreference, public LLKeyBindResponderInterface +{ + LOG_CLASS(LLPanelPreferenceControls); +public: + LLPanelPreferenceControls(); + virtual ~LLPanelPreferenceControls(); + + BOOL postBuild(); + + void apply(); + void cancel(); + void saveSettings(); + void resetDirtyChilds(); + + void onListCommit(); + void onModeCommit(); + void onRestoreDefaultsBtn(); + void onRestoreDefaultsResponse(const LLSD& notification, const LLSD& response); + + // Bypass to let Move & view read values without need to create own key binding handler + // Todo: consider a better way to share access to keybindings + bool canKeyBindHandle(const std::string &control, EMouseClickType click, KEY key, MASK mask); + // Bypasses to let Move & view modify values without need to create own key binding handler + void setKeyBind(const std::string &control, EMouseClickType click, KEY key, MASK mask, bool set /*set or reset*/ ); + void updateAndApply(); + + // from interface + /*virtual*/ bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); + /*virtual*/ void onDefaultKeyBind(bool all_modes); + /*virtual*/ void onCancelKeyBind(); + +private: + // reloads settings, discards current changes, updates table + void regenerateControls(); + + // These fuctions do not clean previous content + bool addControlTableColumns(const std::string &filename); + bool addControlTableRows(const std::string &filename); + void addControlTableSeparator(); + + // Cleans content and then adds content from xml files according to current mEditingMode + void populateControlTable(); + + // Updates keybindings from storage to table + void updateTable(); + + LLScrollListCtrl* pControlsTable; + LLComboBox *pKeyModeBox; + LLKeyConflictHandler mConflictHandler[LLKeyConflictHandler::MODE_COUNT]; + std::string mEditingControl; + S32 mEditingColumn; + S32 mEditingMode; +}; + class LLFloaterPreferenceGraphicsAdvanced : public LLFloater { public: diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp new file mode 100644 index 0000000000..b6107eeedf --- /dev/null +++ b/indra/newview/llkeyconflict.cpp @@ -0,0 +1,1015 @@ +/** + * @file llkeyconflict.cpp + * @brief + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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$ + */ + +/* + * App-wide preferences. Note that these are not per-user, + * because we need to load many preferences before we have + * a login name. + */ + +#include "llviewerprecompiledheaders.h" + +#include "llkeyconflict.h" + +#include "llinitparam.h" +#include "llkeyboard.h" +#include "lltrans.h" +#include "llviewercontrol.h" +#include "llviewerinput.h" +#include "llviewermenu.h" +#include "llxuiparser.h" + +static const std::string saved_settings_key_controls[] = { "placeholder" }; // add settings from gSavedSettings here + +static const std::string filename_default = "key_bindings.xml"; +static const std::string filename_temporary = "key_bindings_tmp.xml"; // used to apply uncommited changes on the go. + +// LLKeyboard::stringFromMask is meant for UI and is OS dependent, +// so this class uses it's own version +std::string string_from_mask(MASK mask) +{ + std::string res; + if ((mask & MASK_CONTROL) != 0) + { + res = "CTL"; + } + if ((mask & MASK_ALT) != 0) + { + if (!res.empty()) res += "_"; + res += "ALT"; + } + if ((mask & MASK_SHIFT) != 0) + { + if (!res.empty()) res += "_"; + res += "SHIFT"; + } + + if (mask == MASK_NONE) + { + res = "NONE"; + } + return res; +} + +std::string string_from_mouse(EMouseClickType click, bool translate) +{ + std::string res; + switch (click) + { + case CLICK_LEFT: + res = "LMB"; + break; + case CLICK_MIDDLE: + res = "MMB"; + break; + case CLICK_RIGHT: + res = "RMB"; + break; + case CLICK_BUTTON4: + res = "MB4"; + break; + case CLICK_BUTTON5: + res = "MB5"; + break; + case CLICK_DOUBLELEFT: + res = "Double LMB"; + break; + default: + break; + } + + if (translate && !res.empty()) + { + res = LLTrans::getString(res); + } + return res; +} + +// LLKeyConflictHandler + +S32 LLKeyConflictHandler::sTemporaryFileUseCount = 0; + +LLKeyConflictHandler::LLKeyConflictHandler() +: mHasUnsavedChanges(false), + mUsesTemporaryFile(false), + mLoadMode(MODE_COUNT) +{ +} + +LLKeyConflictHandler::LLKeyConflictHandler(ESourceMode mode) +: mHasUnsavedChanges(false), + mUsesTemporaryFile(false), + mLoadMode(mode) +{ + loadFromSettings(mode); +} + +LLKeyConflictHandler::~LLKeyConflictHandler() +{ + clearUnsavedChanges(); + // Note: does not reset bindings if temporary file was used +} + +bool LLKeyConflictHandler::canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask) +{ + return mControlsMap[control_name].canHandle(mouse_ind, key, mask); +} + +bool LLKeyConflictHandler::canHandleKey(const std::string &control_name, KEY key, MASK mask) +{ + return canHandleControl(control_name, CLICK_NONE, key, mask); +} + +bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask) +{ + return canHandleControl(control_name, mouse_ind, KEY_NONE, mask); +} + +bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask) +{ + return canHandleControl(control_name, (EMouseClickType)mouse_ind, KEY_NONE, mask); +} + +bool LLKeyConflictHandler::canAssignControl(const std::string &control_name) +{ + control_map_t::iterator iter = mControlsMap.find(control_name); + if (iter != mControlsMap.end()) + { + return iter->second.mAssignable; + } + // If we don't know this control, means it wasn't assigned by user yet and thus is editable + return true; +} + +// static +bool LLKeyConflictHandler::isReservedByMenu(const KEY &key, const MASK &mask) +{ + if (key == KEY_NONE) + { + return false; + } + return (gMenuBarView && gMenuBarView->hasAccelerator(key, mask)) + || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(key, mask)); +} + +// static +bool LLKeyConflictHandler::isReservedByMenu(const LLKeyData &data) +{ + if (data.mMouse != CLICK_NONE || data.mKey == KEY_NONE) + { + return false; + } + return (gMenuBarView && gMenuBarView->hasAccelerator(data.mKey, data.mMask)) + || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(data.mKey, data.mMask)); +} + +bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask) +{ + if (control_name.empty()) + { + return false; + } + LLKeyConflict &type_data = mControlsMap[control_name]; + if (!type_data.mAssignable) + { + // Example: user tried to assign camera spin to all modes, but first person mode doesn't support it + return false; + } + LLKeyData data(mouse, key, mask, ignore_mask); + if (type_data.mKeyBind.getKeyData(index) == data) + { + return true; + } + if (isReservedByMenu(data)) + { + return false; + } + if (removeConflicts(data, type_data.mConflictMask)) + { + type_data.mKeyBind.replaceKeyData(data, index); + mHasUnsavedChanges = true; + return true; + } + // control already in use/blocked + return false; +} + +bool LLKeyConflictHandler::clearControl(const std::string &control_name, U32 data_index) +{ + if (control_name.empty()) + { + return false; + } + LLKeyConflict &type_data = mControlsMap[control_name]; + if (!type_data.mAssignable) + { + // Example: user tried to assign camera spin to all modes, but first person mode doesn't support it + return false; + } + type_data.mKeyBind.resetKeyData(data_index); + mHasUnsavedChanges = true; + return true; +} + +LLKeyData LLKeyConflictHandler::getControl(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return LLKeyData(); + } + return mControlsMap[control_name].getKeyData(index); +} + +bool LLKeyConflictHandler::isControlEmpty(const std::string &control_name) +{ + if (control_name.empty()) + { + return true; + } + return mControlsMap[control_name].mKeyBind.isEmpty(); +} + +// static +std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata) +{ + std::string result; + + if (keydata.mMask != MASK_NONE && keydata.mKey != KEY_NONE) + { + result = LLKeyboard::stringFromAccelerator(keydata.mMask, keydata.mKey); + } + else if (keydata.mKey != KEY_NONE) + { + result = LLKeyboard::stringFromKey(keydata.mKey); + } + else if (keydata.mMask != MASK_NONE) + { + result = LLKeyboard::stringFromAccelerator(keydata.mMask); + } + + result += string_from_mouse(keydata.mMouse, true); + + return result; +} + +std::string LLKeyConflictHandler::getControlString(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return ""; + } + return getStringFromKeyData(mControlsMap[control_name].getKeyData(index)); +} + +void LLKeyConflictHandler::loadFromControlSettings(const std::string &name) +{ + LLControlVariablePtr var = gSavedSettings.getControl(name); + if (var) + { + LLKeyBind bind(var->getValue()); + LLKeyConflict key(bind, true, 0); + mControlsMap[name] = key; + } +} + +void LLKeyConflictHandler::loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination) +{ + for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), + end_it = keymode.bindings.end(); + it != end_it; + ++it) + { + KEY key; + MASK mask; + EMouseClickType mouse = CLICK_NONE; + if (it->mouse.isProvided()) + { + LLViewerInput::mouseFromString(it->mouse.getValue(), &mouse); + } + if (it->key.getValue().empty()) + { + key = KEY_NONE; + } + else + { + LLKeyboard::keyFromString(it->key, &key); + } + LLKeyboard::maskFromString(it->mask, &mask); + // Note: it->command is also the name of UI element, howhever xml we are loading from + // might not know all the commands, so UI will have to know what to fill by its own + // Assumes U32_MAX conflict mask, and is assignable by default, + // but assignability might have been overriden by generatePlaceholders. + LLKeyConflict &type_data = (*destination)[it->command]; + type_data.mKeyBind.addKeyData(mouse, key, mask, true); + } +} + +bool LLKeyConflictHandler::loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination) +{ + if (filename.empty()) + { + return false; + } + + bool res = false; + + LLViewerInput::Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + switch (load_mode) + { + case MODE_FIRST_PERSON: + if (keys.first_person.isProvided()) + { + loadFromSettings(keys.first_person, destination); + res = true; + } + break; + case MODE_THIRD_PERSON: + if (keys.third_person.isProvided()) + { + loadFromSettings(keys.third_person, destination); + res = true; + } + break; + case MODE_EDIT_AVATAR: + if (keys.edit_avatar.isProvided()) + { + loadFromSettings(keys.edit_avatar, destination); + res = true; + } + break; + case MODE_SITTING: + if (keys.sitting.isProvided()) + { + loadFromSettings(keys.sitting, destination); + res = true; + } + break; + default: + LL_ERRS() << "Not implememted mode " << load_mode << LL_ENDL; + break; + } + } + return res; +} + +void LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode) +{ + mControlsMap.clear(); + mDefaultsMap.clear(); + + // E.X. In case we need placeholder keys for conflict resolution. + generatePlaceholders(load_mode); + + if (load_mode == MODE_SAVED_SETTINGS) + { + // load settings clss knows about, but it also possible to load settings by name separately + const S32 size = std::extent::value; + for (U32 i = 0; i < size; i++) + { + loadFromControlSettings(saved_settings_key_controls[i]); + } + } + else + { + // load defaults + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); + if (!loadFromSettings(load_mode, filename, &mDefaultsMap)) + { + LL_WARNS() << "Failed to load default settings, aborting" << LL_ENDL; + return; + } + + // load user's + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); + if (!gDirUtilp->fileExists(filename) || !loadFromSettings(load_mode, filename, &mControlsMap)) + { + // mind placeholders + mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); + } + } + mLoadMode = load_mode; +} + +void LLKeyConflictHandler::saveToSettings(bool temporary) +{ + if (mControlsMap.empty()) + { + return; + } + + if (mLoadMode == MODE_SAVED_SETTINGS) + { + // Does not support 'temporary', preferences handle that themself + // so in case of saved settings we just do not clear mHasUnsavedChanges + control_map_t::iterator iter = mControlsMap.begin(); + control_map_t::iterator end = mControlsMap.end(); + + for (; iter != end; ++iter) + { + if (iter->first.empty()) + { + continue; + } + + LLKeyConflict &key = iter->second; + key.mKeyBind.trimEmpty(); + if (!key.mAssignable) + { + continue; + } + + if (gSavedSettings.controlExists(iter->first)) + { + gSavedSettings.setLLSD(iter->first, key.mKeyBind.asLLSD()); + } + else if (!key.mKeyBind.empty()) + { + // Note: this is currently not in use, might be better for load mechanics to ask for and retain control group + // otherwise settings loaded from other control groups will end in gSavedSettings + LL_INFOS() << "Creating new keybinding " << iter->first << LL_ENDL; + gSavedSettings.declareLLSD(iter->first, key.mKeyBind.asLLSD(), "comment", LLControlVariable::PERSIST_ALWAYS); + } + } + } + else + { + // Determine what file to load and load full copy of that file + std::string filename; + + if (temporary) + { + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); + if (!gDirUtilp->fileExists(filename)) + { + filename.clear(); + } + } + + if (filename.empty()) + { + filename = gDirUtilp->findFile(filename_default, + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + } + + LLViewerInput::Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + // replace category we edited + + // mode is a HACK to correctly reset bindings without reparsing whole file and avoid doing + // own param container (which will face issues with inasseesible members of LLInitParam) + LLViewerInput::KeyMode mode; + LLViewerInput::KeyBinding binding; + + control_map_t::iterator iter = mControlsMap.begin(); + control_map_t::iterator end = mControlsMap.end(); + for (; iter != end; ++iter) + { + // By default xml have (had) up to 6 elements per function + // eventually it will be cleaned up and UI will only shows 3 per function, + // so make sure to cleanup. + // Also this helps in keeping file small. + iter->second.mKeyBind.trimEmpty(); + U32 size = iter->second.mKeyBind.getDataCount(); + for (U32 i = 0; i < size; ++i) + { + if (iter->first.empty()) + { + continue; + } + + LLKeyConflict &key = iter->second; + key.mKeyBind.trimEmpty(); + if (key.mKeyBind.empty() || !key.mAssignable) + { + continue; + } + + LLKeyData data = key.mKeyBind.getKeyData(i); + // Still write empty LLKeyData to make sure we will maintain UI position + if (data.mKey == KEY_NONE) + { + // Might be better idea to be consistent and use NONE. LLViewerInput can work with both cases + binding.key = ""; + } + else + { + binding.key = LLKeyboard::stringFromKey(data.mKey, false /*Do not localize*/); + } + binding.mask = string_from_mask(data.mMask); + if (data.mMouse == CLICK_NONE) + { + binding.mouse.setProvided(false); + } + else + { + // set() because 'optional', for compatibility purposes + // just copy old keys.xml and rename to key_bindings.xml, it should work + binding.mouse.set(string_from_mouse(data.mMouse, false), true); + } + binding.command = iter->first; + mode.bindings.add(binding); + } + } + + switch (mLoadMode) + { + case MODE_FIRST_PERSON: + if (keys.first_person.isProvided()) + { + keys.first_person.bindings.set(mode.bindings, true); + } + break; + case MODE_THIRD_PERSON: + if (keys.third_person.isProvided()) + { + keys.third_person.bindings.set(mode.bindings, true); + } + break; + case MODE_EDIT_AVATAR: + if (keys.edit_avatar.isProvided()) + { + keys.edit_avatar.bindings.set(mode.bindings, true); + } + break; + case MODE_SITTING: + if (keys.sitting.isProvided()) + { + keys.sitting.bindings.set(mode.bindings, true); + } + break; + default: + LL_ERRS() << "Not implememted mode " << mLoadMode << LL_ENDL; + break; + } + + if (temporary) + { + // write to temporary xml and use it for gViewerInput + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); + if (!mUsesTemporaryFile) + { + mUsesTemporaryFile = true; + sTemporaryFileUseCount++; + } + } + else + { + // write back to user's xml and use it for gViewerInput + filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); + // Don't reset mUsesTemporaryFile, it will be reset at cleanup stage + } + + LLXMLNodePtr output_node = new LLXMLNode("keys", false); + LLXUIParser parser; + parser.writeXUI(output_node, keys); + + // Write the resulting XML to file + if (!output_node->isNull()) + { + LLFILE *fp = LLFile::fopen(filename, "w"); + if (fp != NULL) + { + LLXMLNode::writeHeaderToFile(fp); + output_node->writeToFile(fp); + fclose(fp); + } + } + // Now force a rebind for keyboard + if (gDirUtilp->fileExists(filename)) + { + // Ideally instead of rebinding immediately we should shedule + // the rebind since single file can have multiple handlers, + // one per mode, saving simultaneously. + // Or whatever uses LLKeyConflictHandler should control the process. + gViewerInput.loadBindingsXML(filename); + } + } + } + +#if 1 + // Legacy support + // Remove #if-#endif section half a year after DRTVWR-501 releases. + // Update legacy settings in settings.xml + // We only care for third person view since legacy settings can't store + // more than one mode. + // We are saving this even if we are in temporary mode - preferences + // will restore values on cancel + if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) + { + bool value = canHandleMouse("walk_to", CLICK_DOUBLELEFT, MASK_NONE); + gSavedSettings.setBOOL("DoubleClickAutoPilot", value); + + value = canHandleMouse("walk_to", CLICK_LEFT, MASK_NONE); + gSavedSettings.setBOOL("ClickToWalk", value); + + // new method can save both toggle and push-to-talk values simultaneously, + // but legacy one can save only one. It also doesn't support mask. + LLKeyData data = getControl("toggle_voice", 0); + bool can_toggle = !data.isEmpty(); + if (!can_toggle) + { + data = getControl("voice_follow_key", 0); + } + + gSavedSettings.setBOOL("PushToTalkToggle", can_toggle); + if (data.isEmpty()) + { + // legacy viewer has a bug that might crash it if NONE value is assigned. + // just reset to default + gSavedSettings.getControl("PushToTalkButton")->resetToDefault(false); + } + else + { + if (data.mKey != KEY_NONE) + { + gSavedSettings.setString("PushToTalkButton", LLKeyboard::stringFromKey(data.mKey)); + } + else + { + std::string ctrl_value; + switch (data.mMouse) + { + case CLICK_MIDDLE: + ctrl_value = "MiddleMouse"; + break; + case CLICK_BUTTON4: + ctrl_value = "MouseButton4"; + break; + case CLICK_BUTTON5: + ctrl_value = "MouseButton5"; + break; + default: + ctrl_value = "MiddleMouse"; + break; + } + gSavedSettings.setString("PushToTalkButton", ctrl_value); + } + } + } +#endif + + if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) + { + // Map floater should react to doubleclick if doubleclick for teleport is set + // Todo: Seems conterintuitive for map floater to share inworld controls + // after these changes release, discuss with UI UX engineer if this should just + // be set to 1 by default (before release this also doubles as legacy support) + bool value = canHandleMouse("teleport_to", CLICK_DOUBLELEFT, MASK_NONE); + gSavedSettings.setBOOL("DoubleClickTeleport", value); + } + + if (!temporary) + { + // will remove any temporary file if there were any + clearUnsavedChanges(); + } +} + +LLKeyData LLKeyConflictHandler::getDefaultControl(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return LLKeyData(); + } + if (mLoadMode == MODE_SAVED_SETTINGS) + { + LLControlVariablePtr var = gSavedSettings.getControl(control_name); + if (var) + { + return LLKeyBind(var->getDefault()).getKeyData(index); + } + return LLKeyData(); + } + else + { + control_map_t::iterator iter = mDefaultsMap.find(control_name); + if (iter != mDefaultsMap.end()) + { + return iter->second.mKeyBind.getKeyData(index); + } + return LLKeyData(); + } +} + +void LLKeyConflictHandler::resetToDefault(const std::string &control_name, U32 index) +{ + if (control_name.empty()) + { + return; + } + LLKeyData data = getDefaultControl(control_name, index); + + if (data != mControlsMap[control_name].getKeyData(index)) + { + // reset controls that might have been switched to our current control + removeConflicts(data, mControlsMap[control_name].mConflictMask); + mControlsMap[control_name].setKeyData(data, index); + } +} + +void LLKeyConflictHandler::resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts) +{ + if (control_name.empty()) + { + return; + } + if (mLoadMode == MODE_SAVED_SETTINGS) + { + LLControlVariablePtr var = gSavedSettings.getControl(control_name); + if (var) + { + LLKeyBind bind(var->getDefault()); + if (!ignore_conflicts) + { + for (S32 i = 0; i < bind.getDataCount(); ++i) + { + removeConflicts(bind.getKeyData(i), mControlsMap[control_name].mConflictMask); + } + } + mControlsMap[control_name].mKeyBind = bind; + } + else + { + mControlsMap[control_name].mKeyBind.clear(); + } + } + else + { + control_map_t::iterator iter = mDefaultsMap.find(control_name); + if (iter != mDefaultsMap.end()) + { + if (!ignore_conflicts) + { + for (S32 i = 0; i < iter->second.mKeyBind.getDataCount(); ++i) + { + removeConflicts(iter->second.mKeyBind.getKeyData(i), mControlsMap[control_name].mConflictMask); + } + } + mControlsMap[control_name].mKeyBind = iter->second.mKeyBind; + } + else + { + mControlsMap[control_name].mKeyBind.clear(); + } + } +} + +void LLKeyConflictHandler::resetToDefault(const std::string &control_name) +{ + // reset specific binding without ignoring conflicts + resetToDefaultAndResolve(control_name, false); +} + +void LLKeyConflictHandler::resetToDefaults(ESourceMode mode) +{ + if (mode == MODE_SAVED_SETTINGS) + { + control_map_t::iterator iter = mControlsMap.begin(); + control_map_t::iterator end = mControlsMap.end(); + + for (; iter != end; ++iter) + { + resetToDefaultAndResolve(iter->first, true); + } + } + else + { + mControlsMap.clear(); + generatePlaceholders(mode); + mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); + } + + mHasUnsavedChanges = true; +} + +void LLKeyConflictHandler::resetToDefaults() +{ + if (!empty()) + { + resetToDefaults(mLoadMode); + } + else + { + // not optimal since: + // 1. We are not sure that mLoadMode was set + // 2. We are not sure if there are any changes in comparison to default + // 3. We are loading 'current' only to replace it + // but it is reliable and works Todo: consider optimizing. + loadFromSettings(mLoadMode); + resetToDefaults(mLoadMode); + } +} + +void LLKeyConflictHandler::clear() +{ + if (clearUnsavedChanges()) + { + // temporary file was removed, this means we were using it and need to reload keyboard's bindings + resetKeyboardBindings(); + } + mControlsMap.clear(); + mDefaultsMap.clear(); +} + +// static +void LLKeyConflictHandler::resetKeyboardBindings() +{ + // Try to load User's bindings first + std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); + if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) + { + // Failed to load custom bindings, try default ones + key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); + if (!gViewerInput.loadBindingsXML(key_bindings_file)) + { + LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; + } + } +} + +void LLKeyConflictHandler::generatePlaceholders(ESourceMode load_mode) +{ + // These controls are meant to cause conflicts when user tries to assign same control somewhere else + // also this can be used to pre-record controls that should not conflict or to assign conflict groups/masks + + if (load_mode == MODE_FIRST_PERSON) + { + // First person view doesn't support camera controls + // Note: might be better idea to just load these from control_table_contents_camera.xml + // or to pass from floaterpreferences when it loads said file + registerTemporaryControl("look_up"); + registerTemporaryControl("look_down"); + registerTemporaryControl("move_forward"); + registerTemporaryControl("move_backward"); + registerTemporaryControl("move_forward_fast"); + registerTemporaryControl("move_backward_fast"); + registerTemporaryControl("spin_over"); + registerTemporaryControl("spin_under"); + registerTemporaryControl("pan_up"); + registerTemporaryControl("pan_down"); + registerTemporaryControl("pan_left"); + registerTemporaryControl("pan_right"); + registerTemporaryControl("pan_in"); + registerTemporaryControl("pan_out"); + registerTemporaryControl("spin_around_ccw"); + registerTemporaryControl("spin_around_cw"); + + // control_table_contents_editing.xml + registerTemporaryControl("edit_avatar_spin_ccw"); + registerTemporaryControl("edit_avatar_spin_cw"); + registerTemporaryControl("edit_avatar_spin_over"); + registerTemporaryControl("edit_avatar_spin_under"); + registerTemporaryControl("edit_avatar_move_forward"); + registerTemporaryControl("edit_avatar_move_backward"); + + // no autopilot or teleport + registerTemporaryControl("walk_to"); + registerTemporaryControl("teleport_to"); + } + + if (load_mode == MODE_EDIT_AVATAR) + { + // no autopilot or teleport + registerTemporaryControl("walk_to"); + registerTemporaryControl("teleport_to"); + } + + if (load_mode == MODE_SITTING) + { + // no autopilot + registerTemporaryControl("walk_to"); + } + else + { + // sitting related functions should only be avaliable in sitting mode + registerTemporaryControl("move_forward_sitting"); + registerTemporaryControl("move_backward_sitting"); + registerTemporaryControl("spin_over_sitting"); + registerTemporaryControl("spin_under_sitting"); + registerTemporaryControl("spin_around_ccw_sitting"); + registerTemporaryControl("spin_around_cw_sitting"); + } +} + +bool LLKeyConflictHandler::removeConflicts(const LLKeyData &data, const U32 &conlict_mask) +{ + if (conlict_mask == CONFLICT_NOTHING) + { + // Can't conflict + return true; + } + std::map conflict_list; + control_map_t::iterator cntrl_iter = mControlsMap.begin(); + control_map_t::iterator cntrl_end = mControlsMap.end(); + for (; cntrl_iter != cntrl_end; ++cntrl_iter) + { + S32 index = cntrl_iter->second.mKeyBind.findKeyData(data); + if (index >= 0 + && cntrl_iter->second.mConflictMask != CONFLICT_NOTHING + && (cntrl_iter->second.mConflictMask & conlict_mask) != 0) + { + if (cntrl_iter->second.mAssignable) + { + // Potentially we can have multiple conflict flags conflicting + // including unassignable keys. + // So record the conflict and find all others before doing any changes. + // Assume that there is only one conflict per bind + conflict_list[cntrl_iter->first] = index; + } + else + { + return false; + } + } + } + + std::map::iterator cnflct_iter = conflict_list.begin(); + std::map::iterator cnflct_end = conflict_list.end(); + for (; cnflct_iter != cnflct_end; ++cnflct_iter) + { + mControlsMap[cnflct_iter->first].mKeyBind.resetKeyData(cnflct_iter->second); + } + return true; +} + +void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask) +{ + LLKeyConflict *type_data = &mControlsMap[control_name]; + type_data->mAssignable = false; + type_data->mConflictMask = conflict_mask; + type_data->mKeyBind.addKeyData(mouse, key, mask, false); +} + +void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, U32 conflict_mask) +{ + LLKeyConflict *type_data = &mControlsMap[control_name]; + type_data->mAssignable = false; + type_data->mConflictMask = conflict_mask; +} + +bool LLKeyConflictHandler::clearUnsavedChanges() +{ + bool result = false; + mHasUnsavedChanges = false; + + if (mUsesTemporaryFile) + { + mUsesTemporaryFile = false; + sTemporaryFileUseCount--; + if (!sTemporaryFileUseCount) + { + result = clearTemporaryFile(); + } + // else: might be usefull to overwrite content of temp file with defaults + // but at the moment there is no such need + } + return result; +} + +//static +bool LLKeyConflictHandler::clearTemporaryFile() +{ + // At the moment single file needs five handlers (one per mode), so doing this + // will remove file for all hadlers + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); + if (gDirUtilp->fileExists(filename)) + { + LLFile::remove(filename); + return true; + } + return false; +} + diff --git a/indra/newview/llkeyconflict.h b/indra/newview/llkeyconflict.h new file mode 100644 index 0000000000..2926ca3aeb --- /dev/null +++ b/indra/newview/llkeyconflict.h @@ -0,0 +1,175 @@ +/** + * @file llkeyconflict.h + * @brief + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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_LLKEYCONFLICT_H +#define LL_LLKEYCONFLICT_H + +#include "llkeybind.h" +#include "llviewerinput.h" + + +class LLKeyConflict +{ +public: + LLKeyConflict() : mAssignable(true), mConflictMask(U32_MAX) {} //temporary assignable, don't forget to change once all keys are recorded + LLKeyConflict(bool assignable, U32 conflict_mask) + : mAssignable(assignable), mConflictMask(conflict_mask) {} + LLKeyConflict(const LLKeyBind &bind, bool assignable, U32 conflict_mask) + : mAssignable(assignable), mConflictMask(conflict_mask), mKeyBind(bind) {} + + LLKeyData getPrimaryKeyData() { return mKeyBind.getKeyData(0); } + LLKeyData getKeyData(U32 index) { return mKeyBind.getKeyData(index); } + void setPrimaryKeyData(const LLKeyData& data) { mKeyBind.replaceKeyData(data, 0); } + void setKeyData(const LLKeyData& data, U32 index) { mKeyBind.replaceKeyData(data, index); } + bool canHandle(EMouseClickType mouse, KEY key, MASK mask) { return mKeyBind.canHandle(mouse, key, mask); } + + LLKeyBind mKeyBind; + bool mAssignable; // whether user can change key or key simply acts as placeholder + U32 mConflictMask; +}; + +class LLKeyConflictHandler +{ +public: + + enum ESourceMode // partially repeats e_keyboard_mode + { + MODE_FIRST_PERSON, + MODE_THIRD_PERSON, + MODE_EDIT_AVATAR, + MODE_SITTING, + MODE_SAVED_SETTINGS, // for settings from saved settings + MODE_COUNT + }; + + const U32 CONFLICT_NOTHING = 0; + // at the moment this just means that key will conflict with everything that is identical + const U32 CONFLICT_ANY = U32_MAX; + + // Note: missed selection and edition commands (would be really nice to go through selection via MB4/5 or wheel) + + LLKeyConflictHandler(); + LLKeyConflictHandler(ESourceMode mode); + ~LLKeyConflictHandler(); + + bool canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask); + bool canHandleKey(const std::string &control_name, KEY key, MASK mask); + bool canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask); + bool canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask); //Just for convinience + bool canAssignControl(const std::string &control_name); + static bool isReservedByMenu(const KEY &key, const MASK &mask); + static bool isReservedByMenu(const LLKeyData &data); + + // @control_name - see REGISTER_KEYBOARD_ACTION in llviewerinput for avaliable options, + // usually this is just name of the function + // @data_index - single control (function) can have multiple key combinations trigering + // it, this index indicates combination function will change/add; Note that preferences + // floater can only display up to 3 options, but data_index can be bigger than that + // @mouse_ind - mouse action (middle click, MB5 etc) + // @key - keyboard key action + // @mask - shift/ctrl/alt flags + // @ignore_mask - Either to expect exact match (ctrl+K will not trigger if ctrl+shift+K + // is active) or ignore not expected masks as long as expected mask is present + // (ctrl+K will be triggered if ctrl+shift+K is active) + bool registerControl(const std::string &control_name, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask, bool ignore_mask); //todo: return conflicts? + bool clearControl(const std::string &control_name, U32 data_index); + + LLKeyData getControl(const std::string &control_name, U32 data_index); + bool isControlEmpty(const std::string &control_name); + + // localized string + static std::string getStringFromKeyData(const LLKeyData& keydata); + std::string getControlString(const std::string &control_name, U32 data_index); + + // Load single control, overrides existing one if names match + void loadFromControlSettings(const std::string &name); + // Drops any changes loads controls with ones from 'saved settings' or from xml + void loadFromSettings(ESourceMode load_mode); + + // Saves settings to 'saved settings' or to xml + // If 'temporary' is set, function will save settings to temporary + // file and reload input bindings from temporary file. + // 'temporary' does not support gSavedSettings, those are handled + // by preferences, so 'temporary' is such case will simply not + // reset mHasUnsavedChanges + // + // 'temporary' exists to support ability of live-editing settings in + // preferences: temporary for testing changes 'live' without saving them, + // then hitting ok/cancel and save/discard values permanently. + void saveToSettings(bool apply_temporary = false); + + LLKeyData getDefaultControl(const std::string &control_name, U32 data_index); + // Resets keybinding to default variant from 'saved settings' or xml + void resetToDefault(const std::string &control_name, U32 index); + void resetToDefault(const std::string &control_name); + // resets current mode to defaults + void resetToDefaults(); + + bool empty() { return mControlsMap.empty(); } + void clear(); + + // reloads bindings from last valid user's xml or from default xml + // to keyboard's handler + static void resetKeyboardBindings(); + + bool hasUnsavedChanges() { return mHasUnsavedChanges; } + void setLoadMode(ESourceMode mode) { mLoadMode = mode; } + ESourceMode getLoadMode() { return mLoadMode; } + +private: + void resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts); + void resetToDefaults(ESourceMode mode); + + // at the moment these kind of control is not savable, but takes part in conflict resolution + void registerTemporaryControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask, U32 conflict_mask); + void registerTemporaryControl(const std::string &control_name, U32 conflict_mask = 0); + + typedef std::map control_map_t; + void loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination); + bool loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination); + void generatePlaceholders(ESourceMode load_mode); //E.x. non-assignable values + // returns false in case user is trying to reuse control that can't be reassigned + bool removeConflicts(const LLKeyData &data, const U32 &conlict_mask); + + // removes flags and removes temporary file, returns 'true' if file was removed + bool clearUnsavedChanges(); + // return true if there was a file to remove + static bool clearTemporaryFile(); + + control_map_t mControlsMap; + control_map_t mDefaultsMap; + bool mHasUnsavedChanges; + ESourceMode mLoadMode; + + // To implement 'apply immediately'+revert on cancel, class applies changes to temporary file + // but this only works for settings from keybndings files (key_bindings.xml) + // saved setting rely onto external mechanism of preferences floater + bool mUsesTemporaryFile; + static S32 sTemporaryFileUseCount; +}; + + +#endif // LL_LLKEYCONFLICT_H diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 56f20c7341..c47a1cd040 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -373,11 +373,11 @@ BOOL LLPreviewGesture::postBuild() mReplaceEditor = edit; combo = getChild( "modifier_combo"); - combo->setCommitCallback(onCommitSetDirty, this); + combo->setCommitCallback(boost::bind(&LLPreviewGesture::onCommitKeyorModifier, this)); mModifierCombo = combo; combo = getChild( "key_combo"); - combo->setCommitCallback(onCommitSetDirty, this); + combo->setCommitCallback(boost::bind(&LLPreviewGesture::onCommitKeyorModifier, this)); mKeyCombo = combo; list = getChild("library_list"); @@ -944,12 +944,18 @@ void LLPreviewGesture::loadUIFromGesture(LLMultiGesture* gesture) break; } + // Allow CTRL-F10 shortcut for gestures + //mModifierCombo->setEnabledByValue(CTRL_LABEL, gesture->mKey != KEY_F10); + mKeyCombo->setCurrentByIndex(0); if (gesture->mKey != KEY_NONE) { mKeyCombo->setSimple(LLKeyboard::stringFromKey(gesture->mKey)); } + // Allow CTRL-F10 shortcut for gestures + //mKeyCombo->setEnabledByValue(LLKeyboard::stringFromKey(KEY_F10), gesture->mMask != MASK_CONTROL); + // Make UI steps for each gesture step S32 i; S32 count = gesture->mSteps.size(); @@ -1344,6 +1350,19 @@ LLMultiGesture* LLPreviewGesture::createGesture() } +void LLPreviewGesture::onCommitKeyorModifier() +{ + // SL-14139: ctrl-F10 is currently used to access top menu, + // so don't allow to bound gestures to this combination. + + // Allow CTRL-F10 shortcut for gestures + //mKeyCombo->setEnabledByValue(LLKeyboard::stringFromKey(KEY_F10), mModifierCombo->getSimple() != CTRL_LABEL); + //mModifierCombo->setEnabledByValue(CTRL_LABEL, mKeyCombo->getSimple() != LLKeyboard::stringFromKey(KEY_F10)); + // + mDirty = TRUE; + refresh(); +} + // static void LLPreviewGesture::updateLabel(LLScrollListItem* item) { diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h index 19bccf35bd..f5c47d71b8 100644 --- a/indra/newview/llpreviewgesture.h +++ b/indra/newview/llpreviewgesture.h @@ -101,6 +101,8 @@ protected: LLScrollListItem* addStep(const enum EStepType step_type); void onVisibilityChanged ( const LLSD& new_visibility ); + + void onCommitKeyorModifier(); static std::string getLabel(std::vector labels); static void updateLabel(LLScrollListItem* item); diff --git a/indra/newview/llsetkeybinddialog.cpp b/indra/newview/llsetkeybinddialog.cpp new file mode 100644 index 0000000000..4b36822e9a --- /dev/null +++ b/indra/newview/llsetkeybinddialog.cpp @@ -0,0 +1,380 @@ +/** + * @file llsetkeybinddialog.cpp + * @brief LLSetKeyBindDialog class implementation. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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 "llsetkeybinddialog.h" + +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "lleventtimer.h" +#include "llfloaterreg.h" +#include "llfocusmgr.h" +#include "llkeyconflict.h" +#include "llviewercontrol.h" + +class LLSetKeyBindDialog::Updater : public LLEventTimer +{ +public: + + typedef boost::function callback_t; + + Updater(callback_t cb, F32 period, MASK mask) + :LLEventTimer(period), + mMask(mask), + mCallback(cb) + { + mEventTimer.start(); + } + + virtual ~Updater(){} + +protected: + BOOL tick() + { + mCallback(mMask); + // Deletes itseft after execution + return TRUE; + } + +private: + MASK mMask; + callback_t mCallback; +}; + +bool LLSetKeyBindDialog::sRecordKeys = false; + +LLSetKeyBindDialog::LLSetKeyBindDialog(const LLSD& key) + : LLModalDialog(key), + pParent(NULL), + mKeyFilterMask(DEFAULT_KEY_FILTER), + pUpdater(NULL), + mLastMaskKey(0), + mContextConeOpacity(0.f), + mContextConeInAlpha(0.f), + mContextConeOutAlpha(0.f), + mContextConeFadeTime(0.f) +{ + mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha"); + mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha"); + mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime"); +} + +LLSetKeyBindDialog::~LLSetKeyBindDialog() +{ +} + +//virtual +BOOL LLSetKeyBindDialog::postBuild() +{ + childSetAction("SetEmpty", onBlank, this); + childSetAction("Default", onDefault, this); + childSetAction("Cancel", onCancel, this); + getChild("Cancel")->setFocus(TRUE); + + pCheckBox = getChild("apply_all"); + pDesription = getChild("descritption"); + + gFocusMgr.setKeystrokesOnly(TRUE); + + return TRUE; +} + +//virtual +void LLSetKeyBindDialog::onOpen(const LLSD& data) +{ + sRecordKeys = true; + LLModalDialog::onOpen(data); +} + +//virtual +void LLSetKeyBindDialog::onClose(bool app_quiting) +{ + sRecordKeys = false; + if (pParent) + { + pParent->onCancelKeyBind(); + pParent = NULL; + } + if (pUpdater) + { + // Doubleclick timer has't fired, delete it + delete pUpdater; + pUpdater = NULL; + } + LLModalDialog::onClose(app_quiting); +} + +void LLSetKeyBindDialog::drawFrustum() +{ + static LLCachedControl max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); + drawConeToOwner(mContextConeOpacity, max_opacity, mFrustumOrigin.get(), mContextConeFadeTime, mContextConeInAlpha, mContextConeOutAlpha); +} + +//virtual +void LLSetKeyBindDialog::draw() +{ + drawFrustum(); + LLModalDialog::draw(); +} + +void LLSetKeyBindDialog::setParent(LLKeyBindResponderInterface* parent, LLView* frustum_origin, U32 key_mask) +{ + pParent = parent; + mFrustumOrigin = frustum_origin->getHandle(); + mKeyFilterMask = key_mask; + + std::string input; + if ((key_mask & ALLOW_MOUSE) != 0) + { + input = getString("mouse"); + } + if ((key_mask & ALLOW_KEYS) != 0) + { + if (!input.empty()) + { + input += ", "; + } + input += getString("keyboard"); + } + pDesription->setText(getString("basic_description")); + pDesription->setTextArg("[INPUT]", input); +} + +// static +bool LLSetKeyBindDialog::recordKey(KEY key, MASK mask, BOOL down) +{ + if (sRecordKeys) + { + LLSetKeyBindDialog* dialog = LLFloaterReg::getTypedInstance("keybind_dialog", LLSD()); + if (dialog && dialog->getVisible()) + { + return dialog->recordAndHandleKey(key, mask, down); + } + else + { + LL_WARNS() << "Key recording was set despite no open dialog" << LL_ENDL; + sRecordKeys = false; + } + } + return false; +} + +bool LLSetKeyBindDialog::recordAndHandleKey(KEY key, MASK mask, BOOL down) +{ + if ((key == 'Q' && mask == MASK_CONTROL) + || key == KEY_ESCAPE) + { + sRecordKeys = false; + closeFloater(); + return true; + } + + if (key == KEY_DELETE) + { + setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false); + sRecordKeys = false; + closeFloater(); + return false; + } + + // forbidden keys + if (key == KEY_NONE + || key == KEY_RETURN + || key == KEY_BACKSPACE) + { + return false; + } + + if (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT) + { + // Mask keys get special treatment + if ((mKeyFilterMask & ALLOW_MASKS) == 0) + { + // Masks by themself are not allowed + return false; + } + if (down == TRUE) + { + // Most keys are handled on 'down' event because menu is handled on 'down' + // masks are exceptions to let other keys be handled + mLastMaskKey = key; + return false; + } + if (mLastMaskKey != key) + { + // This was mask+key combination that got rejected, don't handle mask's key + // Or user did something like: press shift, press ctrl, release shift + return false; + } + // Mask up event often generates things like 'shift key + shift mask', filter it out. + if (key == KEY_CONTROL) + { + mask &= ~MASK_CONTROL; + } + if (key == KEY_SHIFT) + { + mask &= ~MASK_SHIFT; + } + if (key == KEY_ALT) + { + mask &= ~MASK_ALT; + } + } + if ((mKeyFilterMask & ALLOW_KEYS) == 0) + { + // basic keys not allowed + return false; + } + else if ((mKeyFilterMask & ALLOW_MASK_KEYS) == 0 && mask != 0) + { + // masked keys not allowed + return false; + } + + if (LLKeyConflictHandler::isReservedByMenu(key, mask)) + { + pDesription->setText(getString("reserved_by_menu")); + pDesription->setTextArg("[KEYSTR]", LLKeyboard::stringFromAccelerator(mask,key)); + mLastMaskKey = 0; + return true; + } + + setKeyBind(CLICK_NONE, key, mask, pCheckBox->getValue().asBoolean()); + // Note/Todo: To warranty zero interference we should also consume + // an 'up' event if we recorded on 'down', not just close floater + // on first recorded combination. + sRecordKeys = false; + closeFloater(); + return true; +} + +BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) +{ + BOOL result = FALSE; + if (!pParent) + { + // we already processed 'down' event, this is 'up', consume + closeFloater(); + result = TRUE; + } + if (!result && clicktype == CLICK_LEFT) + { + // try handling buttons first + if (down) + { + result = LLView::handleMouseDown(x, y, mask); + } + else + { + result = LLView::handleMouseUp(x, y, mask); + } + if (result) + { + setFocus(TRUE); + gFocusMgr.setKeystrokesOnly(TRUE); + } + // ignore selection related combinations + else if (down && (mask & (MASK_SHIFT | MASK_CONTROL)) == 0) + { + // this can be a double click, wait a bit; + if (!pUpdater) + { + // Note: default doubleclick time is 500ms, but can stretch up to 5s + pUpdater = new Updater(boost::bind(&onClickTimeout, this, _1), 0.7f, mask); + result = TRUE; + } + } + } + + if (!result + && (clicktype != CLICK_LEFT) // subcases were handled above + && ((mKeyFilterMask & ALLOW_MOUSE) != 0) + && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported + && ((mKeyFilterMask & ALLOW_MASK_MOUSE) != 0 || mask == 0)) // reserved for selection + { + setKeyBind(clicktype, KEY_NONE, mask, pCheckBox->getValue().asBoolean()); + result = TRUE; + if (!down) + { + // wait for 'up' event before closing + // alternative: set pUpdater + closeFloater(); + } + } + + return result; +} + +//static +void LLSetKeyBindDialog::onCancel(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + self->closeFloater(); +} + +//static +void LLSetKeyBindDialog::onBlank(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + // tmp needs 'no key' button + self->setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false); + self->closeFloater(); +} + +//static +void LLSetKeyBindDialog::onDefault(void* user_data) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + if (self->pParent) + { + self->pParent->onDefaultKeyBind(self->pCheckBox->getValue().asBoolean()); + self->pParent = NULL; + } + self->closeFloater(); +} + +//static +void LLSetKeyBindDialog::onClickTimeout(void* user_data, MASK mask) +{ + LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; + + // timer will delete itself after timeout + self->pUpdater = NULL; + + self->setKeyBind(CLICK_LEFT, KEY_NONE, mask, self->pCheckBox->getValue().asBoolean()); + self->closeFloater(); +} + +void LLSetKeyBindDialog::setKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) +{ + if (pParent) + { + pParent->onSetKeyBind(click, key, mask, all_modes); + pParent = NULL; + } +} + diff --git a/indra/newview/llsetkeybinddialog.h b/indra/newview/llsetkeybinddialog.h new file mode 100644 index 0000000000..a34b952233 --- /dev/null +++ b/indra/newview/llsetkeybinddialog.h @@ -0,0 +1,106 @@ +/** + * @file llsetkeybinddialog.h + * @brief LLSetKeyBindDialog class definition + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2019, 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_LLSETKEYBINDDIALOG_H +#define LL_LLSETKEYBINDDIALOG_H + +#include "llmodaldialog.h" + +class LLCheckBoxCtrl; +class LLTextBase; + +// Filters for LLSetKeyBindDialog +static const U32 ALLOW_MOUSE = 1; +static const U32 ALLOW_MASK_MOUSE = 2; +static const U32 ALLOW_KEYS = 4; //keyboard +static const U32 ALLOW_MASK_KEYS = 8; +static const U32 ALLOW_MASKS = 16; +static const U32 DEFAULT_KEY_FILTER = ALLOW_MOUSE | ALLOW_MASK_MOUSE | ALLOW_KEYS | ALLOW_MASKS | ALLOW_MASK_KEYS; + + +class LLKeyBindResponderInterface +{ +public: + virtual ~LLKeyBindResponderInterface() {}; + + virtual void onCancelKeyBind() = 0; + virtual void onDefaultKeyBind(bool all_modes) = 0; + // returns true if parent failed to set key due to key being in use + virtual bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) = 0; +}; + +class LLSetKeyBindDialog : public LLModalDialog +{ +public: + LLSetKeyBindDialog(const LLSD& key); + ~LLSetKeyBindDialog(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& data); + /*virtual*/ void onClose(bool app_quiting); + /*virtual*/ void draw(); + + void setParent(LLKeyBindResponderInterface* parent, LLView* frustum_origin, U32 key_mask = DEFAULT_KEY_FILTER); + + // Wrapper around recordAndHandleKey + // It does not record, it handles, but handleKey function is already in use + static bool recordKey(KEY key, MASK mask, BOOL down); + + BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); + static void onCancel(void* user_data); + static void onBlank(void* user_data); + static void onDefault(void* user_data); + static void onClickTimeout(void* user_data, MASK mask); + + class Updater; + +private: + bool recordAndHandleKey(KEY key, MASK mask, BOOL down); + void setKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); + LLKeyBindResponderInterface *pParent; + LLCheckBoxCtrl *pCheckBox; + LLTextBase *pDesription; + + U32 mKeyFilterMask; + Updater *pUpdater; + KEY mLastMaskKey; + + static bool sRecordKeys; // for convinience and not to check instance each time + + // drawFrustum +private: + void drawFrustum(); + + LLHandle mFrustumOrigin; + F32 mContextConeOpacity; + F32 mContextConeInAlpha; + F32 mContextConeOutAlpha; + F32 mContextConeFadeTime; +}; + + +#endif // LL_LLSETKEYBINDDIALOG_H diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index e1f617ace6..d1e3e9b943 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -372,7 +372,8 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a if (vol_face.mJointRiggingInfoTab.needsUpdate()) { S32 num_verts = vol_face.mNumVertices; - if (num_verts>0 && vol_face.mWeights && (skin->mJointNames.size()>0)) + S32 num_joints = skin->mJointNames.size(); + if (num_verts > 0 && vol_face.mWeights && num_joints > 0) { initJointNums(const_cast(skin), avatar); if (vol_face.mJointRiggingInfoTab.size()==0) @@ -406,7 +407,7 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a for (U32 k=0; k<4; ++k) { S32 joint_index = idx[k]; - if (wght[k] > 0.0f) + if (wght[k] > 0.0f && num_joints > joint_index) { S32 joint_num = skin->mJointNums[joint_index]; if (joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS) @@ -457,35 +458,6 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a } } -void LLSkinningUtil::updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab) -{ - LL_RECORD_BLOCK_TIME(FTM_FACE_RIGGING_INFO); - for (S32 i=0; i < num_verts; i++) - { - LLVector4a& pos = positions[i]; - LLVector4a& wght = weights[i]; - for (U32 k=0; k<4; ++k) - { - S32 joint_num = skin->mJointNums[joint_indices[k]]; - llassert(joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS); - { - rig_info_tab[joint_num].setIsRiggedTo(true); - LLMatrix4a bind_shape; - bind_shape.loadu(skin->mBindShapeMatrix); - LLMatrix4a inv_bind; - inv_bind.loadu(skin->mInvBindMatrix[joint_indices[k]]); - LLMatrix4a mat; - matMul(bind_shape, inv_bind, mat); - LLVector4a pos_joint_space; - mat.affineTransform(pos, pos_joint_space); - pos_joint_space.mul(wght[k]); - LLVector4a *extents = rig_info_tab[joint_num].getRiggedExtents(); - update_min_max(extents[0], extents[1], pos_joint_space); - } - } - } -} - // This is used for extracting rotation from a bind shape matrix that // already has scales baked in LLQuaternion LLSkinningUtil::getUnscaledQuaternion(const LLMatrix4& mat4) diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h index 0f25f96ed3..cb7653603b 100644 --- a/indra/newview/llskinningutil.h +++ b/indra/newview/llskinningutil.h @@ -70,7 +70,6 @@ namespace LLSkinningUtil void initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar); void updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face); - void updateRiggingInfo_(LLMeshSkinInfo* skin, LLVOAvatar *avatar, S32 num_verts, LLVector4a* weights, LLVector4a* positions, U8* joint_indices, LLJointRiggingInfoTab &rig_info_tab); LLQuaternion getUnscaledQuaternion(const LLMatrix4& mat4); }; diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 8a470cc97d..d32172a58e 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -1011,8 +1011,8 @@ LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible) } LLSpatialGroup* group = drawablep->getSpatialGroup(); - // avoid crash for race condition with unseating of an avatar - // llassert(group != NULL); + //llassert(group != NULL); + if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING)) { group->setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 5aba427bc7..7109ad6013 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2086,7 +2086,10 @@ bool idle_startup() // update the voice settings *after* gCacheName initialization // so that we can construct voice UI that relies on the name cache - LLVoiceClient::getInstance()->updateSettings(); + if (LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->updateSettings(); + } display_startup(); // OpenSim support: Init with defaults - we get the OpenSimExtras later during login @@ -2504,8 +2507,12 @@ bool idle_startup() LLAvatarTracker::instance().addBuddyList(list); display_startup(); } - - LGGContactSets::getInstance()->loadFromDisk(); // [FS:CR] Load contact sets + + // Contact sets + LGGContactSets* cs_instance = LGGContactSets::getInstance(); + cs_instance->loadFromDisk(); + LLAvatarNameCache::instance().setCustomNameCheckCallback(boost::bind(&LGGContactSets::checkCustomName, cs_instance, _1, _2, _3)); + // bool show_hud = false; LLSD tutorial_setting = response["tutorial_setting"]; diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index c5e31ff8e6..5235914c34 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -60,7 +60,7 @@ LLTool::~LLTool() } } -BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); @@ -83,9 +83,9 @@ BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask) LL_INFOS() << "LLTool left mouse down" << LL_ENDL; } // by default, didn't handle it + // AGENT_CONTROL_LBUTTON_DOWN is handled by scanMouse() and scanKey() // LL_INFOS() << "LLTool::handleMouseDown" << LL_ENDL; - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); - return TRUE; + return FALSE; } BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask) @@ -95,8 +95,8 @@ BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask) LL_INFOS() << "LLTool left mouse up" << LL_ENDL; } // by default, didn't handle it + // AGENT_CONTROL_LBUTTON_UP is handled by scanMouse() and scanKey() // LL_INFOS() << "LLTool::handleMouseUp" << LL_ENDL; - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); return TRUE; } diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h index 308983afda..41a38804ce 100644 --- a/indra/newview/lltool.h +++ b/indra/newview/lltool.h @@ -49,7 +49,7 @@ public: virtual BOOL isView() const { return FALSE; } // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp index 76d2e3f9d2..122180731d 100644 --- a/indra/newview/lltoolfocus.cpp +++ b/indra/newview/lltoolfocus.cpp @@ -39,7 +39,7 @@ #include "llagentcamera.h" #include "llbutton.h" #include "llviewercontrol.h" -#include "llviewerkeyboard.h"// Mouse movement by Singularity +#include "llviewerinput.h"// Mouse movement by Singularity #include "lldrawable.h" #include "lltooltip.h" #include "llhudmanager.h" diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index a342399d53..db298d144d 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -66,6 +66,7 @@ #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llviewerwindow.h" +#include "llviewerinput.h" #include "llviewermedia.h" #include "llvoavatarself.h" #include "llviewermediafocus.h" @@ -92,7 +93,6 @@ LLToolPie::LLToolPie() mMouseOutsideSlop( false ), mMouseSteerX(-1), mMouseSteerY(-1), - mBlockClickToWalk(false), mClickAction(0), mClickActionBuyEnabled( gSavedSettings.getBOOL("ClickActionBuyEnabled") ), mClickActionPayEnabled( gSavedSettings.getBOOL("ClickActionPayEnabled") ), @@ -100,7 +100,7 @@ LLToolPie::LLToolPie() { } -BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); @@ -187,10 +187,8 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) mPick.mKeyMask = mask; mMouseButtonDown = true; - - handleLeftClickPick(); - return TRUE; + return handleLeftClickPick(); } // Spawn context menus on right mouse down so you can drag over and select @@ -442,8 +440,6 @@ BOOL LLToolPie::handleLeftClickPick() // put focus back "in world" if (gFocusMgr.getKeyboardFocus()) { - // don't click to walk on attempt to give focus to world - mBlockClickToWalk = true; gFocusMgr.setKeyboardFocus(NULL); } @@ -499,10 +495,7 @@ BOOL LLToolPie::handleLeftClickPick() } object = (LLViewerObject*)object->getParent(); } - // FIRE-15189: Fix ClickToWalk not allowing mouse-walk (behavior change) - //if (object && object == gAgentAvatarp && !gSavedSettings.getBOOL("ClickToWalk")) if (object && object == gAgentAvatarp) - // { // we left clicked on avatar, switch to focus mode mMouseButtonDown = false; @@ -520,7 +513,6 @@ BOOL LLToolPie::handleLeftClickPick() // LLFirstUse::useLeftClickNoHit(); ///////// - // Eat the event return LLTool::handleMouseDown(x, y, mask); } @@ -642,17 +634,140 @@ void LLToolPie::resetSelection() mClickAction = 0; } -void LLToolPie::walkToClickedLocation() +bool LLToolPie::walkToClickedLocation() { - if(mAutoPilotDestination) { mAutoPilotDestination->markDead(); } - mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE); - mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal); - mAutoPilotDestination->setPixelSize(5); - mAutoPilotDestination->setColor(LLColor4U(170, 210, 190)); - mAutoPilotDestination->setDuration(3.f); + if (gAgent.getFlying() // don't auto-navigate while flying until that works + || !gAgentAvatarp + || gAgentAvatarp->isSitting()) + { + return false; + } - LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; - gAgent.startAutoPilotGlobal(pos, std::string(), NULL, NULL, NULL, 0.f, 0.03f, FALSE); + LLPickInfo saved_pick = mPick; + if (gAgentCamera.getCameraMode() != CAMERA_MODE_MOUSELOOK) + { + mPick = gViewerWindow->pickImmediate(mHoverPick.mMousePt.mX, mHoverPick.mMousePt.mY, + FALSE /* ignore transparent */, + FALSE /* ignore rigged */, + FALSE /* ignore particles */); + } + else + { + // We do not handle hover in mouselook as we do in other modes, so + // use croshair's position to do a pick + mPick = gViewerWindow->pickImmediate(gViewerWindow->getWorldViewRectScaled().getWidth() / 2, + gViewerWindow->getWorldViewRectScaled().getHeight() / 2, + FALSE /* ignore transparent */, + FALSE /* ignore rigged */, + FALSE /* ignore particles */); + } + + if (mPick.mPickType == LLPickInfo::PICK_OBJECT) + { + if (mPick.getObject() && mPick.getObject()->isHUDAttachment()) + { + mPick = saved_pick; + return false; + } + } + + LLViewerObject* avatar_object = mPick.getObject(); + + // get pointer to avatar + while (avatar_object && !avatar_object->isAvatar()) + { + avatar_object = (LLViewerObject*)avatar_object->getParent(); + } + + if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf()) + { + const F64 SELF_CLICK_WALK_DISTANCE = 3.0; + // pretend we picked some point a bit in front of avatar + mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE; + } + + if ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) || + (mPick.mObjectID.notNull() && !mPick.mPosGlobal.isExactlyZero())) + { +// [RLVa:KB] - Checked: RLVa-2.0.0 + if (RlvActions::isRlvEnabled() && !RlvActions::canTeleportToLocal(mPick.mPosGlobal)) + { + RlvUtil::notifyBlocked(RlvStringKeys::Blocked::AutoPilot); + mPick = saved_pick; + return false; + } +// [/RLVa:KB] + + gAgentCamera.setFocusOnAvatar(TRUE, TRUE); + + if (mAutoPilotDestination) { mAutoPilotDestination->markDead(); } + mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE); + mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal); + mAutoPilotDestination->setPixelSize(5); + mAutoPilotDestination->setColor(LLColor4U(170, 210, 190)); + mAutoPilotDestination->setDuration(3.f); + + LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; + gAgent.startAutoPilotGlobal(pos, std::string(), NULL, NULL, NULL, 0.f, 0.03f, FALSE); + LLFirstUse::notMoving(false); + showVisualContextMenuEffect(); + return true; + } + else + { + LL_DEBUGS() << "walk target was " + << (mPick.mPosGlobal.isExactlyZero() ? "zero" : "not zero") + << ", pick type was " << (mPick.mPickType == LLPickInfo::PICK_LAND ? "land" : "not land") + << ", pick object was " << mPick.mObjectID + << LL_ENDL; + mPick = saved_pick; + return false; + } + return true; +} + +bool LLToolPie::teleportToClickedLocation() +{ + if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) + { + // We do not handle hover in mouselook as we do in other modes, so + // use croshair's position to do a pick + BOOL pick_rigged = false; + mHoverPick = gViewerWindow->pickImmediate(gViewerWindow->getWorldViewRectScaled().getWidth() / 2, + gViewerWindow->getWorldViewRectScaled().getHeight() / 2, + FALSE, + pick_rigged); + } + LLViewerObject* objp = mHoverPick.getObject(); + LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL; + + bool is_in_world = mHoverPick.mObjectID.notNull() && objp && !objp->isHUDAttachment(); + bool is_land = mHoverPick.mPickType == LLPickInfo::PICK_LAND; + bool pos_non_zero = !mHoverPick.mPosGlobal.isExactlyZero(); + bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch()); + bool has_click_action = final_click_action(objp); + + // FIRE-1765: Allow double-click walk/teleport to scripted objects + //if (pos_non_zero && (is_land || (is_in_world && !has_touch_handler && !has_click_action))) + bool allowDoubleClickOnScriptedObjects = gSavedSettings.getBOOL("FSAllowDoubleClickOnScriptedObjects"); + if (pos_non_zero && (is_land || (is_in_world && ((allowDoubleClickOnScriptedObjects && objp->getClickAction() != CLICK_ACTION_SIT) || (!has_touch_handler && !has_click_action))))) + // + { +// [RLVa:KB] - Checked: RLVa-2.0.0 + if (RlvActions::isRlvEnabled() && !RlvActions::canTeleportToLocal(mPick.mPosGlobal)) + { + RlvUtil::notifyBlocked(RlvStringKeys::Blocked::AutoPilot); + return false; + } +// [/RLVa:KB] + LLVector3d pos = mHoverPick.mPosGlobal; + pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot(); + gAgent.teleportViaLocationLookAt(pos); + mPick = mHoverPick; + showVisualContextMenuEffect(); + return true; + } + return false; } // When we get object properties after left-clicking on an object @@ -736,8 +851,9 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) LL_DEBUGS("UserInput") << "hover handled by LLToolPie (inactive)" << LL_ENDL; } else if (!mMouseOutsideSlop - && mMouseButtonDown - && gSavedSettings.getBOOL("ClickToWalk")) + && mMouseButtonDown + // disable camera steering if click on land is not used for moving + && gViewerInput.isMouseBindUsed(CLICK_LEFT)) { S32 delta_x = x - mMouseDownX; S32 delta_y = y - mMouseDownY; @@ -835,84 +951,10 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) mDoubleClickTimer.reset(); } LLViewerObject* obj = mPick.getObject(); - U8 click_action = final_click_action(obj); - // let media have first pass at click - if (handleMediaMouseUp() || LLViewerMediaFocus::getInstance()->getFocus()) - { - mBlockClickToWalk = true; - } stopCameraSteering(); mMouseButtonDown = false; - if (click_action == CLICK_ACTION_NONE // not doing 1-click action - && gSavedSettings.getBOOL("ClickToWalk") // click to walk enabled - && !gAgent.getFlying() // don't auto-navigate while flying until that works - && gAgentAvatarp - && !gAgentAvatarp->isSitting() - && !mBlockClickToWalk // another behavior hasn't cancelled click to walk - ) - { - // We may be doing click to walk, but we don't want to use a target on - // a transparent object because the user thought they were clicking on - // whatever they were seeing through it, so recompute what was clicked on - // ignoring transparent objects - LLPickInfo savedPick = mPick; - mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY, - FALSE /* ignore transparent */, - FALSE /* ignore rigged */, - FALSE /* ignore particles */); - -// if (!mPick.mPosGlobal.isExactlyZero() // valid coordinates for pick -// && (mPick.mPickType == LLPickInfo::PICK_LAND // we clicked on land -// || mPick.mObjectID.notNull())) // or on an object -// [RLVa:KB] - Checked: RLVa-2.0.0 - bool fValidPick = (!mPick.mPosGlobal.isExactlyZero() // valid coordinates for pick - && (mPick.mPickType == LLPickInfo::PICK_LAND // we clicked on land - || mPick.mObjectID.notNull())); // or on an object - - if ( (fValidPick) && (RlvActions::isRlvEnabled()) && (!RlvActions::canTeleportToLocal(mPick.mPosGlobal)) ) - { - RlvUtil::notifyBlocked(RlvStringKeys::Blocked::AutoPilot); - fValidPick = false; - } - - if (fValidPick) -// [/RLVa:KB] - { - - // handle special cases of steering picks - LLViewerObject* avatar_object = mPick.getObject(); - - // get pointer to avatar - while (avatar_object && !avatar_object->isAvatar()) - { - avatar_object = (LLViewerObject*)avatar_object->getParent(); - } - - if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf()) - { - const F64 SELF_CLICK_WALK_DISTANCE = 3.0; - // pretend we picked some point a bit in front of avatar - mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE; - } - gAgentCamera.setFocusOnAvatar(TRUE, TRUE); - walkToClickedLocation(); - LLFirstUse::notMoving(false); - - return TRUE; - } - else - { - LL_DEBUGS("maint5901") << "walk target was " - << (mPick.mPosGlobal.isExactlyZero() ? "zero" : "not zero") - << ", pick type was " << (mPick.mPickType == LLPickInfo::PICK_LAND ? "land" : "not land") - << ", pick object was " << mPick.mObjectID - << LL_ENDL; - // we didn't click to walk, so restore the original target - mPick = savedPick; - } - } gViewerWindow->setCursor(UI_CURSOR_ARROW); if (hasMouseCapture()) { @@ -922,7 +964,6 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) LLToolMgr::getInstance()->clearTransientTool(); gAgentCamera.setLookAt(LOOKAT_TARGET_CONVERSATION, obj); // maybe look at object/person clicked on - mBlockClickToWalk = false; return LLTool::handleMouseUp(x, y, mask); } @@ -947,13 +988,11 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask) { return TRUE; } - - // FIRE-1765: Allow double-click walk/teleport to scripted objects - bool allowDoubleClickOnScriptedObjects = gSavedSettings.getBOOL("FSAllowDoubleClickOnScriptedObjects"); // FIRE-1765: Allow double-click walk/teleport to scripted objects - //if (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f)) - if (!allowDoubleClickOnScriptedObjects && (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f))) + //if (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f)) + bool allowDoubleClickOnScriptedObjects = gSavedSettings.getBOOL("FSAllowDoubleClickOnScriptedObjects"); + if (!allowDoubleClickOnScriptedObjects && (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f))) // { mDoubleClickTimer.stop(); @@ -961,74 +1000,6 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask) } mDoubleClickTimer.stop(); - if (gSavedSettings.getBOOL("DoubleClickAutoPilot")) - { - // We may be doing double click to walk, but we don't want to use a target on - // a transparent object because the user thought they were clicking on - // whatever they were seeing through it, so recompute what was clicked on - // ignoring transparent objects - LLPickInfo savedPick = mPick; - mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY, - FALSE /* ignore transparent */, - FALSE /* ignore rigged */, - FALSE /* ignore particles */); - - if(mPick.mPickType == LLPickInfo::PICK_OBJECT) - { - if (mPick.getObject() && mPick.getObject()->isHUDAttachment()) - { - mPick = savedPick; - return FALSE; - } - } - -// if ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) || -// (mPick.mObjectID.notNull() && !mPick.mPosGlobal.isExactlyZero())) -// [RLVa:KB] - Checked: RLVa-2.0.0 - bool fValidPick = ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) || - (mPick.mObjectID.notNull() && !mPick.mPosGlobal.isExactlyZero())); - - if ( (fValidPick) && (RlvActions::isRlvEnabled()) && (!RlvActions::canTeleportToLocal(mPick.mPosGlobal)) ) - { - RlvUtil::notifyBlocked(RlvStringKeys::Blocked::AutoPilot); - fValidPick = false; - } - - if (fValidPick) -// [/RLVa:KB] - { - walkToClickedLocation(); - return TRUE; - } - else - { - // restore the original pick for any other purpose - mPick = savedPick; - } - } - else if (gSavedSettings.getBOOL("DoubleClickTeleport")) - { - LLViewerObject* objp = mPick.getObject(); - LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL; - - bool is_in_world = mPick.mObjectID.notNull() && objp && !objp->isHUDAttachment(); - bool is_land = mPick.mPickType == LLPickInfo::PICK_LAND; - bool pos_non_zero = !mPick.mPosGlobal.isExactlyZero(); - bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch()); - bool has_click_action = final_click_action(objp); - - // FIRE-1765: Allow double-click walk/teleport to scripted objects - //if (pos_non_zero && (is_land || (is_in_world && !has_touch_handler && !has_click_action))) - if (pos_non_zero && (is_land || (is_in_world && ((allowDoubleClickOnScriptedObjects && objp->getClickAction() != CLICK_ACTION_SIT) || (!has_touch_handler && !has_click_action))))) - // - { - LLVector3d pos = mPick.mPosGlobal; - pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot(); - gAgent.teleportViaLocationLookAt(pos); - return TRUE; - } - } - return FALSE; } @@ -1782,7 +1753,6 @@ void LLToolPie::VisitHomePage(const LLPickInfo& info) void LLToolPie::handleSelect() { // tool is reselected when app gets focus, etc. - mBlockClickToWalk = true; } void LLToolPie::handleDeselect() @@ -1847,7 +1817,7 @@ void LLToolPie::stopCameraSteering() bool LLToolPie::inCameraSteerMode() { - return mMouseButtonDown && mMouseOutsideSlop && gSavedSettings.getBOOL("ClickToWalk"); + return mMouseButtonDown && mMouseOutsideSlop; } // true if x,y outside small box around start_x,start_y @@ -2431,7 +2401,6 @@ void LLToolPie::startCameraSteering() { LLFirstUse::notMoving(false); mMouseOutsideSlop = true; - mBlockClickToWalk = true; if (gAgentCamera.getFocusOnAvatar()) { diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index 34640d7f9c..1f54e51589 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -42,7 +42,7 @@ class LLToolPie : public LLTool, public LLSingleton public: // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); @@ -71,8 +71,8 @@ public: LLViewerObject* getClickActionObject() { return mClickActionObject; } LLObjectSelection* getLeftClickSelection() { return (LLObjectSelection*)mLeftClickSelection; } void resetSelection(); - void walkToClickedLocation(); - void blockClickToWalk() { mBlockClickToWalk = true; } + bool walkToClickedLocation(); + bool teleportToClickedLocation(); void stopClickToWalk(); static void selectionPropertiesReceived(); @@ -121,7 +121,6 @@ private: LLPointer mAutoPilotDestination; LLPointer mMouseSteerGrabPoint; bool mClockwise; - bool mBlockClickToWalk; LLUUID mMediaMouseCaptureID; LLPickInfo mPick; LLPickInfo mHoverPick; diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 10f7e074da..643c5b6b58 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -99,7 +99,7 @@ #include "llnotificationsutil.h" #include "llpanelplaces.h" #include "llstatusbar.h" -#include "llviewerkeyboard.h" +#include "llviewerinput.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "NACLantispam.h" @@ -629,7 +629,10 @@ bool handleHighResSnapshotChanged(const LLSD& newvalue) bool handleVoiceClientPrefsChanged(const LLSD& newvalue) { - LLVoiceClient::getInstance()->updateSettings(); + if (LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->updateSettings(); + } return true; } @@ -876,23 +879,6 @@ void handleUsernameFormatOptionChanged(const LLSD& newvalue) } // -// Allow instant change of keyboard layout -void handleKeyboardLayoutChanged(const LLSD& newvalue) -{ - std::string keyBindingFileName("keys.xml"); - if (newvalue.asBoolean()) - { - keyBindingFileName = "keys_azerty.xml"; - } - - std::string key_bindings_file = gDirUtilp->findFile(keyBindingFileName, - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - - gViewerKeyboard.loadBindingsXML(key_bindings_file); -} -// - // Global online status toggle void handleGlobalOnlineStatusChanged(const LLSD& newvalue) { @@ -1278,8 +1264,6 @@ void settings_setup_listeners() gSavedSettings.getControl("FSNameTagShowLegacyUsernames")->getCommitSignal()->connect(boost::bind(&handleUsernameFormatOptionChanged, _2)); gSavedSettings.getControl("FSTrimLegacyNames")->getCommitSignal()->connect(boost::bind(&handleLegacyTrimOptionChanged, _2)); - gSavedSettings.getControl("FSUseAzertyKeyboardLayout")->getCommitSignal()->connect(boost::bind(&handleKeyboardLayoutChanged, _2)); - // [FS communication UI] gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&FSFloaterIM::processChatHistoryStyleUpdate, _2)); gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&FSFloaterNearbyChat::processChatHistoryStyleUpdate, _2)); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index e955166827..bd5a3e718e 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1427,8 +1427,9 @@ void render_ui(F32 zoom_factor, int subfield) // Finalize scene gPipeline.renderFinalize(); - LL_RECORD_BLOCK_TIME(FTM_RENDER_HUD); - render_hud_elements(); + {// give unique scope + LL_RECORD_BLOCK_TIME(FTM_RENDER_HUD); + render_hud_elements(); // [RLVa:KB] - Checked: RLVa-2.2 (@setoverlay) if (RlvActions::hasBehaviour(RLV_BHVR_SETOVERLAY)) { @@ -1436,7 +1437,7 @@ void render_ui(F32 zoom_factor, int subfield) } // [/RLVa:KB] render_hud_attachments(); - + } // unique scope LLGLSDefault gls_default; LLGLSUIDefault gls_ui; { diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp new file mode 100644 index 0000000000..07a0bb302d --- /dev/null +++ b/indra/newview/llviewerinput.cpp @@ -0,0 +1,1631 @@ +/** + * @file llviewerinput.cpp + * @brief LLViewerInput class implementation + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerinput.h" + +#include "llappviewer.h" +#include "llfloaterreg.h" +#include "llmath.h" +#include "llagent.h" +#include "llagentcamera.h" +// [FS Communication UI] +// #include "llfloaterimnearbychat.h" +#include "fsnearbychathub.h" +#include "lllineeditor.h" +// [FS Communication UI] +#include "llfocusmgr.h" +#include "llkeybind.h" // LLKeyData +#include "llmorphview.h" +#include "llmoveview.h" +#include "lltoolfocus.h" +#include "lltoolpie.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" +#include "llvoavatarself.h" +#include "llfloatercamera.h" +#include "llinitparam.h" +#include "llselectmgr.h" +#include "llfloaterwebcontent.h" +#include "fsfloatersearch.h" +#include "llvoiceclient.h" + +// +// Constants +// + +const F32 FLY_TIME = 0.5f; +const F32 FLY_FRAMES = 4; + +const F32 NUDGE_TIME = 0.25f; // in seconds +const S32 NUDGE_FRAMES = 2; +const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed + +const LLKeyData agent_control_lbutton(CLICK_LEFT, KEY_NONE, MASK_NONE, true); + +struct LLKeyboardActionRegistry +: public LLRegistrySingleton, LLKeyboardActionRegistry> +{ + LLSINGLETON_EMPTY_CTOR(LLKeyboardActionRegistry); +}; + +LLViewerInput gViewerInput; + +bool agent_jump( EKeystate s ) +{ + static BOOL first_fly_attempt(TRUE); + if (KEYSTATE_UP == s) + { + first_fly_attempt = TRUE; + return true; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + // Chalice Yao's crouch toggle + if (gSavedPerAccountSettings.getBOOL("FSCrouchToggleStatus")) + { + gSavedPerAccountSettings.setBOOL("FSCrouchToggleStatus", FALSE); + } + // + + if( time < FLY_TIME + || frame_count <= FLY_FRAMES + || gAgent.upGrabbed() + || !gSavedSettings.getBOOL("AutomaticFly")) + { + gAgent.moveUp(1); + } + else + { + gAgent.setFlying(TRUE, first_fly_attempt); + first_fly_attempt = FALSE; + gAgent.moveUp(1); + } + return true; +} + +bool agent_push_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + // Chalice Yao's crouch toggle + //gAgent.moveUp(-1); + else if (KEYSTATE_DOWN == s && !gAgent.getFlying() && !gAgentAvatarp->isSitting() && gSavedPerAccountSettings.getBOOL("FSCrouchToggle")) + { + if (gSavedPerAccountSettings.getBOOL("FSCrouchToggleStatus")) + { + gSavedPerAccountSettings.setBOOL("FSCrouchToggleStatus", FALSE); + } + else + { + gSavedPerAccountSettings.setBOOL("FSCrouchToggleStatus", TRUE); + gAgent.moveUp(-1); + } + } + else + { + gAgent.moveUp(-1); + } + // + return true; +} + +static void agent_check_temporary_run(LLAgent::EDoubleTapRunMode mode) +{ +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + if ( (gAgent.mDoubleTapRunMode == mode) && (gAgent.getTempRun()) ) + gAgent.clearTempRun(); +// [/RLVa:KB] +// if (gAgent.mDoubleTapRunMode == mode && +// gAgent.getRunning() && +// !gAgent.getAlwaysRun()) +// { +// // Turn off temporary running. +// gAgent.clearRunning(); +// gAgent.sendWalkRun(gAgent.getRunning()); +// } +} + +static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode mode) +{ + if (KEYSTATE_UP == s) + { + // Note: in case shift is already released, slide left/right run + // will be released in agent_turn_left()/agent_turn_right() + agent_check_temporary_run(mode); + } + else if (gSavedSettings.getBOOL("AllowTapTapHoldRun") && + KEYSTATE_DOWN == s && + !gAgent.getRunning()) + { + if (gAgent.mDoubleTapRunMode == mode && + gAgent.mDoubleTapRunTimer.getElapsedTimeF32() < NUDGE_TIME) + { + // Same walk-key was pushed again quickly; this is a + // double-tap so engage temporary running. +// gAgent.setRunning(); +// gAgent.sendWalkRun(gAgent.getRunning()); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.setTempRun(); +// [/RLVa:KB] + } + + // Pressing any walk-key resets the double-tap timer + gAgent.mDoubleTapRunTimer.reset(); + gAgent.mDoubleTapRunMode = mode; + } +} + +static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) +{ + agent_handle_doubletap_run(s, mode); + if (KEYSTATE_UP == s) return; + + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) + { + gAgent.moveAtNudge(direction); + } + else + { + gAgent.moveAt(direction); + } +} + +bool camera_move_forward( EKeystate s ); + +bool agent_push_forward( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_move_forward(s); + } + else + { + agent_push_forwardbackward(s, 1, LLAgent::DOUBLETAP_FORWARD); + } + return true; +} + +bool camera_move_backward( EKeystate s ); + +bool agent_push_backward( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_move_backward(s); + } + else if (!gAgent.backwardGrabbed() && gAgentAvatarp->isSitting() && gSavedSettings.getBOOL("LeaveMouselook")) + { + gAgentCamera.changeCameraToThirdPerson(); + } + else + { + agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD); + } + return true; +} + +static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) +{ + agent_handle_doubletap_run(s, mode); + if( KEYSTATE_UP == s ) return; + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) + { + gAgent.moveLeftNudge(direction); + } + else + { + gAgent.moveLeft(direction); + } +} + + +bool agent_slide_left( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + agent_slide_leftright(s, 1, LLAgent::DOUBLETAP_SLIDELEFT); + return true; +} + + +bool agent_slide_right( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return true; + agent_slide_leftright(s, -1, LLAgent::DOUBLETAP_SLIDERIGHT); + return true; +} + +bool camera_spin_around_cw( EKeystate s ); + +bool agent_turn_left(EKeystate s) +{ + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_spin_around_cw(s); + return true; + } + + if(gAgent.isMovementLocked()) return false; + + if (LLToolCamera::getInstance()->mouseSteerMode()) + { + agent_slide_left(s); + } + else + { + if (KEYSTATE_UP == s) + { + // Check temporary running. In case user released 'left' key with shift already released. + agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDELEFT); + return true; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + gAgent.moveYaw( LLFloaterMove::getYawRate( time ) ); + } + return true; +} + +bool camera_spin_around_ccw( EKeystate s ); + +bool agent_turn_right( EKeystate s ) +{ + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_spin_around_ccw(s); + return true; + } + + if(gAgent.isMovementLocked()) return false; + + if (LLToolCamera::getInstance()->mouseSteerMode()) + { + agent_slide_right(s); + } + else + { + if (KEYSTATE_UP == s) + { + // Check temporary running. In case user released 'right' key with shift already released. + agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDERIGHT); + return true; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) ); + } + return true; +} + +bool agent_look_up( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgent.movePitch(-1); + //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); + return true; +} + + +bool agent_look_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgent.movePitch(1); + //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); + return true; +} + +bool agent_toggle_fly( EKeystate s ) +{ + // Only catch the edge + if (KEYSTATE_DOWN == s ) + { + LLAgent::toggleFlying(); + } + return true; +} + +F32 get_orbit_rate() +{ + F32 time = gKeyboard->getCurKeyElapsedTime(); + if( time < NUDGE_TIME ) + { + F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME; + //LL_INFOS() << rate << LL_ENDL; + return rate; + } + else + { + return 1; + } +} + +bool camera_spin_around_ccw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + return true; +} + + +bool camera_spin_around_cw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + return true; +} + +bool camera_spin_around_ccw_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT ) return true; + if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) + { + //send keystrokes, but do not change camera + agent_turn_right(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.unlockView(); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + } + return true; +} + + +bool camera_spin_around_cw_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT ) return true; + if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) + { + //send keystrokes, but do not change camera + agent_turn_left(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.unlockView(); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + } + return true; +} + + +bool camera_spin_over( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + return true; +} + + +bool camera_spin_under( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + return true; +} + +bool camera_spin_over_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + if (gAgent.upGrabbed() || gAgentCamera.sitCameraEnabled()) + { + //send keystrokes, but do not change camera + agent_jump(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + } + return true; +} + + +bool camera_spin_under_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + if (gAgent.downGrabbed() || gAgentCamera.sitCameraEnabled()) + { + //send keystrokes, but do not change camera + agent_push_down(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + } + return true; +} + +bool camera_move_forward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + return true; +} + + +bool camera_move_backward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + return true; +} + +bool camera_move_forward_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD ) return true; + if (gAgent.forwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) + { + agent_push_forward(s); + } + else + { + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + } + return true; +} + +bool camera_move_backward_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD ) return true; + + if (gAgent.backwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) + { + agent_push_backward(s); + } + else + { + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + } + return true; +} + +bool camera_pan_up( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanUpKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanDownKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_left( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanLeftKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_right( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanRightKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_in( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanInKey( get_orbit_rate() ); + return true; +} + +bool camera_pan_out( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setPanOutKey( get_orbit_rate() ); + return true; +} + +bool camera_move_forward_fast( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitInKey(2.5f); + return true; +} + +bool camera_move_backward_fast( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitOutKey(2.5f); + return true; +} + + +bool edit_avatar_spin_ccw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + //gMorphView->orbitLeft( get_orbit_rate() ); + return true; +} + + +bool edit_avatar_spin_cw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + //gMorphView->orbitRight( get_orbit_rate() ); + return true; +} + +bool edit_avatar_spin_over( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + //gMorphView->orbitUp( get_orbit_rate() ); + return true; +} + + +bool edit_avatar_spin_under( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + //gMorphView->orbitDown( get_orbit_rate() ); + return true; +} + +bool edit_avatar_move_forward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + //gMorphView->orbitIn(); + return true; +} + + +bool edit_avatar_move_backward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return true; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + //gMorphView->orbitOut(); + return true; +} + +bool stop_moving( EKeystate s ) +{ + //it's supposed that 'stop moving' key will be held down for some time + if( KEYSTATE_UP == s ) return true; + // stop agent + gAgent.setControlFlags(AGENT_CONTROL_STOP); + + // cancel autopilot + gAgent.stopAutoPilot(); + return true; +} + +bool start_chat( EKeystate s ) +{ + if (LLAppViewer::instance()->quitRequested()) + { + return true; // can't talk, gotta go, kthxbye! + } + if (KEYSTATE_DOWN != s) return true; + + // start chat + // [FS Communication UI] + //LLFloaterIMNearbyChat::startChat(NULL); + FSNearbyChat::instance().showDefaultChatBar(TRUE); + // [FS Communication UI] + return true; +} + +bool start_gesture( EKeystate s ) +{ + // If avatar is pointing at something, don't start gesture. + // This works around the bug with Shared Media prims. + if (gAgentCamera.mPointAtObject) + { + return true; + } + + // FIRE-4167: Don't start gesture if a floater with web content has focus + LLFloater* focused_floater = gFloaterView->getFocusedFloater(); + if (focused_floater && (dynamic_cast(focused_floater) || dynamic_cast(focused_floater))) + { + return true; + } + + LLUICtrl* focus_ctrlp = dynamic_cast(gFocusMgr.getKeyboardFocus()); + if (KEYSTATE_UP == s && + ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) + { + // [FS Communication UI] + //if ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) + //{ + // // No existing chat in chat editor, insert '/' + // LLFloaterIMNearbyChat::startChat("/"); + //} + //else + //{ + // // Don't overwrite existing text in chat editor + // LLFloaterIMNearbyChat::startChat(NULL); + //} + + FSNearbyChat::instance().showDefaultChatBar(TRUE, "/"); + // [FS Communication UI] + } + return true; +} + +bool run_forward(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_FORWARD; + } + if (!gAgent.getRunning()) + { +// gAgent.setRunning(); +// gAgent.sendWalkRun(true); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.setTempRun(); +// [/RLVa:KB] + } + } + else if(KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_FORWARD) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; +// gAgent.clearRunning(); +// gAgent.sendWalkRun(false); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.clearTempRun(); +// [/RLVa:KB] + } + agent_push_forward(s); + return true; +} + +bool run_backward(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_BACKWARD; + } + if (!gAgent.getRunning()) + { +// gAgent.setRunning(); +// gAgent.sendWalkRun(true); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.setTempRun(); +// [/RLVa:KB] + } + } + else if (KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_BACKWARD) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; +// gAgent.clearRunning(); +// gAgent.sendWalkRun(false); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.clearTempRun(); +// [/RLVa:KB] + } + agent_push_backward(s); + return true; +} + +bool run_left(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_SLIDELEFT; + } + if (!gAgent.getRunning()) + { +// gAgent.setRunning(); +// gAgent.sendWalkRun(true); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.setTempRun(); +// [/RLVa:KB] + } + } + else if (KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_SLIDELEFT) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; +// gAgent.clearRunning(); +// gAgent.sendWalkRun(false); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.clearTempRun(); +// [/RLVa:KB] + } + agent_slide_left(s); + return true; +} + +bool run_right(EKeystate s) +{ + if (KEYSTATE_UP != s) + { + if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT) + { + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_SLIDERIGHT; + } + if (!gAgent.getRunning()) + { +// gAgent.setRunning(); +// gAgent.sendWalkRun(true); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.setTempRun(); +// [/RLVa:KB] + } + } + else if (KEYSTATE_UP == s) + { + if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_SLIDERIGHT) + gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; +// gAgent.clearRunning(); +// gAgent.sendWalkRun(false); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i + gAgent.clearTempRun(); +// [/RLVa:KB] + } + agent_slide_right(s); + return true; +} + +bool toggle_run(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + bool run = gAgent.getAlwaysRun(); + if (run) + { + gAgent.clearAlwaysRun(); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i +// gAgent.clearRunning(); +// [/RLVa:KB] + } + else + { + gAgent.setAlwaysRun(); +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i +// gAgent.setRunning(); +// [/RLVa:KB] + } +// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i +// gAgent.sendWalkRun(!run); +// [/RLVa:KB] + return true; +} + +bool toggle_sit(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + if (gAgent.isSitting()) + { + gAgent.standUp(); + } + else + { + gAgent.sitDown(); + } + return true; +} + +bool toggle_pause_media(EKeystate s) // analogue of play/pause button in top bar +{ + if (KEYSTATE_DOWN != s) return true; + bool pause = LLViewerMedia::getInstance()->isAnyMediaPlaying(); + LLViewerMedia::getInstance()->setAllMediaPaused(pause); + return true; +} + +bool toggle_enable_media(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + bool pause = LLViewerMedia::getInstance()->isAnyMediaPlaying() || LLViewerMedia::getInstance()->isAnyMediaShowing(); + LLViewerMedia::getInstance()->setAllMediaEnabled(!pause); + return true; +} + +bool walk_to(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + return LLToolPie::getInstance()->walkToClickedLocation(); +} + +bool teleport_to(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + return LLToolPie::getInstance()->teleportToClickedLocation(); +} + +bool toggle_voice(EKeystate s) +{ + if (KEYSTATE_DOWN != s) return true; + if (!LLAgent::isActionAllowed("speak")) return false; + LLVoiceClient::getInstance()->toggleUserPTTState(); + return true; +} + +bool voice_follow_key(EKeystate s) +{ + if (KEYSTATE_DOWN == s) + { + if (!LLAgent::isActionAllowed("speak")) return false; + LLVoiceClient::getInstance()->setUserPTTState(true); + return true; + } + else if (KEYSTATE_UP == s && LLVoiceClient::getInstance()->getUserPTTState()) + { + LLVoiceClient::getInstance()->setUserPTTState(false); + return true; + } + return false; +} + +bool agen_control_lbutton_handle(EKeystate s) +{ + switch (s) + { + case KEYSTATE_DOWN: + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); + break; + case KEYSTATE_UP: + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); + break; + default: + break; + } + return true; +} + +#define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION); +REGISTER_KEYBOARD_ACTION("jump", agent_jump); +REGISTER_KEYBOARD_ACTION("push_down", agent_push_down); +REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward); +REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward); +REGISTER_KEYBOARD_ACTION("look_up", agent_look_up); +REGISTER_KEYBOARD_ACTION("look_down", agent_look_down); +REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly); +REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left); +REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right); +REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left); +REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right); +REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw); +REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw); +REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); +REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting); +REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over); +REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under); +REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting); +REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting); +REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward); +REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward); +REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting); +REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting); +REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up); +REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down); +REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left); +REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right); +REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in); +REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out); +REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast); +REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under); +REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward); +REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward); +REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving); +REGISTER_KEYBOARD_ACTION("start_chat", start_chat); +REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture); +REGISTER_KEYBOARD_ACTION("run_forward", run_forward); +REGISTER_KEYBOARD_ACTION("run_backward", run_backward); +REGISTER_KEYBOARD_ACTION("run_left", run_left); +REGISTER_KEYBOARD_ACTION("run_right", run_right); +REGISTER_KEYBOARD_ACTION("toggle_run", toggle_run); +REGISTER_KEYBOARD_ACTION("toggle_sit", toggle_sit); +REGISTER_KEYBOARD_ACTION("toggle_pause_media", toggle_pause_media); +REGISTER_KEYBOARD_ACTION("toggle_enable_media", toggle_enable_media); +REGISTER_KEYBOARD_ACTION("teleport_to", teleport_to); +REGISTER_KEYBOARD_ACTION("walk_to", walk_to); +REGISTER_KEYBOARD_ACTION("toggle_voice", toggle_voice); +REGISTER_KEYBOARD_ACTION("voice_follow_key", voice_follow_key); +#undef REGISTER_KEYBOARD_ACTION + +LLViewerInput::LLViewerInput() +{ + resetBindings(); + + for (S32 i = 0; i < KEY_COUNT; i++) + { + mKeyHandledByUI[i] = FALSE; + } + for (S32 i = 0; i < CLICK_COUNT; i++) + { + mMouseLevel[i] = MOUSE_STATE_SILENT; + } + // we want the UI to never see these keys so that they can always control the avatar/camera + for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++) + { + mKeysSkippedByUI.insert(k); + } +} + +// static +BOOL LLViewerInput::modeFromString(const std::string& string, S32 *mode) +{ + if (string == "FIRST_PERSON") + { + *mode = MODE_FIRST_PERSON; + return TRUE; + } + else if (string == "THIRD_PERSON") + { + *mode = MODE_THIRD_PERSON; + return TRUE; + } + else if (string == "EDIT_AVATAR") + { + *mode = MODE_EDIT_AVATAR; + return TRUE; + } + else if (string == "SITTING") + { + *mode = MODE_SITTING; + return TRUE; + } + else + { + *mode = MODE_THIRD_PERSON; + return FALSE; + } +} + +// static +BOOL LLViewerInput::mouseFromString(const std::string& string, EMouseClickType *mode) +{ + if (string == "LMB") + { + *mode = CLICK_LEFT; + return TRUE; + } + else if (string == "Double LMB") + { + *mode = CLICK_DOUBLELEFT; + return TRUE; + } + else if (string == "MMB") + { + *mode = CLICK_MIDDLE; + return TRUE; + } + else if (string == "MB4") + { + *mode = CLICK_BUTTON4; + return TRUE; + } + else if (string == "MB5") + { + *mode = CLICK_BUTTON5; + return TRUE; + } + else + { + *mode = CLICK_NONE; + return FALSE; + } +} + +BOOL LLViewerInput::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated) +{ + // check for re-map + EKeyboardMode mode = gViewerInput.getMode(); + U32 keyidx = (translated_mask<<16) | translated_key; + key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx); + if (iter != mRemapKeys[mode].end()) + { + translated_key = (iter->second) & 0xff; + translated_mask = (iter->second)>>16; + } + + // No repeats of F-keys + BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12); + if (!repeatable_key && repeated) + { + return FALSE; + } + + LL_DEBUGS("UserInput") << "keydown -" << translated_key << "-" << LL_ENDL; + // skip skipped keys + if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end()) + { + mKeyHandledByUI[translated_key] = FALSE; + //LL_INFOS("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; + LL_INFOS_ONCE("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; // Change log message to not print out every time + } + else + { + // it is sufficient to set this value once per call to handlekey + // without clearing it, as it is only used in the subsequent call to scanKey + mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); + // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress) + // NOT whether some UI shortcut wishes to handle the keypress + + } + return mKeyHandledByUI[translated_key]; +} + +BOOL LLViewerInput::handleKeyUp(KEY translated_key, MASK translated_mask) +{ + return gViewerWindow->handleKeyUp(translated_key, translated_mask); +} + +BOOL LLViewerInput::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name) +{ + S32 index; + typedef boost::function function_t; + function_t function = NULL; + std::string name; + + // Allow remapping of F2-F12 + if (function_name[0] == 'F') + { + int c1 = function_name[1] - '0'; + int c2 = function_name[2] ? function_name[2] - '0' : -1; + if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9) + { + int idx = c1; + if (c2 >= 0) + idx = idx*10 + c2; + if (idx >=2 && idx <= 12) + { + U32 keyidx = ((mask<<16)|key); + (mRemapKeys[mode])[keyidx] = ((0<<16)|(KEY_F1+(idx-1))); + return TRUE; + } + } + } + + // Not remapped, look for a function + + function_t* result = LLKeyboardActionRegistry::getValue(function_name); + if (result) + { + function = *result; + } + + if (!function) + { + LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; + return FALSE; + } + + // check for duplicate first and overwrite + S32 size = mKeyBindings[mode].size(); + for (index = 0; index < size; index++) + { + if (key == mKeyBindings[mode][index].mKey && mask == mKeyBindings[mode][index].mMask) + break; + } + + if (mode >= MODE_COUNT) + { + LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; + return FALSE; + } + + LLKeyboardBinding bind; + bind.mKey = key; + bind.mMask = mask; + bind.mFunction = function; + + mKeyBindings[mode].push_back(bind); + + return TRUE; +} + +BOOL LLViewerInput::bindMouse(const S32 mode, const EMouseClickType mouse, const MASK mask, const std::string& function_name) +{ + S32 index; + typedef boost::function function_t; + function_t function = NULL; + + function_t* result = LLKeyboardActionRegistry::getValue(function_name); + if (result) + { + function = *result; + } + + if (!function) + { + LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; + return FALSE; + } + + // check for duplicate first and overwrite + S32 size = mMouseBindings[mode].size(); + for (index = 0; index < size; index++) + { + if (mouse == mMouseBindings[mode][index].mMouse && mask == mMouseBindings[mode][index].mMask) + break; + } + + if (mode >= MODE_COUNT) + { + LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; + return FALSE; + } + + LLMouseBinding bind; + bind.mMouse = mouse; + bind.mMask = mask; + bind.mFunction = function; + + mMouseBindings[mode].push_back(bind); + + return TRUE; +} + +LLViewerInput::KeyBinding::KeyBinding() +: key("key"), + mouse("mouse"), + mask("mask"), + command("command") +{} + +LLViewerInput::KeyMode::KeyMode() +: bindings("binding") +{} + +LLViewerInput::Keys::Keys() +: first_person("first_person"), + third_person("third_person"), + sitting("sitting"), + edit_avatar("edit_avatar") +{} + +void LLViewerInput::resetBindings() +{ + for (S32 i = 0; i < MODE_COUNT; i++) + { + mKeyBindings[i].clear(); + mMouseBindings[i].clear(); + } +} + +S32 LLViewerInput::loadBindingsXML(const std::string& filename) +{ + resetBindings(); + + S32 binding_count = 0; + Keys keys; + LLSimpleXUIParser parser; + + if (parser.readXUI(filename, keys) + && keys.validateBlock()) + { + binding_count += loadBindingMode(keys.first_person, MODE_FIRST_PERSON); + binding_count += loadBindingMode(keys.third_person, MODE_THIRD_PERSON); + binding_count += loadBindingMode(keys.sitting, MODE_SITTING); + binding_count += loadBindingMode(keys.edit_avatar, MODE_EDIT_AVATAR); + } + return binding_count; +} + +S32 count_masks(const MASK &mask) +{ + S32 res = 0; + if (mask & MASK_CONTROL) + { + res++; + } + if (mask & MASK_SHIFT) + { + res++; + } + if (mask & MASK_ALT) + { + res++; + } + return res; +} + +bool compare_key_by_mask(LLKeyboardBinding i1, LLKeyboardBinding i2) +{ + return (count_masks(i1.mMask) > count_masks(i2.mMask)); +} + +bool compare_mouse_by_mask(LLMouseBinding i1, LLMouseBinding i2) +{ + return (count_masks(i1.mMask) > count_masks(i2.mMask)); +} + +S32 LLViewerInput::loadBindingMode(const LLViewerInput::KeyMode& keymode, S32 mode) +{ + S32 binding_count = 0; + for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), + end_it = keymode.bindings.end(); + it != end_it; + ++it) + { + bool processed = false; + std::string key_str = it->key.getValue(); + if (!key_str.empty() && key_str != "NONE") + { + KEY key; + LLKeyboard::keyFromString(key_str, &key); + if (key != KEY_NONE) + { + MASK mask; + LLKeyboard::maskFromString(it->mask, &mask); + bindKey(mode, key, mask, it->command); + processed = true; + } + else + { + LL_WARNS_ONCE() << "There might be issues in keybindings' file" << LL_ENDL; + } + } + if (!processed && it->mouse.isProvided() && !it->mouse.getValue().empty()) + { + EMouseClickType mouse; + mouseFromString(it->mouse.getValue(), &mouse); + if (mouse != CLICK_NONE) + { + MASK mask; + LLKeyboard::maskFromString(it->mask, &mask); + bindMouse(mode, mouse, mask, it->command); + processed = true; + } + else + { + LL_WARNS_ONCE() << "There might be issues in keybindings' file" << LL_ENDL; + } + } + if (processed) + { + // total + binding_count++; + } + } + + // sort lists by mask (so that Shift+W is executed before W, if both are assigned, but if Shift+W is not assigned W should be executed) + std::sort(mKeyBindings[mode].begin(), mKeyBindings[mode].end(), compare_key_by_mask); + std::sort(mMouseBindings[mode].begin(), mMouseBindings[mode].end(), compare_mouse_by_mask); + + return binding_count; +} + +EKeyboardMode LLViewerInput::getMode() const +{ + if ( gAgentCamera.cameraMouselook() ) + { + return MODE_FIRST_PERSON; + } + else if ( gMorphView && gMorphView->getVisible()) + { + return MODE_EDIT_AVATAR; + } + else if (isAgentAvatarValid() && gAgentAvatarp->isSitting()) + { + return MODE_SITTING; + } + else + { + return MODE_THIRD_PERSON; + } +} + +bool LLViewerInput::scanKey(const std::vector &binding, + S32 binding_count, + KEY key, + MASK mask, + BOOL key_down, + BOOL key_up, + BOOL key_level, + bool repeat) const +{ + for (S32 i = 0; i < binding_count; i++) + { + if (binding[i].mKey == key) + { + if ((binding[i].mMask & mask) == binding[i].mMask) + { + bool res = false; + if (key_down && !repeat) + { + // ...key went down this frame, call function + res = binding[i].mFunction( KEYSTATE_DOWN ); + return true; + } + else if (key_up) + { + // ...key went down this frame, call function + res = binding[i].mFunction( KEYSTATE_UP ); + } + else if (key_level) + { + // ...key held down from previous frame + // Not windows, just call the function. + res = binding[i].mFunction( KEYSTATE_LEVEL ); + }//if + // Key+Mask combinations are supposed to be unique, so we won't find anything else + return res; + }//if + }//if + }//for + return false; +} + +// Called from scanKeyboard. +bool LLViewerInput::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) const +{ + if (LLApp::isExiting()) + { + return false; + } + + S32 mode = getMode(); + // Consider keyboard scanning as NOT mouse event. JC + MASK mask = gKeyboard->currentMask(FALSE); + + if (mKeyHandledByUI[key]) + { + return false; + } + + // don't process key down on repeated keys + BOOL repeat = gKeyboard->getKeyRepeated(key); + + bool res = scanKey(mKeyBindings[mode], mKeyBindings[mode].size(), key, mask, key_down, key_up, key_level, repeat); + + if (!res && agent_control_lbutton.canHandle(CLICK_NONE, key, mask)) + { + if (key_down && !repeat) + { + res = agen_control_lbutton_handle(KEYSTATE_DOWN); + } + if (key_up) + { + res = agen_control_lbutton_handle(KEYSTATE_UP); + } + } + return res; +} + +BOOL LLViewerInput::handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) +{ + BOOL handled = gViewerWindow->handleAnyMouseClick(window_impl, pos, mask, clicktype, down); + + if (clicktype != CLICK_NONE) + { + // Special case + // If UI doesn't handle double click, LMB click is issued, so supres LMB 'down' when doubleclick is set + // handle !down as if we are handling doubleclick + + bool double_click_sp = (clicktype == CLICK_LEFT + && (mMouseLevel[CLICK_DOUBLELEFT] != MOUSE_STATE_SILENT) + && mMouseLevel[CLICK_LEFT] == MOUSE_STATE_SILENT); + if (double_click_sp && !down) + { + // Process doubleclick instead + clicktype = CLICK_DOUBLELEFT; + } + + + if (double_click_sp && down) + { + // Consume click. + // Due to handling, double click that is not handled will be immediately followed by LMB click + } + // If UI handled 'down', it should handle 'up' as well + // If we handle 'down' not by UI, then we should handle 'up'/'level' regardless of UI + else if (handled) + { + // UI handled new 'down' so iterupt whatever state we were in. + if (mMouseLevel[clicktype] != MOUSE_STATE_SILENT) + { + if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) + { + mMouseLevel[clicktype] = MOUSE_STATE_CLICK; + } + else + { + mMouseLevel[clicktype] = MOUSE_STATE_UP; + } + } + } + else if (down) + { + if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) + { + // this is repeated hit (mouse does not repeat event until release) + // for now treat rapid clicking like mouse being held + mMouseLevel[clicktype] = MOUSE_STATE_LEVEL; + } + else + { + mMouseLevel[clicktype] = MOUSE_STATE_DOWN; + } + } + else if (mMouseLevel[clicktype] != MOUSE_STATE_SILENT) + { + // Released mouse key + if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) + { + mMouseLevel[clicktype] = MOUSE_STATE_CLICK; + } + else + { + mMouseLevel[clicktype] = MOUSE_STATE_UP; + } + } + } + + return handled; +} + +bool LLViewerInput::scanMouse(const std::vector &binding, S32 binding_count, EMouseClickType mouse, MASK mask, EMouseState state) const +{ + for (S32 i = 0; i < binding_count; i++) + { + if (binding[i].mMouse == mouse && (binding[i].mMask & mask) == binding[i].mMask) + { + bool res = false; + switch (state) + { + case MOUSE_STATE_DOWN: + res = binding[i].mFunction(KEYSTATE_DOWN); + break; + case MOUSE_STATE_CLICK: + // Button went down and up in scope of single frame + // might not work best with some functions, + // but some function need specific states specifically + res = binding[i].mFunction(KEYSTATE_DOWN); + res |= binding[i].mFunction(KEYSTATE_UP); + break; + case MOUSE_STATE_LEVEL: + res = binding[i].mFunction(KEYSTATE_LEVEL); + break; + case MOUSE_STATE_UP: + res = binding[i].mFunction(KEYSTATE_UP); + break; + default: + break; + } + // Key+Mask combinations are supposed to be unique, no need to continue + return res; + } + } + return false; +} + +// todo: this recods key, scanMouse() triggers functions with EKeystate +bool LLViewerInput::scanMouse(EMouseClickType click, EMouseState state) const +{ + bool res = false; + S32 mode = getMode(); + MASK mask = gKeyboard->currentMask(TRUE); + res = scanMouse(mMouseBindings[mode], mMouseBindings[mode].size(), click, mask, state); + // no user defined actions found or those actions can't handle the key/button, handle control if nessesary + if (!res && agent_control_lbutton.canHandle(click, KEY_NONE, mask)) + { + switch (state) + { + case MOUSE_STATE_DOWN: + agen_control_lbutton_handle(KEYSTATE_DOWN); + res = true; + break; + case MOUSE_STATE_CLICK: + // might not work best with some functions, + // but some function need specific states too specifically + agen_control_lbutton_handle(KEYSTATE_DOWN); + agen_control_lbutton_handle(KEYSTATE_UP); + res = true; + break; + case MOUSE_STATE_UP: + agen_control_lbutton_handle(KEYSTATE_UP); + res = true; + break; + default: + break; + } + } + return res; +} + +void LLViewerInput::scanMouse() +{ + for (S32 i = 0; i < CLICK_COUNT; i++) + { + if (mMouseLevel[i] != MOUSE_STATE_SILENT) + { + scanMouse((EMouseClickType)i, mMouseLevel[i]); + if (mMouseLevel[i] == MOUSE_STATE_DOWN) + { + // mouse doesn't support 'continued' state, so after handling, switch to LEVEL + mMouseLevel[i] = MOUSE_STATE_LEVEL; + } + else if (mMouseLevel[i] == MOUSE_STATE_UP || mMouseLevel[i] == MOUSE_STATE_CLICK) + { + mMouseLevel[i] = MOUSE_STATE_SILENT; + } + } + } +} + +bool LLViewerInput::isMouseBindUsed(const EMouseClickType mouse, const MASK mask, const S32 mode) +{ + S32 size = mMouseBindings[mode].size(); + for (S32 index = 0; index < size; index++) + { + if (mouse == mMouseBindings[mode][index].mMouse && mask == mMouseBindings[mode][index].mMask) + return true; + } + return false; +} diff --git a/indra/newview/llviewerinput.h b/indra/newview/llviewerinput.h new file mode 100644 index 0000000000..8461e0cd9b --- /dev/null +++ b/indra/newview/llviewerinput.h @@ -0,0 +1,179 @@ +/** + * @file llviewerinput.h + * @brief LLViewerInput class header file + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLVIEWERINPUT_H +#define LL_LLVIEWERINPUT_H + +#include "llkeyboard.h" // For EKeystate +#include "llinitparam.h" + +const S32 MAX_KEY_BINDINGS = 128; // was 60 + +class LLNamedFunction +{ +public: + LLNamedFunction() : mFunction(NULL) { }; + ~LLNamedFunction() { }; + + std::string mName; + LLKeyFunc mFunction; +}; + +class LLKeyboardBinding +{ +public: + KEY mKey; + MASK mMask; + + LLKeyFunc mFunction; +}; + +class LLMouseBinding +{ +public: + EMouseClickType mMouse; + MASK mMask; + + LLKeyFunc mFunction; +}; + + +typedef enum e_keyboard_mode +{ + MODE_FIRST_PERSON, + MODE_THIRD_PERSON, + MODE_EDIT_AVATAR, + MODE_SITTING, + MODE_COUNT +} EKeyboardMode; + +class LLWindow; + +void bind_keyboard_functions(); + +class LLViewerInput +{ +public: + struct KeyBinding : public LLInitParam::Block + { + Mandatory key, + mask, + command; + Optional mouse; // Note, not mandatory for the sake of backward campatibility with keys.xml + + KeyBinding(); + }; + + struct KeyMode : public LLInitParam::Block + { + Multiple bindings; + + KeyMode(); + }; + + struct Keys : public LLInitParam::Block + { + Optional first_person, + third_person, + sitting, + edit_avatar; + + Keys(); + }; + + LLViewerInput(); + + BOOL handleKey(KEY key, MASK mask, BOOL repeated); + BOOL handleKeyUp(KEY key, MASK mask); + + S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error + EKeyboardMode getMode() const; + + static BOOL modeFromString(const std::string& string, S32 *mode); // False on failure + static BOOL mouseFromString(const std::string& string, EMouseClickType *mode);// False on failure + + bool scanKey(KEY key, + BOOL key_down, + BOOL key_up, + BOOL key_level) const; + + // handleMouse() records state, scanMouse() goes through states, scanMouse(click) processes individual saved states after UI is done with them + BOOL handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); + void scanMouse(); + + bool isMouseBindUsed(const EMouseClickType mouse, const MASK mask = MASK_NONE, const S32 mode = MODE_THIRD_PERSON); + +private: + bool scanKey(const std::vector &binding, + S32 binding_count, + KEY key, + MASK mask, + BOOL key_down, + BOOL key_up, + BOOL key_level, + bool repeat) const; + + enum EMouseState + { + MOUSE_STATE_DOWN, // key down this frame + MOUSE_STATE_CLICK, // key went up and down in scope of same frame + MOUSE_STATE_LEVEL, // clicked again fast, or never released + MOUSE_STATE_UP, // went up this frame + MOUSE_STATE_SILENT // notified about 'up', do not notify again + }; + bool scanMouse(EMouseClickType click, EMouseState state) const; + bool scanMouse(const std::vector &binding, + S32 binding_count, + EMouseClickType mouse, + MASK mask, + EMouseState state) const; + + S32 loadBindingMode(const LLViewerInput::KeyMode& keymode, S32 mode); + BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name); + BOOL bindMouse(const S32 mode, const EMouseClickType mouse, const MASK mask, const std::string& function_name); + void resetBindings(); + + // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here + + // TODO: at some point it is better to remake this, especially keyaboard part + // would be much better to send to functions actual state of the button than + // to send what we think function wants based on collection of bools (mKeyRepeated, mKeyLevel, mKeyDown) + std::vector mKeyBindings[MODE_COUNT]; + std::vector mMouseBindings[MODE_COUNT]; + + typedef std::map key_remap_t; + key_remap_t mRemapKeys[MODE_COUNT]; + std::set mKeysSkippedByUI; + BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI + + // This is indentical to what llkeyboard does (mKeyRepeated, mKeyLevel, mKeyDown e t c), + // just instead of remembering individually as bools, we record state as enum + EMouseState mMouseLevel[CLICK_COUNT]; // records of key state +}; + +extern LLViewerInput gViewerInput; +bool agent_push_forward(EKeystate s);// Mouse movement by Singularity +#endif // LL_LLVIEWERINPUT_H diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index d8bb7c1960..030f0b6cd2 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -1427,6 +1427,8 @@ bool LLViewerJoystick::isLikeSpaceNavigator() const #if LIB_NDOF return (isJoystickInitialized() && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0 + // FIRE-30846 - Add combined product string that is returned by some devices + || strncmp(mNdofDev->product, "3Dconnexion SpaceNavigator", 26) == 0 || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0 || strncmp(mNdofDev->product, "SpaceTraveler", 13) == 0 || strncmp(mNdofDev->product, "SpacePilot", 10) == 0)); diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp deleted file mode 100644 index f1f9f9e34e..0000000000 --- a/indra/newview/llviewerkeyboard.cpp +++ /dev/null @@ -1,1111 +0,0 @@ -/** - * @file llviewerkeyboard.cpp - * @brief LLViewerKeyboard class implementation - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llappviewer.h" -#include "llfloaterreg.h" -#include "llviewerkeyboard.h" -#include "llmath.h" -#include "llagent.h" -#include "llagentcamera.h" -// [FS Communication UI] -// #include "llfloaterimnearbychat.h" -#include "fsnearbychathub.h" -#include "lllineeditor.h" -// [FS Communication UI] -#include "llviewercontrol.h" -#include "llfocusmgr.h" -#include "llmorphview.h" -#include "llmoveview.h" -#include "lltoolfocus.h" -#include "llviewerwindow.h" -#include "llvoavatarself.h" -#include "llfloatercamera.h" -#include "llinitparam.h" -#include "llfloaterwebcontent.h" -#include "fsfloatersearch.h" - -// -// Constants -// - -const F32 FLY_TIME = 0.5f; -const F32 FLY_FRAMES = 4; - -const F32 NUDGE_TIME = 0.25f; // in seconds -const S32 NUDGE_FRAMES = 2; -const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed - -struct LLKeyboardActionRegistry -: public LLRegistrySingleton, LLKeyboardActionRegistry> -{ - LLSINGLETON_EMPTY_CTOR(LLKeyboardActionRegistry); -}; - -LLViewerKeyboard gViewerKeyboard; - -void agent_jump( EKeystate s ) -{ - static BOOL first_fly_attempt(TRUE); - if (KEYSTATE_UP == s) - { - first_fly_attempt = TRUE; - return; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - // Chalice Yao's crouch toggle - if (gSavedPerAccountSettings.getBOOL("FSCrouchToggleStatus")) - { - gSavedPerAccountSettings.setBOOL("FSCrouchToggleStatus", FALSE); - } - // - - if( time < FLY_TIME - || frame_count <= FLY_FRAMES - || gAgent.upGrabbed() - || !gSavedSettings.getBOOL("AutomaticFly")) - { - gAgent.moveUp(1); - } - else - { - gAgent.setFlying(TRUE, first_fly_attempt); - first_fly_attempt = FALSE; - gAgent.moveUp(1); - } -} - -void agent_push_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - // Chalice Yao's crouch toggle - //gAgent.moveUp(-1); - else if (KEYSTATE_DOWN == s && !gAgent.getFlying() && !gAgentAvatarp->isSitting() && gSavedPerAccountSettings.getBOOL("FSCrouchToggle")) - { - if (gSavedPerAccountSettings.getBOOL("FSCrouchToggleStatus")) - { - gSavedPerAccountSettings.setBOOL("FSCrouchToggleStatus", FALSE); - } - else - { - gSavedPerAccountSettings.setBOOL("FSCrouchToggleStatus", TRUE); - gAgent.moveUp(-1); - } - } - else - { - gAgent.moveUp(-1); - } - // -} - -static void agent_check_temporary_run(LLAgent::EDoubleTapRunMode mode) -{ -// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i - if ( (gAgent.mDoubleTapRunMode == mode) && (gAgent.getTempRun()) ) - gAgent.clearTempRun(); -// [/RLVa:KB] -// if (gAgent.mDoubleTapRunMode == mode && -// gAgent.getRunning() && -// !gAgent.getAlwaysRun()) -// { -// // Turn off temporary running. -// gAgent.clearRunning(); -// gAgent.sendWalkRun(gAgent.getRunning()); -// } -} - -static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode mode) -{ - if (KEYSTATE_UP == s) - { - // Note: in case shift is already released, slide left/right run - // will be released in agent_turn_left()/agent_turn_right() - agent_check_temporary_run(mode); - } - else if (gSavedSettings.getBOOL("AllowTapTapHoldRun") && - KEYSTATE_DOWN == s && - !gAgent.getRunning()) - { - if (gAgent.mDoubleTapRunMode == mode && - gAgent.mDoubleTapRunTimer.getElapsedTimeF32() < NUDGE_TIME) - { - // Same walk-key was pushed again quickly; this is a - // double-tap so engage temporary running. -// gAgent.setRunning(); -// gAgent.sendWalkRun(gAgent.getRunning()); -// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i - gAgent.setTempRun(); -// [/RLVa:KB] - } - - // Pressing any walk-key resets the double-tap timer - gAgent.mDoubleTapRunTimer.reset(); - gAgent.mDoubleTapRunMode = mode; - } -} - -static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) -{ - agent_handle_doubletap_run(s, mode); - if (KEYSTATE_UP == s) return; - - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) - { - gAgent.moveAtNudge(direction); - } - else - { - gAgent.moveAt(direction); - } -} - -void camera_move_forward( EKeystate s ); - -void agent_push_forward( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_move_forward(s); - } - else - { - agent_push_forwardbackward(s, 1, LLAgent::DOUBLETAP_FORWARD); - } -} - -void camera_move_backward( EKeystate s ); - -void agent_push_backward( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_move_backward(s); - } - else if (!gAgent.backwardGrabbed() && gAgentAvatarp->isSitting() && gSavedSettings.getBOOL("LeaveMouselook")) - { - gAgentCamera.changeCameraToThirdPerson(); - } - else - { - agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD); - } -} - -static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) -{ - agent_handle_doubletap_run(s, mode); - if( KEYSTATE_UP == s ) return; - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) - { - gAgent.moveLeftNudge(direction); - } - else - { - gAgent.moveLeft(direction); - } -} - - -void agent_slide_left( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - agent_slide_leftright(s, 1, LLAgent::DOUBLETAP_SLIDELEFT); -} - - -void agent_slide_right( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return; - agent_slide_leftright(s, -1, LLAgent::DOUBLETAP_SLIDERIGHT); -} - -void camera_spin_around_cw( EKeystate s ); - -void agent_turn_left( EKeystate s ) -{ - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_spin_around_cw(s); - return; - } - - if(gAgent.isMovementLocked()) return; - - if (LLToolCamera::getInstance()->mouseSteerMode()) - { - agent_slide_left(s); - } - else - { - if (KEYSTATE_UP == s) - { - // Check temporary running. In case user released 'left' key with shift already released. - agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDELEFT); - return; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - gAgent.moveYaw( LLFloaterMove::getYawRate( time ) ); - } -} - -void camera_spin_around_ccw( EKeystate s ); - -void agent_turn_right( EKeystate s ) -{ - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_spin_around_ccw(s); - return; - } - - if(gAgent.isMovementLocked()) return; - - if (LLToolCamera::getInstance()->mouseSteerMode()) - { - agent_slide_right(s); - } - else - { - if (KEYSTATE_UP == s) - { - // Check temporary running. In case user released 'right' key with shift already released. - agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDERIGHT); - return; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) ); - } -} - -void agent_look_up( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgent.movePitch(-1); - //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); -} - - -void agent_look_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgent.movePitch(1); - //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); -} - -void agent_toggle_fly( EKeystate s ) -{ - // Only catch the edge - if (KEYSTATE_DOWN == s ) - { - LLAgent::toggleFlying(); - } -} - -F32 get_orbit_rate() -{ - F32 time = gKeyboard->getCurKeyElapsedTime(); - if( time < NUDGE_TIME ) - { - F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME; - //LL_INFOS() << rate << LL_ENDL; - return rate; - } - else - { - return 1; - } -} - -void camera_spin_around_ccw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); -} - - -void camera_spin_around_cw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); -} - -void camera_spin_around_ccw_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT ) return; - if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) - { - //send keystrokes, but do not change camera - agent_turn_right(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.unlockView(); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); - } -} - - -void camera_spin_around_cw_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT ) return; - if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) - { - //send keystrokes, but do not change camera - agent_turn_left(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.unlockView(); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); - } -} - - -void camera_spin_over( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); -} - - -void camera_spin_under( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); -} - -void camera_spin_over_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - if (gAgent.upGrabbed() || gAgentCamera.sitCameraEnabled()) - { - //send keystrokes, but do not change camera - agent_jump(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); - } -} - - -void camera_spin_under_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - if (gAgent.downGrabbed() || gAgentCamera.sitCameraEnabled()) - { - //send keystrokes, but do not change camera - agent_push_down(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); - } -} - -void camera_move_forward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitInKey( get_orbit_rate() ); -} - - -void camera_move_backward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); -} - -void camera_move_forward_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD ) return; - if (gAgent.forwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) - { - agent_push_forward(s); - } - else - { - gAgentCamera.setOrbitInKey( get_orbit_rate() ); - } -} - - -void camera_move_backward_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD ) return; - - if (gAgent.backwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) - { - agent_push_backward(s); - } - else - { - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); - } -} - -void camera_pan_up( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanUpKey( get_orbit_rate() ); -} - -void camera_pan_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanDownKey( get_orbit_rate() ); -} - -void camera_pan_left( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanLeftKey( get_orbit_rate() ); -} - -void camera_pan_right( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanRightKey( get_orbit_rate() ); -} - -void camera_pan_in( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanInKey( get_orbit_rate() ); -} - -void camera_pan_out( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setPanOutKey( get_orbit_rate() ); -} - -void camera_move_forward_fast( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitInKey(2.5f); -} - -void camera_move_backward_fast( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitOutKey(2.5f); -} - - -void edit_avatar_spin_ccw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); - //gMorphView->orbitLeft( get_orbit_rate() ); -} - - -void edit_avatar_spin_cw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); - //gMorphView->orbitRight( get_orbit_rate() ); -} - -void edit_avatar_spin_over( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); - //gMorphView->orbitUp( get_orbit_rate() ); -} - - -void edit_avatar_spin_under( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); - //gMorphView->orbitDown( get_orbit_rate() ); -} - -void edit_avatar_move_forward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitInKey( get_orbit_rate() ); - //gMorphView->orbitIn(); -} - - -void edit_avatar_move_backward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); - //gMorphView->orbitOut(); -} - -void stop_moving( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return; - // stop agent - gAgent.setControlFlags(AGENT_CONTROL_STOP); - - // cancel autopilot - gAgent.stopAutoPilot(); -} - -void start_chat( EKeystate s ) -{ - if (LLAppViewer::instance()->quitRequested()) - { - return; // can't talk, gotta go, kthxbye! - } - - // start chat - // [FS Communication UI] - //LLFloaterIMNearbyChat::startChat(NULL); - FSNearbyChat::instance().showDefaultChatBar(TRUE); - // [FS Communication UI] -} - -void start_gesture( EKeystate s ) -{ - // If avatar is pointing at something, don't start gesture. - // This works around the bug with Shared Media prims. - if (gAgentCamera.mPointAtObject) - { - return; - } - - // FIRE-4167: Don't start gesture if a floater with web content has focus - LLFloater* focused_floater = gFloaterView->getFocusedFloater(); - if (focused_floater && (dynamic_cast(focused_floater) || dynamic_cast(focused_floater))) - { - return; - } - - LLUICtrl* focus_ctrlp = dynamic_cast(gFocusMgr.getKeyboardFocus()); - if (KEYSTATE_UP == s && - ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) - { - // [FS Communication UI] - //if ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) - //{ - // // No existing chat in chat editor, insert '/' - // LLFloaterIMNearbyChat::startChat("/"); - //} - //else - //{ - // // Don't overwrite existing text in chat editor - // LLFloaterIMNearbyChat::startChat(NULL); - //} - - FSNearbyChat::instance().showDefaultChatBar(TRUE, "/"); - // [FS Communication UI] - } -} - -#define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION); -REGISTER_KEYBOARD_ACTION("jump", agent_jump); -REGISTER_KEYBOARD_ACTION("push_down", agent_push_down); -REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward); -REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward); -REGISTER_KEYBOARD_ACTION("look_up", agent_look_up); -REGISTER_KEYBOARD_ACTION("look_down", agent_look_down); -REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly); -REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left); -REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right); -REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left); -REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right); -REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw); -REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw); -REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); -REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting); -REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over); -REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under); -REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting); -REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting); -REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward); -REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward); -REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting); -REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting); -REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up); -REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down); -REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left); -REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right); -REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in); -REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out); -REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast); -REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under); -REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward); -REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward); -REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving); -REGISTER_KEYBOARD_ACTION("start_chat", start_chat); -REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture); -#undef REGISTER_KEYBOARD_ACTION - -LLViewerKeyboard::LLViewerKeyboard() -{ - for (S32 i = 0; i < MODE_COUNT; i++) - { - mBindingCount[i] = 0; - } - - for (S32 i = 0; i < KEY_COUNT; i++) - { - mKeyHandledByUI[i] = FALSE; - } - // we want the UI to never see these keys so that they can always control the avatar/camera - for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++) - { - mKeysSkippedByUI.insert(k); - } -} - -BOOL LLViewerKeyboard::modeFromString(const std::string& string, S32 *mode) -{ - if (string == "FIRST_PERSON") - { - *mode = MODE_FIRST_PERSON; - return TRUE; - } - else if (string == "THIRD_PERSON") - { - *mode = MODE_THIRD_PERSON; - return TRUE; - } - else if (string == "EDIT") - { - *mode = MODE_EDIT; - return TRUE; - } - else if (string == "EDIT_AVATAR") - { - *mode = MODE_EDIT_AVATAR; - return TRUE; - } - else if (string == "SITTING") - { - *mode = MODE_SITTING; - return TRUE; - } - else - { - *mode = MODE_THIRD_PERSON; - return FALSE; - } -} - -BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated) -{ - // check for re-map - EKeyboardMode mode = gViewerKeyboard.getMode(); - U32 keyidx = (translated_mask<<16) | translated_key; - key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx); - if (iter != mRemapKeys[mode].end()) - { - translated_key = (iter->second) & 0xff; - translated_mask = (iter->second)>>16; - } - - // No repeats of F-keys - BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12); - if (!repeatable_key && repeated) - { - return FALSE; - } - - LL_DEBUGS("UserInput") << "keydown -" << translated_key << "-" << LL_ENDL; - // skip skipped keys - if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end()) - { - mKeyHandledByUI[translated_key] = FALSE; - //LL_INFOS("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; - LL_INFOS_ONCE("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; // Change log message to not print out every time - } - else - { - // it is sufficient to set this value once per call to handlekey - // without clearing it, as it is only used in the subsequent call to scanKey - mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); - // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress) - // NOT whether some UI shortcut wishes to handle the keypress - - } - return mKeyHandledByUI[translated_key]; -} - -BOOL LLViewerKeyboard::handleKeyUp(KEY translated_key, MASK translated_mask) -{ - return gViewerWindow->handleKeyUp(translated_key, translated_mask); -} - -BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name) -{ - S32 index; - typedef boost::function function_t; - function_t function = NULL; - std::string name; - - // Allow remapping of F2-F12 - if (function_name[0] == 'F') - { - int c1 = function_name[1] - '0'; - int c2 = function_name[2] ? function_name[2] - '0' : -1; - if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9) - { - int idx = c1; - if (c2 >= 0) - idx = idx*10 + c2; - if (idx >=2 && idx <= 12) - { - U32 keyidx = ((mask<<16)|key); - (mRemapKeys[mode])[keyidx] = ((0<<16)|(KEY_F1+(idx-1))); - return TRUE; - } - } - } - - // Not remapped, look for a function - - function_t* result = LLKeyboardActionRegistry::getValue(function_name); - if (result) - { - function = *result; - } - - if (!function) - { - LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; - return FALSE; - } - - // check for duplicate first and overwrite - for (index = 0; index < mBindingCount[mode]; index++) - { - if (key == mBindings[mode][index].mKey && mask == mBindings[mode][index].mMask) - break; - } - - if (index >= MAX_KEY_BINDINGS) - { - LL_ERRS() << "LLKeyboard::bindKey() - too many keys for mode " << mode << LL_ENDL; - return FALSE; - } - - if (mode >= MODE_COUNT) - { - LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; - return FALSE; - } - - mBindings[mode][index].mKey = key; - mBindings[mode][index].mMask = mask; - mBindings[mode][index].mFunction = function; - - if (index == mBindingCount[mode]) - mBindingCount[mode]++; - - return TRUE; -} - -LLViewerKeyboard::KeyBinding::KeyBinding() -: key("key"), - mask("mask"), - command("command") -{} - -LLViewerKeyboard::KeyMode::KeyMode(EKeyboardMode _mode) -: bindings("binding"), - mode(_mode) -{} - -LLViewerKeyboard::Keys::Keys() -: first_person("first_person", KeyMode(MODE_FIRST_PERSON)), - third_person("third_person", KeyMode(MODE_THIRD_PERSON)), - edit("edit", KeyMode(MODE_EDIT)), - sitting("sitting", KeyMode(MODE_SITTING)), - edit_avatar("edit_avatar", KeyMode(MODE_EDIT_AVATAR)) -{} - -S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename) -{ - // Allow instant change of keyboard layout - for (S32 i = 0; i < MODE_COUNT; i++) - { - mBindingCount[i] = 0; - - for (S32 j = 0; j < MAX_KEY_BINDINGS; j++) - { - mBindings[i][j] = LLKeyBinding(); - } - } - // - - S32 binding_count = 0; - Keys keys; - LLSimpleXUIParser parser; - - if (parser.readXUI(filename, keys) - && keys.validateBlock()) - { - binding_count += loadBindingMode(keys.first_person); - binding_count += loadBindingMode(keys.third_person); - binding_count += loadBindingMode(keys.edit); - binding_count += loadBindingMode(keys.sitting); - binding_count += loadBindingMode(keys.edit_avatar); - } - return binding_count; -} - -S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode) -{ - S32 binding_count = 0; - for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), - end_it = keymode.bindings.end(); - it != end_it; - ++it) - { - KEY key; - MASK mask; - LLKeyboard::keyFromString(it->key, &key); - LLKeyboard::maskFromString(it->mask, &mask); - bindKey(keymode.mode, key, mask, it->command); - binding_count++; - } - - return binding_count; -} - -S32 LLViewerKeyboard::loadBindings(const std::string& filename) -{ - LLFILE *fp; - const S32 BUFFER_SIZE = 2048; - char buffer[BUFFER_SIZE]; /* Flawfinder: ignore */ - // *NOTE: This buffer size is hard coded into scanf() below. - char mode_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - char key_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - char mask_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - char function_string[MAX_STRING] = ""; /* Flawfinder: ignore */ - S32 mode = MODE_THIRD_PERSON; - KEY key = 0; - MASK mask = 0; - S32 tokens_read; - S32 binding_count = 0; - S32 line_count = 0; - - if(filename.empty()) - { - LL_ERRS() << " No filename specified" << LL_ENDL; - return 0; - } - - fp = LLFile::fopen(filename, "r"); - - if (!fp) - { - return 0; - } - - - while (!feof(fp)) - { - line_count++; - if (!fgets(buffer, BUFFER_SIZE, fp)) - break; - - // skip over comments, blank lines - if (buffer[0] == '#' || buffer[0] == '\n') continue; - - // grab the binding strings - tokens_read = sscanf( /* Flawfinder: ignore */ - buffer, - "%254s %254s %254s %254s", - mode_string, - key_string, - mask_string, - function_string); - - if (tokens_read == EOF) - { - LL_INFOS() << "Unexpected end-of-file at line " << line_count << " of key binding file " << filename << LL_ENDL; - fclose(fp); - return 0; - } - else if (tokens_read < 4) - { - LL_INFOS() << "Can't read line " << line_count << " of key binding file " << filename << LL_ENDL; - continue; - } - - // convert mode - if (!modeFromString(mode_string, &mode)) - { - LL_INFOS() << "Unknown mode on line " << line_count << " of key binding file " << filename << LL_ENDL; - LL_INFOS() << "Mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR" << LL_ENDL; - continue; - } - - // convert key - if (!LLKeyboard::keyFromString(key_string, &key)) - { - LL_INFOS() << "Can't interpret key on line " << line_count << " of key binding file " << filename << LL_ENDL; - continue; - } - - // convert mask - if (!LLKeyboard::maskFromString(mask_string, &mask)) - { - LL_INFOS() << "Can't interpret mask on line " << line_count << " of key binding file " << filename << LL_ENDL; - continue; - } - - // bind key - if (bindKey(mode, key, mask, function_string)) - { - binding_count++; - } - } - - fclose(fp); - - return binding_count; -} - - -EKeyboardMode LLViewerKeyboard::getMode() -{ - if ( gAgentCamera.cameraMouselook() ) - { - return MODE_FIRST_PERSON; - } - else if ( gMorphView && gMorphView->getVisible()) - { - return MODE_EDIT_AVATAR; - } - else if (isAgentAvatarValid() && gAgentAvatarp->isSitting()) - { - return MODE_SITTING; - } - else - { - return MODE_THIRD_PERSON; - } -} - - -// Called from scanKeyboard. -void LLViewerKeyboard::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) -{ - if (LLApp::isExiting()) - { - return; - } - - S32 mode = getMode(); - // Consider keyboard scanning as NOT mouse event. JC - MASK mask = gKeyboard->currentMask(FALSE); - - LLKeyBinding* binding = mBindings[mode]; - S32 binding_count = mBindingCount[mode]; - - - if (mKeyHandledByUI[key]) - { - return; - } - - // don't process key down on repeated keys - BOOL repeat = gKeyboard->getKeyRepeated(key); - - for (S32 i = 0; i < binding_count; i++) - { - //for (S32 key = 0; key < KEY_COUNT; key++) - if (binding[i].mKey == key) - { - //if (binding[i].mKey == key && binding[i].mMask == mask) - if (binding[i].mMask == mask) - { - if (key_down && !repeat) - { - // ...key went down this frame, call function - binding[i].mFunction( KEYSTATE_DOWN ); - } - else if (key_up) - { - // ...key went down this frame, call function - binding[i].mFunction( KEYSTATE_UP ); - } - else if (key_level) - { - // ...key held down from previous frame - // Not windows, just call the function. - binding[i].mFunction( KEYSTATE_LEVEL ); - }//if - }//if - }//for - }//for -} diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h deleted file mode 100644 index 80f326175f..0000000000 --- a/indra/newview/llviewerkeyboard.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @file llviewerkeyboard.h - * @brief LLViewerKeyboard class header file - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVIEWERKEYBOARD_H -#define LL_LLVIEWERKEYBOARD_H - -#include "llkeyboard.h" // For EKeystate -#include "llinitparam.h" - -const S32 MAX_NAMED_FUNCTIONS = 100; -const S32 MAX_KEY_BINDINGS = 128; // was 60 - -class LLNamedFunction -{ -public: - LLNamedFunction() : mFunction(NULL) { }; - ~LLNamedFunction() { }; - - std::string mName; - LLKeyFunc mFunction; -}; - -typedef enum e_keyboard_mode -{ - MODE_FIRST_PERSON, - MODE_THIRD_PERSON, - MODE_EDIT, - MODE_EDIT_AVATAR, - MODE_SITTING, - MODE_COUNT -} EKeyboardMode; - - -void bind_keyboard_functions(); - -class LLViewerKeyboard -{ -public: - struct KeyBinding : public LLInitParam::Block - { - Mandatory key, - mask, - command; - - KeyBinding(); - }; - - struct KeyMode : public LLInitParam::Block - { - Multiple bindings; - EKeyboardMode mode; - KeyMode(EKeyboardMode mode); - }; - - struct Keys : public LLInitParam::Block - { - Optional first_person, - third_person, - edit, - sitting, - edit_avatar; - - Keys(); - }; - - LLViewerKeyboard(); - - BOOL handleKey(KEY key, MASK mask, BOOL repeated); - BOOL handleKeyUp(KEY key, MASK mask); - - S32 loadBindings(const std::string& filename); // returns number bound, 0 on error - S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error - EKeyboardMode getMode(); - - BOOL modeFromString(const std::string& string, S32 *mode); // False on failure - - void scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level); - -private: - S32 loadBindingMode(const LLViewerKeyboard::KeyMode& keymode); - BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name); - - // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here - S32 mBindingCount[MODE_COUNT]; - LLKeyBinding mBindings[MODE_COUNT][MAX_KEY_BINDINGS]; - - typedef std::map key_remap_t; - key_remap_t mRemapKeys[MODE_COUNT]; - std::set mKeysSkippedByUI; - BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI -}; - -extern LLViewerKeyboard gViewerKeyboard; -void agent_push_forward(EKeystate s);// Mouse movement by Singularity -#endif // LL_LLVIEWERKEYBOARD_H diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index fd777ad8ff..77376b74e8 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -5822,6 +5822,20 @@ void handle_take_copy() derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id); } +void handle_link_objects() +{ + // We don't use a shortcut for two completely different functions based on context + //if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) + //{ + // LLFloaterReg::toggleInstanceOrBringToFront("places"); + //} + //else + // + { + LLSelectMgr::getInstance()->linkObjects(); + } +} + // You can return an object to its owner if it is on your land. class LLObjectReturn : public view_listener_t { @@ -9475,6 +9489,20 @@ class LLAdvancedToggleDoubleClickTeleport: public view_listener_t } }; +// Add telemetry controls to the viewer menus +class FSTelemetryToggleActive : public view_listener_t +{ +protected: + + bool handleEvent(const LLSD& userdata) + { + BOOL checked = gSavedSettings.getBOOL( "FSTelemetryActive" ); + gSavedSettings.setBOOL( "FSTelemetryActive", !checked ); + FSTelemetry::active = !checked; + return true; + } +}; +// void menu_toggle_attached_lights(void* user_data) { LLPipeline::sRenderAttachedLights = gSavedSettings.getBOOL("RenderAttachedLights"); @@ -10266,7 +10294,6 @@ BOOL LLViewerMenuHolderGL::hideMenus() if (LLMenuHolderGL::hideMenus()) { - LLToolPie::instance().blockClickToWalk(); handled = TRUE; } @@ -11616,7 +11643,7 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsSnapObjectXY(), "Tools.SnapObjectXY"); view_listener_t::addMenu(new LLToolsUseSelectionForGrid(), "Tools.UseSelectionForGrid"); view_listener_t::addMenu(new LLToolsSelectNextPartFace(), "Tools.SelectNextPart"); - commit.add("Tools.Link", boost::bind(&LLSelectMgr::linkObjects, LLSelectMgr::getInstance())); + commit.add("Tools.Link", boost::bind(&handle_link_objects)); commit.add("Tools.Unlink", boost::bind(&LLSelectMgr::unlinkObjects, LLSelectMgr::getInstance())); view_listener_t::addMenu(new LLToolsStopAllAnimations(), "Tools.StopAllAnimations"); view_listener_t::addMenu(new LLToolsReleaseKeys(), "Tools.ReleaseKeys"); @@ -11831,6 +11858,9 @@ void initialize_menus() //Develop (clear cache immediately) commit.add("Develop.ClearCache", boost::bind(&handle_cache_clear_immediately) ); + // Add telemetry controls to the viewer Develop menu (Toggle profiling) + view_listener_t::addMenu(new FSTelemetryToggleActive(), "Develop.ToggleTelemetry"); + // Admin >Object view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy"); view_listener_t::addMenu(new LLAdminHandleObjectOwnerSelf(), "Admin.HandleObjectOwnerSelf"); diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index e378c2448a..3bed65e1c4 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -106,5 +106,31 @@ // Library includes from llmessage project #include "llcachename.h" +// Firestorm additions +#include "llavatarname.h" +#include "llavatarnamecache.h" +#include "llcorehttputil.h" +#include "llformat.h" +#include "llmatrix4a.h" +#include "llvector4a.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #endif diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 751df0582d..608c95f222 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -45,7 +45,8 @@ #include "llmeshrepository.h" #include "llnotificationhandler.h" #include "llpanellogin.h" -#include "llviewerkeyboard.h" +#include "llsetkeybinddialog.h" +#include "llviewerinput.h" #include "llviewermenu.h" // physics display changes #include "llspatialpartition.h" @@ -180,7 +181,7 @@ #include "llviewergesture.h" #include "llviewertexturelist.h" #include "llviewerinventory.h" -#include "llviewerkeyboard.h" +#include "llviewerinput.h" #include "llviewermedia.h" #include "llviewermediafocus.h" #include "llviewermenu.h" @@ -1021,7 +1022,18 @@ LLViewerWindow::Params::Params() {} -BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +void LLViewerWindow::handlePieMenu(S32 x, S32 y, MASK mask) +{ + if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance() && gAgent.isInitialized()) + { + // If the current tool didn't process the click, we should show + // the pie menu. This can be done by passing the event to the pie + // menu tool. + LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); + } +} + +BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) { const char* buttonname = ""; const char* buttonstatestr = ""; @@ -1045,28 +1057,30 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK switch (clicktype) { - case LLMouseHandler::CLICK_LEFT: + case CLICK_LEFT: mLeftMouseDown = down; buttonname = "Left"; break; - case LLMouseHandler::CLICK_RIGHT: + case CLICK_RIGHT: mRightMouseDown = down; buttonname = "Right"; break; - case LLMouseHandler::CLICK_MIDDLE: + case CLICK_MIDDLE: mMiddleMouseDown = down; buttonname = "Middle"; break; - case LLMouseHandler::CLICK_DOUBLELEFT: + case CLICK_DOUBLELEFT: mLeftMouseDown = down; buttonname = "Left Double Click"; break; - case LLMouseHandler::CLICK_BUTTON4: + case CLICK_BUTTON4: buttonname = "Button 4"; break; - case LLMouseHandler::CLICK_BUTTON5: + case CLICK_BUTTON5: buttonname = "Button 5"; break; + default: + break; // COUNT and NONE } LLView::sMouseHandlerMessage.clear(); @@ -1117,6 +1131,11 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK LLViewerEventRecorder::instance().logMouseEvent(std::string(buttonstatestr),std::string(buttonname)); } + else if (down && clicktype == CLICK_RIGHT) + { + handlePieMenu(x, y, mask); + r = TRUE; + } return r; } @@ -1163,7 +1182,12 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK return TRUE; } - + if (down && clicktype == CLICK_RIGHT) + { + handlePieMenu(x, y, mask); + return TRUE; + } + // If we got this far on a down-click, it wasn't handled. // Up-clicks, though, are always handled as far as the OS is concerned. BOOL default_rtn = !down; @@ -1182,7 +1206,8 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask mMouseDownTimer.reset(); } BOOL down = TRUE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); + //handleMouse() loops back to LLViewerWindow::handleAnyMouseClick + return gViewerInput.handleMouse(window, pos, mask, CLICK_LEFT, down); } BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1190,8 +1215,7 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma // try handling as a double-click first, then a single-click if that // wasn't handled. BOOL down = TRUE; - if (handleAnyMouseClick(window, pos, mask, - LLMouseHandler::CLICK_DOUBLELEFT, down)) + if (gViewerInput.handleMouse(window, pos, mask, CLICK_DOUBLELEFT, down)) { return TRUE; } @@ -1205,47 +1229,24 @@ BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) mMouseDownTimer.stop(); } BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); + return gViewerInput.handleMouse(window, pos, mask, CLICK_LEFT, down); } - - BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { - S32 x = pos.mX; - S32 y = pos.mY; - x = ll_round((F32)x / mDisplayScale.mV[VX]); - y = ll_round((F32)y / mDisplayScale.mV[VY]); - BOOL down = TRUE; - BOOL handle = handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); - if (handle) - return handle; - - // *HACK: this should be rolled into the composite tool logic, not - // hardcoded at the top level. - if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance() && gAgent.isInitialized()) - { - // If the current tool didn't process the click, we should show - // the pie menu. This can be done by passing the event to the pie - // menu tool. - LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); - // show_context_menu( x, y, mask ); - } - - return TRUE; + return gViewerInput.handleMouse(window, pos, mask, CLICK_RIGHT, down); } BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); + return gViewerInput.handleMouse(window, pos, mask, CLICK_RIGHT, down); } BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = TRUE; - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, true); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + gViewerInput.handleMouse(window, pos, mask, CLICK_MIDDLE, down); // Always handled as far as the OS is concerned. return TRUE; @@ -1400,8 +1401,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, false); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + gViewerInput.handleMouse(window, pos, mask, CLICK_MIDDLE, down); // Always handled as far as the OS is concerned. return TRUE; @@ -1412,12 +1412,10 @@ BOOL LLViewerWindow::handleOtherMouse(LLWindow *window, LLCoordGL pos, MASK mask switch (button) { case 4: - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON4, down); - handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON4, down); + gViewerInput.handleMouse(window, pos, mask, CLICK_BUTTON4, down); break; case 5: - LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON5, down); - handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON5, down); + gViewerInput.handleMouse(window, pos, mask, CLICK_BUTTON5, down); break; default: break; @@ -1564,9 +1562,6 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) { - // Let the voice chat code check for its PTT key. Note that this never affects event processing. - LLVoiceClient::getInstance()->keyDown(key, mask); - if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) { gAgent.clearAFK(); @@ -1585,14 +1580,12 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) return FALSE; } - return gViewerKeyboard.handleKey(key, mask, repeated); + // remaps, handles ignored cases and returns back to viewer window. + return gViewerInput.handleKey(key, mask, repeated); } BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) { - // Let the voice chat code check for its PTT key. Note that this never affects event processing. - LLVoiceClient::getInstance()->keyUp(key, mask); - // Let the inspect tool code check for ALT key to set LLToolSelectRect active instead LLToolCamera LLToolCompInspect * tool_inspectp = LLToolCompInspect::getInstance(); if (LLToolMgr::getInstance()->getCurrentTool() == tool_inspectp) @@ -1600,13 +1593,13 @@ BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) tool_inspectp->keyUp(key, mask); } - return gViewerKeyboard.handleKeyUp(key, mask); + return gViewerInput.handleKeyUp(key, mask); } void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) { LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true); - gViewerKeyboard.scanKey(key, key_down, key_up, key_level); + gViewerInput.scanKey(key, key_down, key_up, key_level); return; // Be clear this function returns nothing } @@ -3119,6 +3112,13 @@ void LLViewerWindow::setTitle(const std::string& win_title) // Takes a single keyup event, usually when UI is visible BOOL LLViewerWindow::handleKeyUp(KEY key, MASK mask) { + if (LLSetKeyBindDialog::recordKey(key, mask, FALSE)) + { + LL_DEBUGS() << "KeyUp handled by LLSetKeyBindDialog" << LL_ENDL; + LLViewerEventRecorder::instance().logKeyEvent(key, mask); + return TRUE; + } + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); if (keyboard_focus @@ -3162,23 +3162,74 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // hide tooltips on keypress LLToolTipMgr::instance().blockToolTips(); - LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); + // Menus get handled on key down instead of key up + // so keybindings have to be recorded before that + if (LLSetKeyBindDialog::recordKey(key, mask, TRUE)) + { + LL_DEBUGS() << "Key handled by LLSetKeyBindDialog" << LL_ENDL; + LLViewerEventRecorder::instance().logKeyEvent(key,mask); + return TRUE; + } + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); + if (keyboard_focus - && !(mask & (MASK_CONTROL | MASK_ALT)) - && !gFocusMgr.getKeystrokesOnly()) - { - // We have keyboard focus, and it's not an accelerator - if (keyboard_focus && keyboard_focus->wantsKeyUpKeyDown()) + && !gFocusMgr.getKeystrokesOnly()) + { +#ifdef LL_WINDOWS + // On windows Alt Gr key generates additional Ctrl event, as result handling situations + // like 'AltGr + D' will result in 'Alt+Ctrl+D'. If it results in WM_CHAR, don't let it + // pass into menu or it will trigger 'develop' menu assigned to this combination on top + // of character handling. + // Alt Gr can be additionally modified by Shift + const MASK alt_gr = MASK_CONTROL | MASK_ALT; + if ((mask & alt_gr) != 0 + && key >= 0x30 + && key <= 0x5A + && (GetKeyState(VK_RMENU) & 0x8000) != 0 + && (GetKeyState(VK_RCONTROL) & 0x8000) == 0) // ensure right control is not pressed, only left one { - return keyboard_focus->handleKey(key, mask, FALSE ); + // Alt Gr key is represented as right alt and left control. + // Any alt+ctrl combination is treated as Alt Gr by TranslateMessage() and + // will generate a WM_CHAR message, but here we only treat virtual Alt Graph + // key by checking if this specific combination has unicode char. + // + // I decided to handle only virtual RAlt+LCtrl==AltGr combination to minimize + // impact on menu, but the right way might be to handle all Alt+Ctrl calls. + + BYTE keyboard_state[256]; + if (GetKeyboardState(keyboard_state)) + { + const int char_count = 6; + wchar_t chars[char_count]; + HKL layout = GetKeyboardLayout(0); + // ToUnicodeEx changes buffer state on OS below Win10, which is undesirable, + // but since we already did a TranslateMessage() in gatherInput(), this + // should have no negative effect + int res = ToUnicodeEx(key, 0, keyboard_state, chars, char_count, 1 << 2 /*do not modify buffer flag*/, layout); + if (res == 1 && chars[0] >= 0x20) + { + // Let it fall through to character handler and get a WM_CHAR. + return TRUE; + } + } } - else if (key < 0x80) - { - // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. - return (keyboard_focus != NULL); - } - } +#endif + + if (!(mask & (MASK_CONTROL | MASK_ALT))) + { + // We have keyboard focus, and it's not an accelerator + if (keyboard_focus && keyboard_focus->wantsKeyUpKeyDown()) + { + return keyboard_focus->handleKey(key, mask, FALSE); + } + else if (key < 0x80) + { + // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. + return TRUE; + } + } + } // let menus handle navigation keys for navigation if ((gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE)) @@ -3434,7 +3485,8 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) { if (mask != MASK_ALT) { - return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); + // remaps, handles ignored cases and returns back to viewer window. + return gViewerInput.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); } } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index d040980b62..b5e0ed73bb 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -176,8 +176,9 @@ public: void initWorldUI(); void setUIVisibility(bool); bool getUIVisibility(); + void handlePieMenu(S32 x, S32 y, MASK mask); - BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); // // LLWindowCallback interface implementation diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 5b707fa36f..637212cddd 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -217,8 +217,6 @@ const LLVoiceVersionInfo LLVoiceClient::getVersion() void LLVoiceClient::updateSettings() { setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); - std::string keyString = gSavedSettings.getString("PushToTalkButton"); - setPTTKey(keyString); setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); mDisableMic = gSavedSettings.getBOOL("VoiceDisableMic"); @@ -660,32 +658,6 @@ bool LLVoiceClient::getPTTIsToggle() return mPTTIsToggle; } -void LLVoiceClient::setPTTKey(std::string &key) -{ - // Value is stored as text for readability - if(key == "MiddleMouse") - { - mPTTMouseButton = LLMouseHandler::CLICK_MIDDLE; - } - else if(key == "MouseButton4") - { - mPTTMouseButton = LLMouseHandler::CLICK_BUTTON4; - } - else if (key == "MouseButton5") - { - mPTTMouseButton = LLMouseHandler::CLICK_BUTTON5; - } - else - { - mPTTMouseButton = 0; - if(!LLKeyboard::keyFromString(key, &mPTTKey)) - { - // If the call failed, don't match any key. - key = KEY_NONE; - } - } -} - void LLVoiceClient::inputUserControlState(bool down) { if(mPTTIsToggle) @@ -706,43 +678,6 @@ void LLVoiceClient::toggleUserPTTState(void) setUserPTTState(!getUserPTTState()); } -void LLVoiceClient::keyDown(KEY key, MASK mask) -{ - if (gKeyboard->getKeyRepeated(key)) - { - // ignore auto-repeat keys - return; - } - - if (mPTTMouseButton == 0 && LLAgent::isActionAllowed("speak") && (key == mPTTKey)) - { - bool down = gKeyboard->getKeyDown(mPTTKey); - if (down) - { - inputUserControlState(down); - } - } - -} -void LLVoiceClient::keyUp(KEY key, MASK mask) -{ - if (mPTTMouseButton == 0 && (key == mPTTKey)) - { - bool down = gKeyboard->getKeyDown(mPTTKey); - if (!down) - { - inputUserControlState(down); - } - } -} -void LLVoiceClient::updateMouseState(S32 click, bool down) -{ - if(mPTTMouseButton == click && LLAgent::isActionAllowed("speak")) - { - inputUserControlState(down); - } -} - //------------------------------------------- // nearby speaker accessors diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 6f0add2288..b3147e2e06 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -437,16 +437,10 @@ public: void setUsePTT(bool usePTT); void setPTTIsToggle(bool PTTIsToggle); bool getPTTIsToggle(); - void setPTTKey(std::string &key); - + void updateMicMuteLogic(); - + BOOL lipSyncEnabled(); - - // PTT key triggering - void keyDown(KEY key, MASK mask); - void keyUp(KEY key, MASK mask); - void updateMouseState(S32 click, bool down); boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 3167f7cce5..d848810eeb 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -6349,9 +6349,10 @@ static LLTrace::BlockTimerStatHandle FTM_REBUILD_MESH_FLUSH("Flush Mesh"); void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) { llassert(group); + LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_VB);// move out one scope (but are these even useful as dupes?) if (group && group->hasState(LLSpatialGroup::MESH_DIRTY) && !group->hasState(LLSpatialGroup::GEOM_DIRTY)) { - LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_VB); + // LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_VB);// move out one scope (but are these even useful as dupes?) LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_GEN_DRAW_INFO); //make sure getgeometryvolume shows up in the right place in timers group->mBuilt = 1.f; diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp index 9e4297baaf..aa8c79b0d2 100644 --- a/indra/newview/llwindowlistener.cpp +++ b/indra/newview/llwindowlistener.cpp @@ -37,7 +37,7 @@ #include "llview.h" #include "llviewinject.h" #include "llviewerwindow.h" -#include "llviewerkeyboard.h" +#include "llviewerinput.h" #include "llrootview.h" #include "llsdutil.h" #include "stringize.h" @@ -279,7 +279,7 @@ void LLWindowListener::keyDown(LLSD const & evt) response.setResponse(target_view->getInfo()); gFocusMgr.setKeyboardFocus(target_view); - gViewerKeyboard.handleKey(key, mask, false); + gViewerInput.handleKey(key, mask, false); if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } else @@ -291,7 +291,7 @@ void LLWindowListener::keyDown(LLSD const & evt) } else { - gViewerKeyboard.handleKey(key, mask, false); + gViewerInput.handleKey(key, mask, false); if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 6b9f27a101..0a5e59418d 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -4753,7 +4753,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera) { LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderGeomDeferred"); - LL_RECORD_BLOCK_TIME(FTM_RENDER_GEOMETRY); + // LL_RECORD_BLOCK_TIME(FTM_RENDER_GEOMETRY); remove duplicate zone scope LL_RECORD_BLOCK_TIME(FTM_DEFERRED_POOLS); diff --git a/indra/newview/skins/default/xui/de/control_table_contents_camera.xml b/indra/newview/skins/default/xui/de/control_table_contents_camera.xml new file mode 100644 index 0000000000..984a6aa87e --- /dev/null +++ b/indra/newview/skins/default/xui/de/control_table_contents_camera.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/de/control_table_contents_columns_basic.xml b/indra/newview/skins/default/xui/de/control_table_contents_columns_basic.xml new file mode 100644 index 0000000000..8f6e96e4ad --- /dev/null +++ b/indra/newview/skins/default/xui/de/control_table_contents_columns_basic.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/indra/newview/skins/default/xui/de/control_table_contents_editing.xml b/indra/newview/skins/default/xui/de/control_table_contents_editing.xml new file mode 100644 index 0000000000..726a27611e --- /dev/null +++ b/indra/newview/skins/default/xui/de/control_table_contents_editing.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/indra/newview/skins/default/xui/de/control_table_contents_media.xml b/indra/newview/skins/default/xui/de/control_table_contents_media.xml new file mode 100644 index 0000000000..c594aea187 --- /dev/null +++ b/indra/newview/skins/default/xui/de/control_table_contents_media.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/indra/newview/skins/default/xui/de/control_table_contents_movement.xml b/indra/newview/skins/default/xui/de/control_table_contents_movement.xml new file mode 100644 index 0000000000..ea8ab77e5c --- /dev/null +++ b/indra/newview/skins/default/xui/de/control_table_contents_movement.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/indra/newview/skins/default/xui/de/floater_preferences.xml b/indra/newview/skins/default/xui/de/floater_preferences.xml index 7765814df1..322a19e023 100644 --- a/indra/newview/skins/default/xui/de/floater_preferences.xml +++ b/indra/newview/skins/default/xui/de/floater_preferences.xml @@ -20,6 +20,7 @@ https://accounts.secondlife.com/change_email/?lang=de-DE + diff --git a/indra/newview/skins/default/xui/de/menu_viewer.xml b/indra/newview/skins/default/xui/de/menu_viewer.xml index 48bcf1f4a4..ef047c2355 100644 --- a/indra/newview/skins/default/xui/de/menu_viewer.xml +++ b/indra/newview/skins/default/xui/de/menu_viewer.xml @@ -16,6 +16,7 @@ + @@ -352,7 +353,6 @@ - @@ -402,7 +402,11 @@ - + + + + + diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index 4df4f2379b..f02f325868 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -4692,6 +4692,10 @@ Wählen Sie eine kleinere Landfläche aus. Dadurch werden die Protokolle vorheriger Unterhaltungen und alle Backups dieser Datei gelöscht. + + Möchten Sie die Standard-Tastenbelegung wiederherstellen? + + Dadurch werden die Transkripte aller vorherigen Unterhaltungen gelöscht. Die Liste vergangener Unterhaltungen ist davon nicht betroffen. Alle Dateien mit den Suffixen .txt und txt.backup im Order [FOLDER] werden gelöscht. diff --git a/indra/newview/skins/default/xui/de/panel_login.xml b/indra/newview/skins/default/xui/de/panel_login.xml index 287f55a127..4467f1adb2 100644 --- a/indra/newview/skins/default/xui/de/panel_login.xml +++ b/indra/newview/skins/default/xui/de/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=de - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/de/panel_login_first.xml b/indra/newview/skins/default/xui/de/panel_login_first.xml index 038001157e..034c2d60cc 100644 --- a/indra/newview/skins/default/xui/de/panel_login_first.xml +++ b/indra/newview/skins/default/xui/de/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=de - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml b/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml index ba5551ac3a..89c938994c 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_alerts.xml @@ -54,12 +54,14 @@ - - Immer anzeigen: - - - Niemals anzeigen: + + Hinweise, die immer oder niemals angezeigt werden können: + + + + + diff --git a/indra/newview/skins/default/xui/de/panel_preferences_backup.xml b/indra/newview/skins/default/xui/de/panel_preferences_backup.xml index 66042bcd71..dd7ca35b9a 100644 --- a/indra/newview/skins/default/xui/de/panel_preferences_backup.xml +++ b/indra/newview/skins/default/xui/de/panel_preferences_backup.xml @@ -40,6 +40,9 @@ Favoriten + + Tastenbelegung + diff --git a/indra/newview/skins/default/xui/de/panel_preferences_controls.xml b/indra/newview/skins/default/xui/de/panel_preferences_controls.xml new file mode 100644 index 0000000000..bf5a03b7aa --- /dev/null +++ b/indra/newview/skins/default/xui/de/panel_preferences_controls.xml @@ -0,0 +1,10 @@ + + + + + + + + + - - - Never show: - - + name="all_popups" + right="-10"> + + + + + + diff --git a/indra/newview/skins/default/xui/en/panel_preferences_backup.xml b/indra/newview/skins/default/xui/en/panel_preferences_backup.xml index d30ef8bb1e..f83ca06150 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_backup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_backup.xml @@ -166,6 +166,11 @@ Settings groups to be restored (backup will always save all): Favorites stored_favorites.xml + + + Controls + key_bindings.xml + diff --git a/indra/newview/skins/default/xui/en/panel_preferences_controls.xml b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml new file mode 100644 index 0000000000..cb9996d9ed --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml @@ -0,0 +1,63 @@ + + + + + + + + + + - - - + + + + + + diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml index a969fcbd9c..e9ac21e01e 100644 --- a/indra/newview/skins/default/xui/fr/menu_viewer.xml +++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml @@ -116,10 +116,16 @@ + + + + + + diff --git a/indra/newview/skins/default/xui/fr/panel_login.xml b/indra/newview/skins/default/xui/fr/panel_login.xml index e168aeb94a..08c5ea67f0 100644 --- a/indra/newview/skins/default/xui/fr/panel_login.xml +++ b/indra/newview/skins/default/xui/fr/panel_login.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=fr - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/fr/panel_login_first.xml b/indra/newview/skins/default/xui/fr/panel_login_first.xml index 8f40d0230c..7db6ac7bf6 100644 --- a/indra/newview/skins/default/xui/fr/panel_login_first.xml +++ b/indra/newview/skins/default/xui/fr/panel_login_first.xml @@ -4,7 +4,7 @@ http://secondlife.com/account/request.php?lang=fr - https://join.secondlife.com/ + https://www.firestormviewer.org/join-secondlife/ diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml b/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml index e3c7a61b41..db08c38422 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_UI.xml @@ -51,9 +51,10 @@ (Redémarrage requis) - - + (Redémarrage requis) + + @@ -70,6 +71,18 @@ + Format heure : + + + + + + + + + + + Barres de Navigation & de Favoris : @@ -81,6 +94,7 @@ + Utiliser des fenêtres distinctes pour : diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml b/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml index fc921c4c21..dba0daf029 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_firestorm.xml @@ -22,6 +22,8 @@ + + diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml index 7d5db3f508..c57734a419 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_graphics1.xml @@ -125,6 +125,7 @@ + diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_move.xml b/indra/newview/skins/default/xui/fr/panel_preferences_move.xml index af9f8946b2..9f9ea3eae8 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_move.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_move.xml @@ -40,13 +40,13 @@ - + Prévision de mouvement si changement de région : @@ -66,6 +66,7 @@ +