diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 000000000..44cbbb60f
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,166 @@
+stages:
+ - build
+ - deploy
+
+variables:
+ VIEWER_USE_CRASHPAD: "TRUE"
+ VIEWER_CRASHPAD_URL: $SENTRY_DSN
+
+.win_build: &win_build
+ stage: build
+ tags:
+ - autobuild
+ - windows
+ before_script:
+ - pipenv install
+ script:
+ - If ($env:VIEWER_CHANNEL_TYPE -eq 'Project')
+ {
+ $env:VIEWER_CHANNEL_CODENAME = $env:CI_COMMIT_REF_NAME[8..100] -join ''
+ }
+ - pipenv run autobuild configure -c Release -- -DUSE_FMODSTUDIO=ON -DUSE_NVAPI=ON -DUSE_LTO=ON -DVS_DISABLE_FATAL_WARNINGS=ON
+ - pipenv run autobuild build -c Release --no-configure
+ - If ($env:VIEWER_USE_CRASHPAD -eq 'TRUE') {
+ - Push-Location .\build-vc-*\newview\Release\
+ - sentry-cli upload-dif --include-sources singularity-bin.exe singularity-bin.pdb crashpad_handler.exe crashpad_handler.pdb fmod.dll libcrypto-1_1.dll libcrypto-1_1.pdb libssl-1_1.dll libssl-1_1.pdb libcrypto-1_1-x64.dll libcrypto-1_1-x64.pdb libssl-1_1-x64.dll libssl-1_1-x64.pdb vcruntime140.dll msvcp140.dll libhunspell.dll libhunspell.pdb glod.dll
+ - Pop-Location }
+ artifacts:
+ name: "$env:CI_COMMIT_REF_NAME-$env:CI_COMMIT_SHORT_SHA"
+ expire_in: 2 week
+ paths:
+ - build-vc-*/newview/Release/build_data.json
+ - build-vc-*/newview/Release/singularity-bin.pdb
+ - build-vc-*/newview/Release/Singularity_*_Setup.exe
+
+.beta_rules: &beta_rules
+ only:
+ - /^.*-beta$/
+ except:
+ - branches
+
+.release_rules: &release_rules
+ only:
+ - /^.*-release$/
+ except:
+ - branches
+
+build:master:windows32:
+ <<: *win_build
+ interruptible: true
+ variables:
+ AUTOBUILD_ADDRSIZE: 32
+ VIEWER_CHANNEL_TYPE: Test
+ VIEWER_USE_CRASHPAD: "FALSE"
+ only:
+ - schedules
+
+build:master:windows64:
+ <<: *win_build
+ interruptible: true
+ variables:
+ AUTOBUILD_ADDRSIZE: 64
+ VIEWER_CHANNEL_TYPE: Test
+ VIEWER_USE_CRASHPAD: "FALSE"
+ only:
+ - schedules
+
+build:project:windows32:
+ <<: *win_build
+ interruptible: true
+ variables:
+ AUTOBUILD_ADDRSIZE: 32
+ VIEWER_CHANNEL_TYPE: Project
+ VIEWER_USE_CRASHPAD: "FALSE"
+ only:
+ - /^project-.*$/
+
+build:project:windows64:
+ <<: *win_build
+ interruptible: true
+ variables:
+ AUTOBUILD_ADDRSIZE: 64
+ VIEWER_CHANNEL_TYPE: Project
+ only:
+ - /^project-.*$/
+
+build:beta:windows32:
+ <<: *win_build
+ variables:
+ AUTOBUILD_ADDRSIZE: 32
+ VIEWER_CHANNEL_TYPE: Beta
+ VIEWER_USE_CRASHPAD: "FALSE"
+ <<: *beta_rules
+
+build:beta:windows64:
+ <<: *win_build
+ variables:
+ AUTOBUILD_ADDRSIZE: 64
+ VIEWER_CHANNEL_TYPE: Beta
+ <<: *beta_rules
+
+build:release:windows32:
+ <<: *win_build
+ variables:
+ AUTOBUILD_ADDRSIZE: 32
+ VIEWER_CHANNEL_TYPE: Release
+ VIEWER_USE_CRASHPAD: "FALSE"
+ <<: *release_rules
+
+build:release:windows64:
+ <<: *win_build
+ variables:
+ AUTOBUILD_ADDRSIZE: 64
+ VIEWER_CHANNEL_TYPE: Release
+ <<: *release_rules
+
+.deploy_template: &deploy_template
+ stage: deploy
+ tags:
+ - autobuild
+ - windows
+ script:
+ - $BuildData = Get-Content .\build-vc-64\newview\Release\build_data.json | ConvertFrom-Json
+ - $BuildChannelVersion = $BuildData."Channel" + ' ' + $BuildData."Version"
+ - $UploadDestViewerDir = $BuildChannelVersion.ToLower().Replace(" ", "/")
+ - $UploadDestURL = "https://pkg.alchemyviewer.org/repository/viewer/${UploadDestViewerDir}"
+
+ - $UploadParams = @{ UseBasicParsing = $true;
+ Method = "PUT";
+ Headers = @{
+ ContentType = "application/x-executable";
+ Authorization = "Basic $([System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("$env:AUTOBUILD_HTTP_USER`:$env:AUTOBUILD_HTTP_PASS")))"; };
+ Verbose = $true; };
+
+ - Push-Location .\build-vc-32\newview\Release\
+ - $FileNameWin32 = Get-ChildItem -Path . -Name -Include Singularity_*_Setup.exe
+ - Invoke-WebRequest @UploadParams -InFile .\$FileNameWin32 -Uri "${UploadDestURL}/${FileNameWin32}"
+ - Pop-Location
+
+ - Push-Location .\build-vc-64\newview\Release\
+ - $FileNameWin64 = Get-ChildItem -Path . -Name -Include Singularity_*_Setup.exe
+ - Invoke-WebRequest @UploadParams -InFile .\$FileNameWin64 -Uri "${UploadDestURL}/${FileNameWin64}"
+ - Pop-Location
+
+ - sentry-cli releases new $BuildChannelVersion
+ - sentry-cli releases set-commits --auto $BuildChannelVersion
+ - sentry-cli releases finalize $BuildChannelVersion
+ when: manual
+
+deploy_project:
+ <<: *deploy_template
+ environment:
+ name: qa
+ only:
+ - /^project-.*$/
+
+deploy_beta:
+ <<: *deploy_template
+ environment:
+ name: staging
+ <<: *beta_rules
+
+deploy_release:
+ <<: *deploy_template
+ environment:
+ name: production
+ <<: *release_rules
diff --git a/Pipfile b/Pipfile
index 03b834648..81e20cec2 100644
--- a/Pipfile
+++ b/Pipfile
@@ -8,7 +8,7 @@ name = "pypi"
[packages]
llbase = "*"
certifi = "*"
-autobuild = {hg = "https://bitbucket.org/alchemyviewer/autobuild-1.1"}
+autobuild = {git = "https://git.alchemyviewer.org/alchemy/autobuild.git"}
[requires]
python_version = "2.7"
diff --git a/autobuild.xml b/autobuild.xml
index f5f17ea95..5d3f1a881 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -410,11 +410,11 @@
archive
name
windows
@@ -424,18 +424,18 @@
archive
name
windows64
version
- 365c520b.6
+ ce32d093.7
curl
version
- 1.1.1320_73.1.12+gee4b49f+chromium-73.0.3683.75
+ 1.3.201911222103_78.3.7_gea7ef34_chromium-78.0.3904.108
elfio
@@ -816,7 +818,7 @@
fmodstudio
copyright
- FMOD Studio, copyright (c) Firelight Technologies Pty, Ltd., 2012-2019.
+ FMOD Studio, Copyright (c) Firelight Technologies Pty Ltd.
description
FMOD Studio audio system library
license
@@ -874,11 +876,11 @@
archive
hash
- 54dbd41322a08a1fc333ca6d96af5502
+ ccd495598894c8e2e541a348015ee3f0
hash_algorithm
md5
url
- /opt/devel/secondlife/pkg/fmodstudio-2.00.02.191991250-linux64-191991250.tar.bz2
+ /opt/devel/fmodstudio-2.00.07.200182252-linux64-200182252.tar.bz2
name
linux64
@@ -888,11 +890,11 @@
archive
hash
- e0e87e0423fa42e4d2997b47b92eac6e
+ d32efb193ffcd73bcba4875ddfd17bf0
hash_algorithm
md5
url
- https://pkg.alchemyviewer.org/repository/autobuild-internal/fmodstudio/windows/fmodstudio-2.00.03.192211030-windows-192211030.tar.bz2
+ https://pkg.alchemyviewer.org/repository/autobuild-internal/fmodstudio/windows/fmodstudio-2.00.07.4-windows-4.tar.bz2
name
windows
@@ -902,18 +904,18 @@
archive
hash
- c2e55e1bfef7e066a0e40867a64b4cce
+ 0604fd6b53ceaf14ce04d0de1bea51b8
hash_algorithm
md5
url
- https://pkg.alchemyviewer.org/repository/autobuild-internal/fmodstudio/windows64/fmodstudio-2.00.03.192211029-windows64-192211029.tar.bz2
+ https://pkg.alchemyviewer.org/repository/autobuild-internal/fmodstudio/windows64/fmodstudio-2.00.07.4-windows64-4.tar.bz2
name
windows64
version
- 2.00.03.192211300
+ 2.00.07.4
fonts
@@ -996,7 +998,7 @@
freetype
copyright
- Copyright 2006-2017 by David Turner, Robert Wilhelm, and Werner Lemberg.
+ Copyright 2006-2018 by David Turner, Robert Wilhelm, and Werner Lemberg.
description
Font rendering library
license
@@ -2506,7 +2508,7 @@
vlc-bin
copyright
- Copyright (C) 1998-2016 VLC authors and VideoLAN
+ Copyright (C) 1998-2020 VLC authors and VideoLAN
license
GPL2
license_file
@@ -2544,9 +2546,9 @@
archive
hash
- add560654a53cb1c554044a4fac3c718
+ 3ff1d097e4f9b8f864a639aff974a506
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12144/71458/vlc_bin-2.2.8.511703-windows-511703.tar.bz2
+ https://pkg.alchemyviewer.org/repository/autobuild-external/vlc-bin/windows/vlc_bin-3.0.8.189-windows-189.tar.bz2
name
windows
@@ -2556,16 +2558,16 @@
archive
hash
- 94bf04b49acc1e1bf2c06e2232f8a083
+ b890b109b526cc6ad211eadefed83316
url
- http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/12145/71463/vlc_bin-2.2.8.511703-windows64-511703.tar.bz2
+ https://pkg.alchemyviewer.org/repository/autobuild-external/vlc-bin/windows64/vlc_bin-3.0.8.189-windows64-189.tar.bz2
name
windows64
version
- 2.2.8.511703
+ 3.0.8.189
xmlrpc-epi
@@ -2771,7 +2773,7 @@
options
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
- -DWORD_SIZE:STRING=$AUTOBUILD_ADDRSIZE
+ -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE
-DROOT_PROJECT_NAME:STRING=Singularity
-DINSTALL_PROPRIETARY=FALSE
@@ -2792,7 +2794,7 @@
options
-DCMAKE_BUILD_TYPE:STRING=Release
- -DWORD_SIZE:STRING=$AUTOBUILD_ADDRSIZE
+ -DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE
-DROOT_PROJECT_NAME:STRING=Singularity
-DINSTALL_PROPRIETARY=FALSE
@@ -2943,7 +2945,7 @@
-G
Ninja
- -DWORD_SIZE:STRING=64
+ -DADDRESS_SIZE:STRING=64
default
@@ -2968,7 +2970,7 @@
-G
Ninja
- -DWORD_SIZE:STRING=64
+ -DADDRESS_SIZE:STRING=64
name
@@ -2981,7 +2983,7 @@
windows
build_directory
- build-vc${AUTOBUILD_WIN_VSVER|161}-$AUTOBUILD_ADDRSIZE
+ build-vc-$AUTOBUILD_ADDRSIZE
configurations
RelWithDebInfo
@@ -3040,6 +3042,7 @@
/p:Configuration=Release
/p:Platform=${AUTOBUILD_WIN_VSPLATFORM|NOTWIN}
/t:Build
+ /p:PreferredToolArchitecture=x64
/p:useenv=true
/verbosity:normal
/m
@@ -3067,7 +3070,7 @@
name
- windows64
+ windows
version_file
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 6b5c80cf0..3bcd9e161 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -3,12 +3,7 @@
# cmake_minimum_required should appear before any
# other commands to guarantee full compatibility
# with the version specified
-## prior to 2.8, the add_custom_target commands used in setting the version did not work correctly
-if(WIN32)
- cmake_minimum_required(VERSION 3.4 FATAL_ERROR)
-else()
- cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)
-endif()
+cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
set(ROOT_PROJECT_NAME "Singularity" CACHE STRING
"The root project/makefile/solution name. Defaults to Singularity.")
@@ -16,11 +11,6 @@ project(${ROOT_PROJECT_NAME})
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
-include(Variables)
-include(00-Common)
-include(BuildVersion)
-include(CTest)
-
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -29,6 +19,11 @@ if (NOT CMAKE_BUILD_TYPE)
"Build type. One of: Debug Release RelWithDebInfo" FORCE)
endif (NOT CMAKE_BUILD_TYPE)
+include(Variables)
+include(00-Common)
+include(BuildVersion)
+include(CTest)
+
add_subdirectory(deps)
add_subdirectory(cmake)
@@ -65,8 +60,10 @@ add_subdirectory(${LIBS_OPEN_PREFIX}plugins)
add_subdirectory(${VIEWER_PREFIX}newview/statemachine)
add_subdirectory(${VIEWER_PREFIX}newview)
-add_dependencies(viewer ${VIEWER_BRANDING_ID}-bin)
+add_dependencies(viewer ${VIEWER_BINARY_NAME})
+
if (WINDOWS)
- set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${VIEWER_BRANDING_ID}-bin)
+ set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ PROPERTY VS_STARTUP_PROJECT ${VIEWER_BINARY_NAME})
endif (WINDOWS)
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 6dddeed57..653c72412 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -39,9 +39,9 @@ if (WINDOWS)
"${CMAKE_C_FLAGS_RELEASE} ${LL_C_FLAGS} /O2 /Zi /MD /MP /fp:fast"
CACHE STRING "C compiler release options" FORCE)
- if (WORD_SIZE EQUAL 32)
+ if (ADDRESS_SIZE EQUAL 32)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE")
- endif (WORD_SIZE EQUAL 32)
+ endif (ADDRESS_SIZE EQUAL 32)
if (FULL_DEBUG_SYMS OR USE_CRASHPAD)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG:FULL")
@@ -103,9 +103,9 @@ if (WINDOWS)
)
endif (USE_LTO)
- if (WORD_SIZE EQUAL 32)
+ if (ADDRESS_SIZE EQUAL 32)
add_compile_options(/arch:SSE2)
- endif (WORD_SIZE EQUAL 32)
+ endif (ADDRESS_SIZE EQUAL 32)
if (NOT DISABLE_FATAL_WARNINGS)
add_definitions(/WX)
@@ -292,13 +292,13 @@ if (LINUX OR DARWIN)
set(CMAKE_C_FLAGS "${UNIX_WARNINGS} ${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${UNIX_CXX_WARNINGS} ${CMAKE_CXX_FLAGS}")
- if (WORD_SIZE EQUAL 32)
+ if (ADDRESS_SIZE EQUAL 32)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
- elseif (WORD_SIZE EQUAL 64)
+ elseif (ADDRESS_SIZE EQUAL 64)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64")
- endif (WORD_SIZE EQUAL 32)
+ endif (ADDRESS_SIZE EQUAL 32)
endif (LINUX OR DARWIN)
diff --git a/indra/cmake/ConfigurePkgConfig.cmake b/indra/cmake/ConfigurePkgConfig.cmake
index afbc36d63..a2d5b31e2 100644
--- a/indra/cmake/ConfigurePkgConfig.cmake
+++ b/indra/cmake/ConfigurePkgConfig.cmake
@@ -6,17 +6,17 @@ SET(DEBUG_PKG_CONFIG "YES")
IF("$ENV{PKG_CONFIG_LIBDIR}" STREQUAL "")
# Guess at architecture-specific system library paths.
- if (WORD_SIZE EQUAL 32)
+ if (ADDRESS_SIZE EQUAL 32)
SET(PKG_CONFIG_NO_MULTI_GUESS /usr/lib32 /usr/lib)
SET(PKG_CONFIG_NO_MULTI_LOCAL_GUESS /usr/local/lib32 /usr/local/lib)
SET(PKG_CONFIG_MULTI_GUESS /usr/lib/i386-linux-gnu)
SET(PKG_CONFIG_MULTI_LOCAL_GUESS /usr/local/lib/i386-linux-gnu)
- else (WORD_SIZE EQUAL 32)
+ else (ADDRESS_SIZE EQUAL 32)
SET(PKG_CONFIG_NO_MULTI_GUESS /usr/lib64 /usr/lib)
SET(PKG_CONFIG_NO_MULTI_LOCAL_GUESS /usr/local/lib64 /usr/local/lib)
SET(PKG_CONFIG_MULTI_GUESS /usr/lib/x86_64-linux-gnu)
SET(PKG_CONFIG_MULTI_LOCAL_GUESS /usr/local/lib/x86_64-linux-gnu)
- endif (WORD_SIZE EQUAL 32)
+ endif (ADDRESS_SIZE EQUAL 32)
# Use DPKG architecture, if available.
IF (${DPKG_ARCH})
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index abe2a6f2a..00a9f33e9 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -13,10 +13,6 @@ include(LLCommon)
# set up platform specific lists of files that need to be copied
###################################################################
if(WINDOWS)
- set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
- set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
- include(InstallRequiredSystemLibrariesAL)
-
set(SHARED_LIB_STAGING_DIR_DEBUG "${SHARED_LIB_STAGING_DIR}/Debug")
set(SHARED_LIB_STAGING_DIR_RELWITHDEBINFO "${SHARED_LIB_STAGING_DIR}/RelWithDebInfo")
set(SHARED_LIB_STAGING_DIR_RELEASE "${SHARED_LIB_STAGING_DIR}/Release")
@@ -82,39 +78,19 @@ if(WINDOWS)
endif(ADDRESS_SIZE EQUAL 64)
if(NOT DISABLE_TCMALLOC)
- set(debug_files ${debug_files} libtcmalloc_minimal-debug.dll)
- set(release_files ${release_files} libtcmalloc_minimal.dll)
+ list(APPEND debug_files libtcmalloc_minimal-debug.dll)
+ list(APPEND release_files libtcmalloc_minimal.dll)
endif(NOT DISABLE_TCMALLOC)
if(OPENAL)
- set(debug_files ${debug_files} alut.dll OpenAL32.dll)
- set(release_files ${release_files} alut.dll OpenAL32.dll)
+ list(APPEND debug_files alut.dll OpenAL32.dll)
+ list(APPEND release_files alut.dll OpenAL32.dll)
endif(OPENAL)
- if (FMODSTUDIO)
- set(debug_files ${debug_files} fmodL.dll)
- set(release_files ${release_files} fmod.dll)
- endif (FMODSTUDIO)
-
- foreach(redistfullfile IN LISTS CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
- get_filename_component(redistfilepath ${redistfullfile} DIRECTORY )
- get_filename_component(redistfilename ${redistfullfile} NAME)
- copy_if_different(
- ${redistfilepath}
- "${SHARED_LIB_STAGING_DIR_RELEASE}"
- out_targets
- ${redistfilename}
- )
- set(third_party_targets ${third_party_targets} ${out_targets})
-
- copy_if_different(
- ${redistfilepath}
- "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}"
- out_targets
- ${redistfilename}
- )
- set(third_party_targets ${third_party_targets} ${out_targets})
- endforeach()
+ if (USE_FMODSTUDIO)
+ list(APPEND debug_files fmodL.dll)
+ list(APPEND release_files fmod.dll)
+ endif (USE_FMODSTUDIO)
elseif(DARWIN)
set(SHARED_LIB_STAGING_DIR_DEBUG "${SHARED_LIB_STAGING_DIR}/Debug/Resources")
@@ -191,13 +167,13 @@ elseif(LINUX)
)
if (USE_TCMALLOC)
- set(release_files ${release_files} "libtcmalloc_minimal.so")
+ list(APPEND release_files "libtcmalloc_minimal.so")
endif (USE_TCMALLOC)
- if (FMODSTUDIO)
- set(debug_files ${debug_files} "libfmodL.so")
- set(release_files ${release_files} "libfmod.so")
- endif (FMODSTUDIO)
+ if (USE_FMODSTUDIO)
+ list(APPEND debug_files "libfmodL.so")
+ list(APPEND release_files "libfmod.so")
+ endif (USE_FMODSTUDIO)
else(WINDOWS)
message(STATUS "WARNING: unrecognized platform for staging 3rd party libs, skipping...")
diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake
index 2f8691c27..83ac0ac9c 100644
--- a/indra/cmake/FMODSTUDIO.cmake
+++ b/indra/cmake/FMODSTUDIO.cmake
@@ -1,7 +1,7 @@
# -*- cmake -*-
include(Variables)
-if (FMODSTUDIO)
+if (USE_FMODSTUDIO)
use_prebuilt_binary(fmodstudio)
if(WINDOWS)
set(lib_suffix .dll)
@@ -31,7 +31,7 @@ if (FMODSTUDIO)
)
set(FMODSTUDIO_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/fmodstudio)
-endif(FMODSTUDIO)
+endif(USE_FMODSTUDIO)
if(FMOD_LIBRARY_RELEASE AND FMODSTUDIO_INCLUDE_DIR)
set(FMOD ON)
@@ -41,7 +41,7 @@ if(FMOD_LIBRARY_RELEASE AND FMODSTUDIO_INCLUDE_DIR)
else (FMOD_LIBRARY_RELEASE AND FMODSTUDIO_INCLUDE_DIR)
message(STATUS "No support for FMOD Studio audio (need to set FMODSTUDIO_SDK_DIR?)")
set(FMOD OFF)
- set(FMODSTUDIO OFF)
+ set(USE_FMODSTUDIO OFF)
endif (FMOD_LIBRARY_RELEASE AND FMODSTUDIO_INCLUDE_DIR)
if (FMOD)
diff --git a/indra/cmake/GooglePerfTools.cmake b/indra/cmake/GooglePerfTools.cmake
index 49ff3f5eb..d951bc55e 100644
--- a/indra/cmake/GooglePerfTools.cmake
+++ b/indra/cmake/GooglePerfTools.cmake
@@ -2,16 +2,16 @@
include(Prebuilt)
-if(WORD_SIZE EQUAL 64)
+if(ADDRESS_SIZE EQUAL 64)
set(DISABLE_TCMALLOC TRUE)
-endif(WORD_SIZE EQUAL 64)
+endif(ADDRESS_SIZE EQUAL 64)
if (STANDALONE)
include(FindGooglePerfTools)
else (STANDALONE)
- if (LINUX OR WINDOWS AND NOT WORD_SIZE EQUAL 64)
+ if (LINUX OR WINDOWS AND NOT ADDRESS_SIZE EQUAL 64)
use_prebuilt_binary(gperftools)
- endif (LINUX OR WINDOWS AND NOT WORD_SIZE EQUAL 64)
+ endif (LINUX OR WINDOWS AND NOT ADDRESS_SIZE EQUAL 64)
if (WINDOWS AND NOT DISABLE_TCMALLOC)
set(TCMALLOC_LIBRARIES libtcmalloc_minimal.lib)
set(TCMALLOC_LINKER_FLAGS "/INCLUDE:\"__tcmalloc\"")
diff --git a/indra/cmake/InstallRequiredSystemLibrariesAL.cmake b/indra/cmake/InstallRequiredSystemLibrariesAL.cmake
deleted file mode 100644
index 0498da38c..000000000
--- a/indra/cmake/InstallRequiredSystemLibrariesAL.cmake
+++ /dev/null
@@ -1,741 +0,0 @@
-# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
-# file Copyright.txt or https://cmake.org/licensing for details.
-
-#[=======================================================================[.rst:
-InstallRequiredSystemLibraries
-------------------------------
-
-Include this module to search for compiler-provided system runtime
-libraries and add install rules for them. Some optional variables
-may be set prior to including the module to adjust behavior:
-
-``CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS``
- Specify additional runtime libraries that may not be detected.
- After inclusion any detected libraries will be appended to this.
-
-``CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP``
- Set to TRUE to skip calling the :command:`install(PROGRAMS)` command to
- allow the includer to specify its own install rule, using the value of
- ``CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS`` to get the list of libraries.
-
-``CMAKE_INSTALL_DEBUG_LIBRARIES``
- Set to TRUE to install the debug runtime libraries when available
- with MSVC tools.
-
-``CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY``
- Set to TRUE to install only the debug runtime libraries with MSVC
- tools even if the release runtime libraries are also available.
-
-``CMAKE_INSTALL_UCRT_LIBRARIES``
- Set to TRUE to install the Windows Universal CRT libraries for
- app-local deployment (e.g. to Windows XP). This is meaningful
- only with MSVC from Visual Studio 2015 or higher.
-
- One may set a ``CMAKE_WINDOWS_KITS_10_DIR`` *environment variable*
- to an absolute path to tell CMake to look for Windows 10 SDKs in
- a custom location. The specified directory is expected to contain
- ``Redist/ucrt/DLLs/*`` directories.
-
-``CMAKE_INSTALL_MFC_LIBRARIES``
- Set to TRUE to install the MSVC MFC runtime libraries.
-
-``CMAKE_INSTALL_OPENMP_LIBRARIES``
- Set to TRUE to install the MSVC OpenMP runtime libraries
-
-``CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION``
- Specify the :command:`install(PROGRAMS)` command ``DESTINATION``
- option. If not specified, the default is ``bin`` on Windows
- and ``lib`` elsewhere.
-
-``CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS``
- Set to TRUE to disable warnings about required library files that
- do not exist. (For example, Visual Studio Express editions may
- not provide the redistributable files.)
-
-``CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT``
- Specify the :command:`install(PROGRAMS)` command ``COMPONENT``
- option. If not specified, no such option will be used.
-#]=======================================================================]
-
-cmake_policy(PUSH)
-cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
-
-set(_IRSL_HAVE_Intel FALSE)
-set(_IRSL_HAVE_MSVC FALSE)
-foreach(LANG IN ITEMS C CXX Fortran)
- if("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Intel")
- if(NOT _IRSL_HAVE_Intel)
- get_filename_component(_Intel_basedir "${CMAKE_${LANG}_COMPILER}" PATH)
- if(CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(_Intel_archdir intel64)
- else()
- set(_Intel_archdir x86)
- endif()
- set(_Intel_compiler_ver ${CMAKE_${LANG}_COMPILER_VERSION})
- if(WIN32)
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../redist/${_Intel_archdir}/compiler" ABSOLUTE)
- elseif(APPLE)
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../compiler/lib" ABSOLUTE)
- else()
- if(EXISTS "${_Intel_basedir}/../lib/${_Intel_archdir}_lin")
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../lib/${_Intel_archdir}" ABSOLUTE)
- else()
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../compiler/lib/${_Intel_archdir}_lin" ABSOLUTE)
- endif()
- endif()
- set(_IRSL_HAVE_Intel TRUE)
- endif()
- elseif("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "MSVC")
- set(_IRSL_HAVE_MSVC TRUE)
- endif()
-endforeach()
-
-if(MSVC)
- file(TO_CMAKE_PATH "$ENV{SYSTEMROOT}" SYSTEMROOT)
-
- if(CMAKE_CL_64)
- if(MSVC_VERSION GREATER 1599)
- # VS 10 and later:
- set(CMAKE_MSVC_ARCH x64)
- else()
- # VS 9 and earlier:
- set(CMAKE_MSVC_ARCH amd64)
- endif()
- else()
- set(CMAKE_MSVC_ARCH x86)
- endif()
-
- get_filename_component(devenv_dir "${CMAKE_MAKE_PROGRAM}" PATH)
- get_filename_component(base_dir "${devenv_dir}/../.." ABSOLUTE)
-
- if(MSVC_VERSION EQUAL 1300)
- set(__install__libs
- "${SYSTEMROOT}/system32/msvcp70.dll"
- "${SYSTEMROOT}/system32/msvcr70.dll"
- )
- endif()
-
- if(MSVC_VERSION EQUAL 1310)
- set(__install__libs
- "${SYSTEMROOT}/system32/msvcp71.dll"
- "${SYSTEMROOT}/system32/msvcr71.dll"
- )
- endif()
-
- if(MSVC_TOOLSET_VERSION EQUAL 80)
- # Find the runtime library redistribution directory.
- get_filename_component(msvc_install_dir
- "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]" ABSOLUTE)
- if(DEFINED MSVC80_REDIST_DIR AND EXISTS "${MSVC80_REDIST_DIR}")
- set(MSVC_REDIST_DIR "${MSVC80_REDIST_DIR}") # use old cache entry
- endif()
- find_path(MSVC_REDIST_DIR NAMES ${CMAKE_MSVC_ARCH}/Microsoft.VC80.CRT/Microsoft.VC80.CRT.manifest
- PATHS
- "${msvc_install_dir}/../../VC/redist"
- "${base_dir}/VC/redist"
- )
- mark_as_advanced(MSVC_REDIST_DIR)
- set(MSVC_CRT_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.VC80.CRT")
-
- # Install the manifest that allows DLLs to be loaded from the
- # directory containing the executable.
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- set(__install__libs
- "${MSVC_CRT_DIR}/Microsoft.VC80.CRT.manifest"
- "${MSVC_CRT_DIR}/msvcm80.dll"
- "${MSVC_CRT_DIR}/msvcp80.dll"
- "${MSVC_CRT_DIR}/msvcr80.dll"
- )
- else()
- set(__install__libs)
- endif()
-
- if(CMAKE_INSTALL_DEBUG_LIBRARIES)
- set(MSVC_CRT_DIR
- "${MSVC_REDIST_DIR}/Debug_NonRedist/${CMAKE_MSVC_ARCH}/Microsoft.VC80.DebugCRT")
- set(__install__libs ${__install__libs}
- "${MSVC_CRT_DIR}/Microsoft.VC80.DebugCRT.manifest"
- "${MSVC_CRT_DIR}/msvcm80d.dll"
- "${MSVC_CRT_DIR}/msvcp80d.dll"
- "${MSVC_CRT_DIR}/msvcr80d.dll"
- )
- endif()
- endif()
-
- if(MSVC_TOOLSET_VERSION EQUAL 90)
- # Find the runtime library redistribution directory.
- get_filename_component(msvc_install_dir
- "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]" ABSOLUTE)
- get_filename_component(msvc_express_install_dir
- "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\9.0;InstallDir]" ABSOLUTE)
- if(DEFINED MSVC90_REDIST_DIR AND EXISTS "${MSVC90_REDIST_DIR}")
- set(MSVC_REDIST_DIR "${MSVC90_REDIST_DIR}") # use old cache entry
- endif()
- find_path(MSVC_REDIST_DIR NAMES ${CMAKE_MSVC_ARCH}/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest
- PATHS
- "${msvc_install_dir}/../../VC/redist"
- "${msvc_express_install_dir}/../../VC/redist"
- "${base_dir}/VC/redist"
- )
- mark_as_advanced(MSVC_REDIST_DIR)
- set(MSVC_CRT_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.VC90.CRT")
-
- # Install the manifest that allows DLLs to be loaded from the
- # directory containing the executable.
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- set(__install__libs
- "${MSVC_CRT_DIR}/Microsoft.VC90.CRT.manifest"
- "${MSVC_CRT_DIR}/msvcm90.dll"
- "${MSVC_CRT_DIR}/msvcp90.dll"
- "${MSVC_CRT_DIR}/msvcr90.dll"
- )
- else()
- set(__install__libs)
- endif()
-
- if(CMAKE_INSTALL_DEBUG_LIBRARIES)
- set(MSVC_CRT_DIR
- "${MSVC_REDIST_DIR}/Debug_NonRedist/${CMAKE_MSVC_ARCH}/Microsoft.VC90.DebugCRT")
- set(__install__libs ${__install__libs}
- "${MSVC_CRT_DIR}/Microsoft.VC90.DebugCRT.manifest"
- "${MSVC_CRT_DIR}/msvcm90d.dll"
- "${MSVC_CRT_DIR}/msvcp90d.dll"
- "${MSVC_CRT_DIR}/msvcr90d.dll"
- )
- endif()
- endif()
-
- set(MSVC_REDIST_NAME "")
- set(_MSVC_DLL_VERSION "")
- set(_MSVC_IDE_VERSION "")
- if(MSVC_VERSION GREATER_EQUAL 2000)
- message(WARNING "MSVC ${MSVC_VERSION} not yet supported.")
- elseif(MSVC_VERSION_VERSION GREATER_EQUAL 143)
- message(WARNING "MSVC toolset v${MSVC_VERSION_VERSION} not yet supported.")
- elseif(MSVC_TOOLSET_VERSION EQUAL 142)
- set(MSVC_REDIST_NAME VC142)
- set(_MSVC_DLL_VERSION 140)
- set(_MSVC_IDE_VERSION 16)
- if(MSVC_VERSION EQUAL 1920)
- # VS2019 named this differently prior to update 1.
- set(MSVC_REDIST_NAME VC141)
- endif()
- elseif(MSVC_TOOLSET_VERSION EQUAL 141)
- set(MSVC_REDIST_NAME VC141)
- set(_MSVC_DLL_VERSION 140)
- set(_MSVC_IDE_VERSION 15)
- if(MSVC_VERSION EQUAL 1910)
- # VS2017 named this differently prior to update 3.
- set(MSVC_REDIST_NAME VC150)
- endif()
- elseif(MSVC_TOOLSET_VERSION)
- set(MSVC_REDIST_NAME VC${MSVC_TOOLSET_VERSION})
- math(EXPR _MSVC_DLL_VERSION "${MSVC_TOOLSET_VERSION} / 10 * 10")
- math(EXPR _MSVC_IDE_VERSION "${MSVC_TOOLSET_VERSION} / 10")
- endif()
-
- set(_MSVCRT_DLL_VERSION "")
- set(_MSVCRT_IDE_VERSION "")
- if(_MSVC_IDE_VERSION GREATER_EQUAL 10)
- set(_MSVCRT_DLL_VERSION "${_MSVC_DLL_VERSION}")
- set(_MSVCRT_IDE_VERSION "${_MSVC_IDE_VERSION}")
- endif()
-
- if(_MSVCRT_DLL_VERSION)
- set(v "${_MSVCRT_DLL_VERSION}")
- set(vs "${_MSVCRT_IDE_VERSION}")
-
- # Find the runtime library redistribution directory.
- if(vs VERSION_LESS 15 AND DEFINED MSVC${vs}_REDIST_DIR AND EXISTS "${MSVC${vs}_REDIST_DIR}")
- set(MSVC_REDIST_DIR "${MSVC${vs}_REDIST_DIR}") # use old cache entry
- endif()
- if(NOT vs VERSION_LESS 15)
- set(_vs_redist_paths "")
- cmake_host_system_information(RESULT _vs_dir QUERY VS_${vs}_DIR) # undocumented query
- if(IS_DIRECTORY "${_vs_dir}")
- file(GLOB _vs_redist_paths "${_vs_dir}/VC/Redist/MSVC/*")
- endif()
- unset(_vs_dir)
- else()
- get_filename_component(_vs_dir
- "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\${vs}.0;InstallDir]" ABSOLUTE)
- set(programfilesx86 "ProgramFiles(x86)")
- set(_vs_redist_paths
- "${_vs_dir}/../../VC/redist"
- "${base_dir}/VC/redist"
- "$ENV{ProgramFiles}/Microsoft Visual Studio ${vs}.0/VC/redist"
- "$ENV{${programfilesx86}}/Microsoft Visual Studio ${vs}.0/VC/redist"
- )
- unset(_vs_dir)
- unset(programfilesx86)
- endif()
- find_path(MSVC_REDIST_DIR NAMES ${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.CRT PATHS ${_vs_redist_paths})
- unset(_vs_redist_paths)
- mark_as_advanced(MSVC_REDIST_DIR)
- set(MSVC_CRT_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.CRT")
-
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- set(__install__libs
- "${MSVC_CRT_DIR}/msvcp${v}.dll"
- )
- if(NOT vs VERSION_LESS 14)
- file(GLOB __msvcr_dlls "${MSVC_CRT_DIR}/*.dll")
- list(APPEND __install__libs ${__msvcr_dlls})
- else()
- list(APPEND __install__libs "${MSVC_CRT_DIR}/msvcr${v}.dll")
- endif()
- else()
- set(__install__libs)
- endif()
-
- if(CMAKE_INSTALL_DEBUG_LIBRARIES)
- set(MSVC_CRT_DIR
- "${MSVC_REDIST_DIR}/Debug_NonRedist/${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.DebugCRT")
- set(__install__libs ${__install__libs}
- "${MSVC_CRT_DIR}/msvcp${v}d.dll"
- )
- if(NOT vs VERSION_LESS 14)
- list(APPEND __install__libs
- "${MSVC_CRT_DIR}/vcruntime${v}d.dll"
- "${MSVC_CRT_DIR}/concrt${v}d.dll"
- )
- else()
- list(APPEND __install__libs "${MSVC_CRT_DIR}/msvcr${v}d.dll")
- endif()
- endif()
-
- if(CMAKE_INSTALL_UCRT_LIBRARIES AND NOT vs VERSION_LESS 14)
- # Find the Windows Kits directory.
- get_filename_component(windows_kits_dir
- "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]" ABSOLUTE)
- set(programfilesx86 "ProgramFiles(x86)")
- if(";${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION};$ENV{UCRTVersion};$ENV{WindowsSDKVersion};" MATCHES [=[;(10\.[0-9.]+)[;\]]=])
- set(__ucrt_version "${CMAKE_MATCH_1}/")
- else()
- set(__ucrt_version "")
- endif()
- find_path(WINDOWS_KITS_DIR
- NAMES
- Redist/${__ucrt_version}ucrt/DLLs/${CMAKE_MSVC_ARCH}/ucrtbase.dll
- Redist/ucrt/DLLs/${CMAKE_MSVC_ARCH}/ucrtbase.dll
- PATHS
- $ENV{CMAKE_WINDOWS_KITS_10_DIR}
- "${windows_kits_dir}"
- "$ENV{ProgramFiles}/Windows Kits/10"
- "$ENV{${programfilesx86}}/Windows Kits/10"
- )
- mark_as_advanced(WINDOWS_KITS_DIR)
-
- # Glob the list of UCRT DLLs.
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- if(EXISTS "${WINDOWS_KITS_DIR}/Redist/${__ucrt_version}ucrt/DLLs/${CMAKE_MSVC_ARCH}/ucrtbase.dll")
- file(GLOB __ucrt_dlls "${WINDOWS_KITS_DIR}/Redist/${__ucrt_version}ucrt/DLLs/${CMAKE_MSVC_ARCH}/*.dll")
- else()
- file(GLOB __ucrt_dlls "${WINDOWS_KITS_DIR}/Redist/ucrt/DLLs/${CMAKE_MSVC_ARCH}/*.dll")
- endif()
- list(APPEND __install__libs ${__ucrt_dlls})
- endif()
- if(CMAKE_INSTALL_DEBUG_LIBRARIES)
- if(EXISTS "${WINDOWS_KITS_DIR}/bin/${__ucrt_version}${CMAKE_MSVC_ARCH}/ucrt/ucrtbased.dll")
- file(GLOB __ucrt_dlls "${WINDOWS_KITS_DIR}/bin/${__ucrt_version}${CMAKE_MSVC_ARCH}/ucrt/*.dll")
- else()
- file(GLOB __ucrt_dlls "${WINDOWS_KITS_DIR}/bin/${CMAKE_MSVC_ARCH}/ucrt/*.dll")
- endif()
- list(APPEND __install__libs ${__ucrt_dlls})
- endif()
- endif()
- endif()
-
- if(CMAKE_INSTALL_MFC_LIBRARIES)
- if(MSVC_VERSION EQUAL 1300)
- set(__install__libs ${__install__libs}
- "${SYSTEMROOT}/system32/mfc70.dll"
- )
- endif()
-
- if(MSVC_VERSION EQUAL 1310)
- set(__install__libs ${__install__libs}
- "${SYSTEMROOT}/system32/mfc71.dll"
- )
- endif()
-
- if(MSVC_VERSION EQUAL 1400)
- if(CMAKE_INSTALL_DEBUG_LIBRARIES)
- set(MSVC_MFC_DIR
- "${MSVC_REDIST_DIR}/Debug_NonRedist/${CMAKE_MSVC_ARCH}/Microsoft.VC80.DebugMFC")
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/Microsoft.VC80.DebugMFC.manifest"
- "${MSVC_MFC_DIR}/mfc80d.dll"
- "${MSVC_MFC_DIR}/mfc80ud.dll"
- "${MSVC_MFC_DIR}/mfcm80d.dll"
- "${MSVC_MFC_DIR}/mfcm80ud.dll"
- )
- endif()
-
- set(MSVC_MFC_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.VC80.MFC")
- # Install the manifest that allows DLLs to be loaded from the
- # directory containing the executable.
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/Microsoft.VC80.MFC.manifest"
- "${MSVC_MFC_DIR}/mfc80.dll"
- "${MSVC_MFC_DIR}/mfc80u.dll"
- "${MSVC_MFC_DIR}/mfcm80.dll"
- "${MSVC_MFC_DIR}/mfcm80u.dll"
- )
- endif()
-
- # include the language dll's for vs8 as well as the actual dll's
- set(MSVC_MFCLOC_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.VC80.MFCLOC")
- # Install the manifest that allows DLLs to be loaded from the
- # directory containing the executable.
- set(__install__libs ${__install__libs}
- "${MSVC_MFCLOC_DIR}/Microsoft.VC80.MFCLOC.manifest"
- "${MSVC_MFCLOC_DIR}/mfc80chs.dll"
- "${MSVC_MFCLOC_DIR}/mfc80cht.dll"
- "${MSVC_MFCLOC_DIR}/mfc80enu.dll"
- "${MSVC_MFCLOC_DIR}/mfc80esp.dll"
- "${MSVC_MFCLOC_DIR}/mfc80deu.dll"
- "${MSVC_MFCLOC_DIR}/mfc80fra.dll"
- "${MSVC_MFCLOC_DIR}/mfc80ita.dll"
- "${MSVC_MFCLOC_DIR}/mfc80jpn.dll"
- "${MSVC_MFCLOC_DIR}/mfc80kor.dll"
- )
- endif()
-
- if(MSVC_VERSION EQUAL 1500)
- if(CMAKE_INSTALL_DEBUG_LIBRARIES)
- set(MSVC_MFC_DIR
- "${MSVC_REDIST_DIR}/Debug_NonRedist/${CMAKE_MSVC_ARCH}/Microsoft.VC90.DebugMFC")
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/Microsoft.VC90.DebugMFC.manifest"
- "${MSVC_MFC_DIR}/mfc90d.dll"
- "${MSVC_MFC_DIR}/mfc90ud.dll"
- "${MSVC_MFC_DIR}/mfcm90d.dll"
- "${MSVC_MFC_DIR}/mfcm90ud.dll"
- )
- endif()
-
- set(MSVC_MFC_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.VC90.MFC")
- # Install the manifest that allows DLLs to be loaded from the
- # directory containing the executable.
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/Microsoft.VC90.MFC.manifest"
- "${MSVC_MFC_DIR}/mfc90.dll"
- "${MSVC_MFC_DIR}/mfc90u.dll"
- "${MSVC_MFC_DIR}/mfcm90.dll"
- "${MSVC_MFC_DIR}/mfcm90u.dll"
- )
- endif()
-
- # include the language dll's for vs9 as well as the actual dll's
- set(MSVC_MFCLOC_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.VC90.MFCLOC")
- # Install the manifest that allows DLLs to be loaded from the
- # directory containing the executable.
- set(__install__libs ${__install__libs}
- "${MSVC_MFCLOC_DIR}/Microsoft.VC90.MFCLOC.manifest"
- "${MSVC_MFCLOC_DIR}/mfc90chs.dll"
- "${MSVC_MFCLOC_DIR}/mfc90cht.dll"
- "${MSVC_MFCLOC_DIR}/mfc90enu.dll"
- "${MSVC_MFCLOC_DIR}/mfc90esp.dll"
- "${MSVC_MFCLOC_DIR}/mfc90deu.dll"
- "${MSVC_MFCLOC_DIR}/mfc90fra.dll"
- "${MSVC_MFCLOC_DIR}/mfc90ita.dll"
- "${MSVC_MFCLOC_DIR}/mfc90jpn.dll"
- "${MSVC_MFCLOC_DIR}/mfc90kor.dll"
- )
- endif()
-
- set(_MFC_DLL_VERSION "")
- set(_MFC_IDE_VERSION "")
- if(_MSVC_IDE_VERSION GREATER_EQUAL 10)
- set(_MFC_DLL_VERSION ${_MSVC_DLL_VERSION})
- set(_MFC_IDE_VERSION ${_MSVC_IDE_VERSION})
- endif()
-
- if(_MFC_DLL_VERSION)
- set(v "${_MFC_DLL_VERSION}")
- set(vs "${_MFC_IDE_VERSION}")
-
- # Starting with VS 15 the MFC DLLs may be in a different directory.
- if (NOT vs VERSION_LESS 15)
- file(GLOB _MSVC_REDIST_DIRS "${MSVC_REDIST_DIR}/../*")
- find_path(MSVC_REDIST_MFC_DIR NAMES ${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.MFC
- PATHS ${_MSVC_REDIST_DIRS} NO_DEFAULT_PATH)
- mark_as_advanced(MSVC_REDIST_MFC_DIR)
- unset(_MSVC_REDIST_DIRS)
- else()
- set(MSVC_REDIST_MFC_DIR "${MSVC_REDIST_DIR}")
- endif()
-
- # Multi-Byte Character Set versions of MFC are available as optional
- # addon since Visual Studio 12. So for version 12 or higher, check
- # whether they are available and exclude them if they are not.
-
- if(CMAKE_INSTALL_DEBUG_LIBRARIES)
- set(MSVC_MFC_DIR
- "${MSVC_REDIST_MFC_DIR}/Debug_NonRedist/${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.DebugMFC")
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/mfc${v}ud.dll"
- "${MSVC_MFC_DIR}/mfcm${v}ud.dll"
- )
- if("${v}" LESS 12 OR EXISTS "${MSVC_MFC_DIR}/mfc${v}d.dll")
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/mfc${v}d.dll"
- )
- endif()
- if("${v}" LESS 12 OR EXISTS "${MSVC_MFC_DIR}/mfcm${v}d.dll")
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/mfcm${v}d.dll"
- )
- endif()
- endif()
-
- set(MSVC_MFC_DIR "${MSVC_REDIST_MFC_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.MFC")
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/mfc${v}u.dll"
- "${MSVC_MFC_DIR}/mfcm${v}u.dll"
- )
- if("${v}" LESS 12 OR EXISTS "${MSVC_MFC_DIR}/mfc${v}.dll")
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/mfc${v}.dll"
- )
- endif()
- if("${v}" LESS 12 OR EXISTS "${MSVC_MFC_DIR}/mfcm${v}.dll")
- set(__install__libs ${__install__libs}
- "${MSVC_MFC_DIR}/mfcm${v}.dll"
- )
- endif()
- endif()
-
- # include the language dll's as well as the actual dll's
- set(MSVC_MFCLOC_DIR "${MSVC_REDIST_MFC_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.MFCLOC")
- set(__install__libs ${__install__libs}
- "${MSVC_MFCLOC_DIR}/mfc${v}chs.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}cht.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}deu.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}enu.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}esn.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}fra.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}ita.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}jpn.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}kor.dll"
- "${MSVC_MFCLOC_DIR}/mfc${v}rus.dll"
- )
- endif()
- endif()
-
- # MSVC 8 was the first version with OpenMP
- # Furthermore, there is no debug version of this
- if(CMAKE_INSTALL_OPENMP_LIBRARIES AND _IRSL_HAVE_MSVC)
- set(_MSOMP_DLL_VERSION ${_MSVC_DLL_VERSION})
- set(_MSOMP_IDE_VERSION ${_MSVC_IDE_VERSION})
-
- if(_MSOMP_DLL_VERSION)
- set(v "${_MSOMP_DLL_VERSION}")
- set(vs "${_MSOMP_IDE_VERSION}")
- set(MSVC_OPENMP_DIR "${MSVC_REDIST_DIR}/${CMAKE_MSVC_ARCH}/Microsoft.${MSVC_REDIST_NAME}.OPENMP")
-
- if(NOT CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY)
- set(__install__libs ${__install__libs}
- "${MSVC_OPENMP_DIR}/vcomp${v}.dll")
- endif()
- endif()
- endif()
-
- foreach(lib
- ${__install__libs}
- )
- if(EXISTS ${lib})
- set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
- ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} ${lib})
- else()
- if(NOT CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS)
- message(WARNING "system runtime library file does not exist: '${lib}'")
- # This warning indicates an incomplete Visual Studio installation
- # or a bug somewhere above here in this file.
- # If you would like to avoid this warning, fix the real problem, or
- # set CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS before including
- # this file.
- endif()
- endif()
- endforeach()
-endif()
-
-if(_IRSL_HAVE_Intel)
- unset(__install_libs)
- if(CMAKE_INSTALL_OPENMP_LIBRARIES)
- if(WIN32)
- list(APPEND __install_libs "${_Intel_redistdir}/libiomp5md.dll" "${_Intel_redistdir}/libiompstubs5md.dll")
- elseif(APPLE)
- list(APPEND __install_libs "${_Intel_redistdir}/libiomp5.dylib" "${_Intel_redistdir}/libiompstubs5.dylib")
- else()
- list(APPEND __install_libs "${_Intel_redistdir}/libiomp5.so" "${_Intel_redistdir}/libiompstubs5.so")
- if(_Intel_compiler_ver VERSION_LESS 17)
- list(APPEND __install_libs "${_Intel_redistdir}/libomp_db.so")
- endif()
- if(_Intel_compiler_ver VERSION_LESS 13)
- list(APPEND __install_libs "${_Intel_redistdir}/libiompprof5.so")
- endif()
- endif()
- endif()
- if(WIN32)
- set(__install_dirs "${_Intel_redistdir}/1033")
- if(EXISTS "${_Intel_redistdir}/1041")
- list(APPEND __install_dirs "${_Intel_redistdir}/1041")
- endif()
- if(_Intel_compiler_ver VERSION_LESS 18)
- list(APPEND __install_dirs "${_Intel_redistdir}/irml" "${_Intel_redistdir}/irml_c")
- endif()
- foreach(__Intel_lib IN ITEMS cilkrts20.dll libchkp.dll libioffload_host.dll libirngmd.dll
- libmmd.dll libmmdd.dll libmpx.dll liboffload.dll svml_dispmd.dll)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- if(CMAKE_C_COMPILER_ID STREQUAL Intel OR CMAKE_CXX_COMPILER_ID STREQUAL Intel)
- list(APPEND __install_libs "${_Intel_redistdir}/libgfxoffload.dll")
- endif()
- if(CMAKE_Fortran_COMPILER_ID STREQUAL Intel)
- foreach(__Intel_lib IN ITEMS ifdlg100.dll libicaf.dll libifcoremd.dll libifcoremdd.dll libifcorert.dll libifcorertd.dll libifportmd.dll)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- endif()
- elseif(APPLE)
- foreach(__Intel_lib IN ITEMS libchkp.dylib libcilkrts.5.dylib libcilkrts.dylib libimf.dylib libintlc.dylib libirc.dylib libirng.dylib libsvml.dylib)
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- if(CMAKE_C_COMPILER_ID STREQUAL Intel OR CMAKE_CXX_COMPILER_ID STREQUAL Intel)
- if(_Intel_compiler_ver VERSION_LESS 17)
- list(APPEND __install_libs "${_Intel_redistdir}/libistrconv.dylib")
- endif()
- endif()
- if(CMAKE_Fortran_COMPILER_ID STREQUAL Intel)
- foreach(__Intel_lib IN ITEMS libifcore.dylib libifcoremt.dylib libifport.dylib libifportmt.dylib)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- endif()
- else()
- foreach(__Intel_lib IN ITEMS libchkp.so libcilkrts.so libcilkrts.so.5 libimf.so libintlc.so libintlc.so.5 libirc.so libpdbx.so libpdbx.so.5 libsvml.so)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- if(_Intel_compiler_ver VERSION_GREATER_EQUAL 13)
- foreach(__Intel_lib IN ITEMS libirng.so liboffload.so liboffload.so.5)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- endif()
- if(CMAKE_C_COMPILER_ID STREQUAL Intel OR CMAKE_CXX_COMPILER_ID STREQUAL Intel)
- set(__install_dirs "${_Intel_redistdir}/irml")
- list(APPEND __install_libs "${_Intel_redistdir}/cilk_db.so")
- if(_Intel_compiler_ver VERSION_GREATER_EQUAL 15)
- list(APPEND __install_libs "${_Intel_redistdir}/libistrconv.so" "${_Intel_redistdir}/libgfxoffload.so")
- endif()
- endif()
- if(_Intel_compiler_ver VERSION_GREATER_EQUAL 16)
- foreach(__Intel_lib IN ITEMS libioffload_host.so libioffload_host.so.5 libioffload_target.so libioffload_target.so.5 libmpx.so offload_main)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- endif()
- if(_Intel_compiler_ver VERSION_LESS 15)
- foreach(__Intel_lib IN ITEMS libcxaguard.so libcxaguard.so.5)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- endif()
- if(CMAKE_Fortran_COMPILER_ID STREQUAL Intel)
- foreach(__Intel_lib IN ITEMS libicaf.so libifcore.so libifcore.so.5 libifcoremt.so libifcoremt.so.5 libifport.so libifport.so.5)
-
- list(APPEND __install_libs "${_Intel_redistdir}/${__Intel_lib}")
- endforeach()
- endif()
- endif()
-
- foreach(lib IN LISTS __install_libs)
- if(EXISTS ${lib})
- list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${lib})
- else()
- if(NOT CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS)
- message(WARNING "system runtime library file does not exist: '${lib}'")
- endif()
- endif()
- endforeach()
-
- foreach(dir IN LISTS __install_dirs)
- if(EXISTS ${dir})
- list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_DIRECTORIES ${dir})
- else()
- if(NOT CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS)
- message(WARNING "system runtime library file does not exist: '${dir}'")
- endif()
- endif()
- endforeach()
-endif()
-
-if(WATCOM)
- get_filename_component( CompilerPath ${CMAKE_C_COMPILER} PATH )
- if(CMAKE_C_COMPILER_VERSION)
- set(_compiler_version ${CMAKE_C_COMPILER_VERSION})
- else()
- set(_compiler_version ${CMAKE_CXX_COMPILER_VERSION})
- endif()
- string(REGEX MATCHALL "[0-9]+" _watcom_version_list "${_compiler_version}")
- list(GET _watcom_version_list 0 _watcom_major)
- list(GET _watcom_version_list 1 _watcom_minor)
- set( __install__libs
- ${CompilerPath}/clbr${_watcom_major}${_watcom_minor}.dll
- ${CompilerPath}/mt7r${_watcom_major}${_watcom_minor}.dll
- ${CompilerPath}/plbr${_watcom_major}${_watcom_minor}.dll )
- foreach(lib
- ${__install__libs}
- )
- if(EXISTS ${lib})
- set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
- ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} ${lib})
- else()
- if(NOT CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS)
- message(WARNING "system runtime library file does not exist: '${lib}'")
- # This warning indicates an incomplete Watcom installation
- # or a bug somewhere above here in this file.
- # If you would like to avoid this warning, fix the real problem, or
- # set CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS before including
- # this file.
- endif()
- endif()
- endforeach()
-endif()
-
-
-# Include system runtime libraries in the installation if any are
-# specified by CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS.
-if(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
- if(NOT CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP)
- if(NOT CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION)
- if(WIN32)
- set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION bin)
- else()
- set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION lib)
- endif()
- endif()
- if(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT)
- set(_CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT
- COMPONENT ${CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT})
- endif()
- install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
- DESTINATION ${CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION}
- ${_CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT}
- )
-
- install(DIRECTORY ${CMAKE_INSTALL_SYSTEM_RUNTIME_DIRECTORIES}
- DESTINATION ${CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION}
- ${_CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT}
- )
- endif()
-endif()
-
-cmake_policy(POP)
diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake
index 6cc325495..169ba4eb6 100644
--- a/indra/cmake/LLCommon.cmake
+++ b/indra/cmake/LLCommon.cmake
@@ -10,7 +10,6 @@ if (DARWIN)
find_library(CORESERVICES_LIBRARY CoreServices)
endif (DARWIN)
-
set(LLCOMMON_INCLUDE_DIRS
${LIBS_OPEN_DIR}/cwdebug
${LIBS_OPEN_DIR}/llcommon
@@ -19,14 +18,7 @@ set(LLCOMMON_INCLUDE_DIRS
${Boost_INCLUDE_DIRS}
)
-if (LINUX)
- # In order to support using ld.gold on linux, we need to explicitely
- # specify all libraries that llcommon uses.
- # llcommon uses `clock_gettime' which is provided by librt on linux.
- set(LLCOMMON_LIBRARIES llcommon rt)
-else (LINUX)
- set(LLCOMMON_LIBRARIES llcommon)
-endif (LINUX)
+set(LLCOMMON_LIBRARIES llcommon)
set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a shared library.")
if(LLCOMMON_LINK_SHARED)
diff --git a/indra/cmake/LLPlugin.cmake b/indra/cmake/LLPlugin.cmake
index 399cb332d..9722f16c3 100644
--- a/indra/cmake/LLPlugin.cmake
+++ b/indra/cmake/LLPlugin.cmake
@@ -5,10 +5,4 @@ set(LLPLUGIN_INCLUDE_DIRS
${LIBS_OPEN_DIR}/llplugin
)
-if (LINUX)
- # In order to support using ld.gold on linux, we need to explicitely
- # specify all libraries that llplugin uses.
- set(LLPLUGIN_LIBRARIES llplugin pthread)
-else (LINUX)
- set(LLPLUGIN_LIBRARIES llplugin)
-endif (LINUX)
+set(LLPLUGIN_LIBRARIES llplugin)
diff --git a/indra/cmake/NVAPI.cmake b/indra/cmake/NVAPI.cmake
index 080d8fcf7..3cdf5ca47 100644
--- a/indra/cmake/NVAPI.cmake
+++ b/indra/cmake/NVAPI.cmake
@@ -2,18 +2,18 @@
include(Prebuilt)
include(Variables)
-if (NVAPI)
+if (USE_NVAPI)
if (WINDOWS)
use_prebuilt_binary(nvapi)
- if (WORD_SIZE EQUAL 32)
+ if (ADDRESS_SIZE EQUAL 32)
set(NVAPI_LIBRARY nvapi)
- elseif (WORD_SIZE EQUAL 64)
+ elseif (ADDRESS_SIZE EQUAL 64)
set(NVAPI_LIBRARY nvapi64)
- endif (WORD_SIZE EQUAL 32)
+ endif (ADDRESS_SIZE EQUAL 32)
else (WINDOWS)
set(NVAPI_LIBRARY "")
endif (WINDOWS)
-else (NVAPI)
+else (USE_NVAPI)
set(NVAPI_LIBRARY "")
-endif (NVAPI)
+endif (USE_NVAPI)
diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake
index 51e10a09a..b1afa31e2 100644
--- a/indra/cmake/Variables.cmake
+++ b/indra/cmake/Variables.cmake
@@ -17,6 +17,14 @@ if(NOT DEFINED COMMON_CMAKE_DIR)
set(COMMON_CMAKE_DIR "${CMAKE_SOURCE_DIR}/cmake")
endif(NOT DEFINED COMMON_CMAKE_DIR)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# https://blog.kitware.com/upcoming-in-cmake-2-8-12-osx-rpath-support/
+set(CMAKE_MACOSX_RPATH ON)
+set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF)
+
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
@@ -28,23 +36,40 @@ set(LIBS_OPEN_PREFIX)
set(SCRIPTS_PREFIX ../scripts)
set(VIEWER_PREFIX)
set(INTEGRATION_TESTS_PREFIX)
+
option(LL_TESTS "Build and run unit and integration tests (disable for build timing runs to reduce variation" OFF)
option(BUILD_TESTING "Build test suite" OFF)
+option(UNATTENDED "Disable use of uneeded tooling for automated builds" OFF)
+# Compiler and toolchain options
+option(USESYSTEMLIBS "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF)
+option(STANDALONE "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF)
+if (USESYSTEMLIBS)
+ set(STANDALONE ON)
+elseif (STANDALONE)
+ set(USESYSTEMLIBS ON)
+endif (USESYSTEMLIBS)
option(INCREMENTAL_LINK "Use incremental linking on win32 builds (enable for faster links on some machines)" OFF)
option(USE_PRECOMPILED_HEADERS "Enable use of precompiled header directives where supported." ON)
-option(USE_LTO "Enable Whole Program Optimization and related folding and binary reduction routines" OFF)
-option(UNATTENDED "Disable use of uneeded tooling for automated builds" OFF)
+option(USE_LTO "Enable global and interprocedural optimizations" OFF)
# Configure crash reporting
option(USE_CRASHPAD "Build support for crashpad reporting engine" OFF)
-set(CRASHPAD_URL "" CACHE STRING "Crashpad endpoint url")
+if (DEFINED ENV{VIEWER_USE_CRASHPAD})
+ set(USE_CRASHPAD $ENV{VIEWER_USE_CRASHPAD})
+endif()
+
+if (DEFINED ENV{VIEWER_CRASHPAD_URL})
+ set(CRASHPAD_URL $ENV{VIEWER_CRASHPAD_URL} CACHE STRING "Viewer Channel Base Name")
+else()
+ set(CRASHPAD_URL "" CACHE STRING "Crashpad endpoint url")
+endif()
+
set(VIEWER_SYMBOL_FILE "" CACHE STRING "Name of tarball into which to place symbol files")
# Media Plugins
option(ENABLE_MEDIA_PLUGINS "Turn off building media plugins if they are imported by third-party library mechanism" ON)
option(LIBVLCPLUGIN "Turn off building support for libvlc plugin" ON)
-
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LIBVLCPLUGIN OFF)
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
@@ -54,13 +79,11 @@ set(DISABLE_TCMALLOC OFF CACHE BOOL "Disable linkage of TCMalloc. (64bit builds
set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.")
# Audio Engines
-option(FMODSTUDIO "Build with support for the FMOD Studio audio engine" ON)
-
-# Window implementation
-option(LLWINDOW_SDL2 "Use SDL2 for window and input handling" OFF)
+option(USE_FMODSTUDIO "Build with support for the FMOD Studio audio engine" OFF)
# Proprietary Library Features
-option(NVAPI "Use nvapi driver interface library" OFF)
+option(USE_NVAPI "Use nvapi driver interface library" OFF)
+
if(LIBS_CLOSED_DIR)
file(TO_CMAKE_PATH "${LIBS_CLOSED_DIR}" LIBS_CLOSED_DIR)
@@ -87,7 +110,7 @@ if (EXISTS ${CMAKE_SOURCE_DIR}/Server.cmake)
set(INSTALL_PROPRIETARY ON CACHE BOOL "Install proprietary binaries")
endif (EXISTS ${CMAKE_SOURCE_DIR}/Server.cmake)
set(TEMPLATE_VERIFIER_OPTIONS "" CACHE STRING "Options for scripts/template_verifier.py")
-set(TEMPLATE_VERIFIER_MASTER_URL "https://forge.alchemyviewer.org/alchemy/tools/Master-Message-Template/raw/master/message_template.msg" CACHE STRING "Location of the master message template")
+set(TEMPLATE_VERIFIER_MASTER_URL "https://git.alchemyviewer.org/alchemy/master-message-template/raw/master/message_template.msg" CACHE STRING "Location of the master message template")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
@@ -96,7 +119,6 @@ endif (NOT CMAKE_BUILD_TYPE)
# If someone has specified an address size, use that to determine the
# architecture. Otherwise, let the architecture specify the address size.
-set(ADDRESS_SIZE ${WORD_SIZE})
if (ADDRESS_SIZE EQUAL 32)
#message(STATUS "ADDRESS_SIZE is 32")
set(ARCH i686)
@@ -160,7 +182,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
- set(DARWIN 1)
+ set(DARWIN ON BOOL FORCE)
# Architecture
set(CMAKE_OSX_SYSROOT macosx10.14)
@@ -200,57 +222,60 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LL_ARCH_DIR universal-darwin)
endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+# Platform specific
+if (WINDOWS)
+ option(LLWINDOW_SDL2 "Use SDL2 for window and input handling. Windows only" OFF)
+endif()
+
# Default deploy grid
set(GRID agni CACHE STRING "Target Grid")
-set(VIEWER_PRODUCT_NAME "Singularity" CACHE STRING "Viewer Base Name")
-string(TOLOWER ${VIEWER_PRODUCT_NAME} VIEWER_PRODUCT_NAME_LOWER)
+if (DEFINED ENV{VIEWER_CHANNEL_BASE})
+ set(VIEWER_CHANNEL_BASE $ENV{VIEWER_CHANNEL_BASE} CACHE STRING "Viewer Channel Base Name" FORCE)
+else()
+ set(VIEWER_CHANNEL_BASE "Singularity" CACHE STRING "Viewer Channel Base Name")
+endif()
+
+if (DEFINED ENV{VIEWER_CHANNEL_TYPE})
+ set(VIEWER_CHANNEL_TYPE $ENV{VIEWER_CHANNEL_TYPE} CACHE STRING "Viewer Channel Type Name" FORCE)
+else()
+ set(VIEWER_CHANNEL_TYPE "Test" CACHE STRING "Viewer Channel Type Name")
+endif()
+
+if (DEFINED ENV{VIEWER_CHANNEL_CODENAME})
+ set(VIEWER_CHANNEL_CODENAME $ENV{VIEWER_CHANNEL_CODENAME} CACHE STRING "Viewer Channel Code Name for Project type" FORCE)
+else()
+ set(VIEWER_CHANNEL_CODENAME "Default" CACHE STRING "Viewer Channel Code Name for Project type")
+endif()
+
+if("${VIEWER_CHANNEL_TYPE}" STREQUAL "Project")
+ set(VIEWER_CHANNEL "${VIEWER_CHANNEL_BASE} ${VIEWER_CHANNEL_TYPE} ${VIEWER_CHANNEL_CODENAME}")
+else()
+ set(VIEWER_CHANNEL "${VIEWER_CHANNEL_BASE} ${VIEWER_CHANNEL_TYPE}")
+endif()
+
+string(TOLOWER "${VIEWER_CHANNEL_BASE}" VIEWER_BRANDING_ID)
+string(REPLACE " " "-" VIEWER_BRANDING_ID ${VIEWER_BRANDING_ID})
+set(VIEWER_BINARY_NAME "${VIEWER_BRANDING_ID}-bin" CACHE STRING
+ "The name of the viewer executable to create.")
-set(VIEWER_CHANNEL_BASE "Test" CACHE STRING "Viewer Channel Name")
-set(VIEWER_CHANNEL "${VIEWER_PRODUCT_NAME} ${VIEWER_CHANNEL_BASE}")
-string(TOLOWER ${VIEWER_CHANNEL} VIEWER_CHANNEL_LOWER)
string(REPLACE " " "" VIEWER_CHANNEL_ONEWORD ${VIEWER_CHANNEL})
+set(VIEWER_CHANNEL_NOSPACE ${VIEWER_CHANNEL_ONEWORD} CACHE STRING "Prefix used for resulting artifacts.")
option(VIEWER_CHANNEL_GRK "Greek character(s) to represent the viewer channel for support purposes, override only for special branches" "")
if (NOT VIEWER_CHANNEL_GRK)
- if (VIEWER_CHANNEL_BASE MATCHES "Test")
- set(VIEWER_CHANNEL_GRK "\\u03C4") # "τ"
- elseif (VIEWER_CHANNEL_BASE MATCHES "Alpha")
- set(VIEWER_CHANNEL_GRK "\\u03B1") # "α"
- elseif (VIEWER_CHANNEL_BASE MATCHES "Beta")
- set(VIEWER_CHANNEL_GRK "\\u03B2") # "β"
- endif ()
+ if (VIEWER_CHANNEL_TYPE MATCHES "Test")
+ set(VIEWER_CHANNEL_GRK "\\u03C4") # "τ"
+ elseif (VIEWER_CHANNEL_TYPE MATCHES "Alpha")
+ set(VIEWER_CHANNEL_GRK "\\u03B1") # "α"
+ elseif (VIEWER_CHANNEL_TYPE MATCHES "Beta")
+ set(VIEWER_CHANNEL_GRK "\\u03B2") # "β"
+ endif ()
endif (NOT VIEWER_CHANNEL_GRK)
-if(VIEWER_CHANNEL_LOWER MATCHES "^${VIEWER_PRODUCT_NAME_LOWER} release")
- set(VIEWER_PACKAGE_ID "${VIEWER_PRODUCT_NAME}Release")
- set(VIEWER_EXE_STRING "${VIEWER_PRODUCT_NAME}Viewer")
- set(VIEWER_SHORTCUT_STRING "${VIEWER_PRODUCT_NAME} Viewer")
-else()
- set(VIEWER_PACKAGE_ID ${VIEWER_CHANNEL_ONEWORD})
- set(VIEWER_EXE_STRING ${VIEWER_CHANNEL_ONEWORD})
- set(VIEWER_SHORTCUT_STRING ${VIEWER_CHANNEL})
-endif()
-
-set(VIEWER_CHANNEL_NOSPACE ${VIEWER_CHANNEL_ONEWORD} CACHE STRING "Prefix used for resulting artifacts.")
-
-set(VIEWER_BRANDING_ID "singularity" CACHE STRING "Viewer branding id")
-
option(ENABLE_SIGNING "Enable signing the viewer" OFF)
set(SIGNING_IDENTITY "" CACHE STRING "Specifies the signing identity to use, if necessary.")
-set(VERSION_BUILD "0" CACHE STRING "Revision number passed in from the outside")
-# Compiler and toolchain options
-option(USESYSTEMLIBS "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF)
-option(STANDALONE "Use libraries from your system rather than Linden-supplied prebuilt libraries." OFF)
-if (USESYSTEMLIBS)
- set(STANDALONE ON)
-elseif (STANDALONE)
- set(USESYSTEMLIBS ON)
-endif (USESYSTEMLIBS)
-
-
-
source_group("CMake Rules" FILES CMakeLists.txt)
endif(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED)
diff --git a/indra/deps/CMakeLists.txt b/indra/deps/CMakeLists.txt
index 47b676d2c..fb2ed3050 100644
--- a/indra/deps/CMakeLists.txt
+++ b/indra/deps/CMakeLists.txt
@@ -3,21 +3,22 @@ project(deps)
include(FetchContent)
set(CMAKE_FOLDER "Third Party")
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
- GIT_TAG v2.10.0
+ GIT_TAG v2.11.0
)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
- GIT_TAG 7512a55aa3ae309587ca89668ef9ec4074a51a1f
+ GIT_TAG 6.1.2
)
FetchContent_Declare(
absl
GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
- GIT_TAG ab3552a18964e7063c8324f45b3896a6a20b08a8
+ GIT_TAG 29235139149790f5afc430c11cec8f1eb1677607
)
# This is a hack because absl has dumb cmake
@@ -43,4 +44,5 @@ if(WINDOWS)
FetchContent_MakeAvailable(fmt)
endif()
-set(CMAKE_FOLDER "")
+unset(CMAKE_FOLDER)
+unset(CMAKE_POSITION_INDEPENDENT_CODE)
diff --git a/indra/llappearance/llwearabletype.h b/indra/llappearance/llwearabletype.h
index 6dec79fcb..ec946d5ad 100644
--- a/indra/llappearance/llwearabletype.h
+++ b/indra/llappearance/llwearabletype.h
@@ -58,6 +58,7 @@ public:
WT_UNKNOWN = 17, // Singu note: used for corrupt wearables that do not have their type set in the inventory database.
// While all the above values are serialized and stored in the database, this value is local only:
// When a new item with value 17 is added by upstream, just increase this value to 18 (and WT_COUNT to 19).
+ // Keep WT_UNKNOWN and WT_COUNT in sync with llinventory.cpp
WT_COUNT = 18,
WT_INVALID = 255,
diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt
index 20456f5dc..92aa14ddc 100644
--- a/indra/llaudio/CMakeLists.txt
+++ b/indra/llaudio/CMakeLists.txt
@@ -43,7 +43,7 @@ set(llaudio_HEADER_FILES
llwindgen.h
)
-if (FMODSTUDIO)
+if (USE_FMODSTUDIO)
include_directories(
${FMODSTUDIO_INCLUDE_DIR}
)
@@ -58,7 +58,7 @@ if (FMODSTUDIO)
lllistener_fmodstudio.h
llstreamingaudio_fmodstudio.h
)
-endif (FMODSTUDIO)
+endif (USE_FMODSTUDIO)
if (OPENAL)
list(APPEND llaudio_SOURCE_FILES
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index cae62271d..9baf87d9d 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -269,12 +269,6 @@ list(APPEND llcommon_SOURCE_FILES ${cwdebug_SOURCE_FILES})
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
-if(NOT WORD_SIZE EQUAL 32)
- if(NOT WINDOWS)
- add_definitions(-fPIC)
- endif(NOT WINDOWS)
-endif(NOT WORD_SIZE EQUAL 32)
-
if(LLCOMMON_LINK_SHARED)
add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
if(WINDOWS)
@@ -286,6 +280,8 @@ else(LLCOMMON_LINK_SHARED)
add_library (llcommon ${llcommon_SOURCE_FILES})
endif(LLCOMMON_LINK_SHARED)
+set_target_properties(llcommon PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
+
target_link_libraries(
llcommon
PUBLIC
@@ -304,6 +300,7 @@ target_link_libraries(
${Boost_SYSTEM_LIBRARY}
${CORESERVICES_LIBRARY}
${URIPARSER_LIBRARY}
+ ${RT_LIBRARY}
)
if (DARWIN)
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 3cb14a2ca..f6c58513b 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -1338,7 +1338,7 @@ namespace LLError
}
#if LL_WINDOWS
- // VC80 was optimizing the error away.
+ // MSVC is optimizing the error away.
#pragma optimize("", off)
#endif
void crashAndLoop(const std::string& message)
@@ -1347,9 +1347,8 @@ namespace LLError
DoutFatal(dc::core, message);
#else
// Now, we go kaboom!
- int* make_me_crash = NULL;
-
- *make_me_crash = 0;
+ int* make_me_crash = nullptr;
+ *make_me_crash = 0xDEADBEEF;
while(true)
{
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index e68ccd2ad..0b7677fad 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -282,6 +282,11 @@ int LLFile::rename_nowarn(const std::string& filename, const std::string& newnam
int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
#else
int rc = ::rename(filename.c_str(),newname.c_str());
+ if (rc == -1 && errno == EXDEV)
+ {
+ rc = std::system(("mv '" + filename + "' '" + newname + '\'').data());
+ errno = 0;
+ }
#endif
return rc;
}
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
index 3883b7e8e..73a0ab3e7 100644
--- a/indra/llcommon/llsd.cpp
+++ b/indra/llcommon/llsd.cpp
@@ -126,9 +126,9 @@ public:
virtual Date asDate() const { return LLDate(); }
virtual URI asURI() const { return LLURI(); }
virtual const Binary& asBinary() const { static const std::vector empty; return empty; }
-
- virtual const String& asStringRef() const { static const std::string empty; return empty; }
+ virtual const String& asStringRef() const { static const std::string empty; return empty; }
+
virtual bool has(const String&) const { return false; }
virtual LLSD get(const String&) const { return LLSD(); }
virtual LLSD getKeys() const { return LLSD::emptyArray(); }
@@ -142,11 +142,11 @@ public:
virtual const std::map& map() const { static const std::map empty; return empty; }
virtual std::map& map() { static std::map empty; return empty; }
- LLSD::map_const_iterator beginMap() const { return endMap(); }
+ LLSD::map_const_iterator beginMap() const { return map().begin(); }
LLSD::map_const_iterator endMap() const { return map().end(); }
virtual const std::vector& array() const { static const std::vector empty; return empty; }
virtual std::vector& array() { static std::vector empty; return empty; }
- LLSD::array_const_iterator beginArray() const { return endArray(); }
+ LLSD::array_const_iterator beginArray() const { return array().begin(); }
LLSD::array_const_iterator endArray() const { return array().end(); }
virtual void dumpStats() const;
@@ -187,10 +187,11 @@ namespace
public:
ImplBase(DataRef value) : mValue(value) { }
- virtual LLSD::Type type() const { return T; }
+ LLSD::Type type() const override { return T; }
using LLSD::Impl::assign; // Unhiding base class virtuals...
- virtual void assign(LLSD::Impl*& var, DataRef value) {
+ void assign(LLSD::Impl*& var, DataRef value) override
+ {
if (shared())
{
Impl::assign(var, value);
@@ -203,16 +204,16 @@ namespace
};
- class ImplBoolean
+ class ImplBoolean final
: public ImplBase
{
public:
ImplBoolean(LLSD::Boolean v) : Base(v) { }
- virtual LLSD::Boolean asBoolean() const { return mValue; }
- virtual LLSD::Integer asInteger() const { return mValue ? 1 : 0; }
- virtual LLSD::Real asReal() const { return mValue ? 1 : 0; }
- virtual LLSD::String asString() const;
+ LLSD::Boolean asBoolean() const override { return mValue; }
+ LLSD::Integer asInteger() const override { return mValue ? 1 : 0; }
+ LLSD::Real asReal() const override { return mValue ? 1 : 0; }
+ LLSD::String asString() const override;
};
LLSD::String ImplBoolean::asString() const
@@ -224,32 +225,32 @@ namespace
{ return mValue ? "true" : ""; }
- class ImplInteger
+ class ImplInteger final
: public ImplBase
{
public:
ImplInteger(LLSD::Integer v) : Base(v) { }
- virtual LLSD::Boolean asBoolean() const { return mValue != 0; }
- virtual LLSD::Integer asInteger() const { return mValue; }
- virtual LLSD::Real asReal() const { return mValue; }
- virtual LLSD::String asString() const;
+ LLSD::Boolean asBoolean() const override { return mValue != 0; }
+ LLSD::Integer asInteger() const override { return mValue; }
+ LLSD::Real asReal() const override { return mValue; }
+ LLSD::String asString() const override;
};
LLSD::String ImplInteger::asString() const
{ return llformat("%d", mValue); }
- class ImplReal
+ class ImplReal final
: public ImplBase
{
public:
ImplReal(LLSD::Real v) : Base(v) { }
- virtual LLSD::Boolean asBoolean() const;
- virtual LLSD::Integer asInteger() const;
- virtual LLSD::Real asReal() const { return mValue; }
- virtual LLSD::String asString() const;
+ LLSD::Boolean asBoolean() const override;
+ LLSD::Integer asInteger() const override;
+ LLSD::Real asReal() const override { return mValue; }
+ LLSD::String asString() const override;
};
LLSD::Boolean ImplReal::asBoolean() const
@@ -262,21 +263,21 @@ namespace
{ return llformat("%lg", mValue); }
- class ImplString
+ class ImplString final
: public ImplBase
{
public:
ImplString(const LLSD::String& v) : Base(v) { }
- virtual LLSD::Boolean asBoolean() const { return !mValue.empty(); }
- virtual LLSD::Integer asInteger() const;
- virtual LLSD::Real asReal() const;
- virtual LLSD::String asString() const { return mValue; }
- virtual LLSD::UUID asUUID() const { return LLUUID(mValue); }
- virtual LLSD::Date asDate() const { return LLDate(mValue); }
- virtual LLSD::URI asURI() const { return LLURI(mValue); }
- virtual int size() const { return mValue.size(); }
- virtual const LLSD::String& asStringRef() const { return mValue; }
+ LLSD::Boolean asBoolean() const override { return !mValue.empty(); }
+ LLSD::Integer asInteger() const override;
+ LLSD::Real asReal() const override;
+ LLSD::String asString() const override { return mValue; }
+ LLSD::UUID asUUID() const override { return LLUUID(mValue); }
+ LLSD::Date asDate() const override { return LLDate(mValue); }
+ LLSD::URI asURI() const override { return LLURI(mValue); }
+ int size() const override { return mValue.size(); }
+ const LLSD::String& asStringRef() const override { return mValue; }
};
LLSD::Integer ImplString::asInteger() const
@@ -306,18 +307,18 @@ namespace
}
- class ImplUUID
+ class ImplUUID final
: public ImplBase
{
public:
ImplUUID(const LLSD::UUID& v) : Base(v) { }
- virtual LLSD::String asString() const{ return mValue.asString(); }
- virtual LLSD::UUID asUUID() const { return mValue; }
+ LLSD::String asString() const override { return mValue.asString(); }
+ LLSD::UUID asUUID() const override { return mValue; }
};
- class ImplDate
+ class ImplDate final
: public ImplBase
{
public:
@@ -325,41 +326,42 @@ namespace
: ImplBase(v)
{ }
- virtual LLSD::Integer asInteger() const
+ LLSD::Integer asInteger() const override
{
return (LLSD::Integer)(mValue.secondsSinceEpoch());
}
- virtual LLSD::Real asReal() const
+ LLSD::Real asReal() const override
{
return mValue.secondsSinceEpoch();
}
- virtual LLSD::String asString() const{ return mValue.asString(); }
- virtual LLSD::Date asDate() const { return mValue; }
+
+ LLSD::String asString() const override { return mValue.asString(); }
+ LLSD::Date asDate() const override { return mValue; }
};
- class ImplURI
+ class ImplURI final
: public ImplBase
{
public:
ImplURI(const LLSD::URI& v) : Base(v) { }
- virtual LLSD::String asString() const{ return mValue.asString(); }
- virtual LLSD::URI asURI() const { return mValue; }
+ LLSD::String asString() const override { return mValue.asString(); }
+ LLSD::URI asURI() const override { return mValue; }
};
- class ImplBinary
+ class ImplBinary final
: public ImplBase
{
public:
ImplBinary(const LLSD::Binary& v) : Base(v) { }
- virtual const LLSD::Binary& asBinary() const{ return mValue; }
+ const LLSD::Binary& asBinary() const override { return mValue; }
};
- class ImplMap : public LLSD::Impl
+ class ImplMap final : public LLSD::Impl
{
private:
typedef std::map DataMap;
@@ -372,31 +374,31 @@ namespace
public:
ImplMap() { }
- virtual ImplMap& makeMap(LLSD::Impl*&);
+ ImplMap& makeMap(LLSD::Impl*&) override;
- virtual LLSD::Type type() const { return LLSD::TypeMap; }
+ LLSD::Type type() const override { return LLSD::TypeMap; }
- virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
+ LLSD::Boolean asBoolean() const override { return !mData.empty(); }
- virtual bool has(const LLSD::String&) const;
+ bool has(const LLSD::String&) const override;
using LLSD::Impl::get; // Unhiding get(LLSD::Integer)
using LLSD::Impl::erase; // Unhiding erase(LLSD::Integer)
using LLSD::Impl::ref; // Unhiding ref(LLSD::Integer)
- virtual LLSD get(const LLSD::String&) const;
- virtual LLSD getKeys() const;
+ LLSD get(const LLSD::String&) const override;
+ LLSD getKeys() const override;
void insert(const LLSD::String& k, const LLSD& v);
- virtual void erase(const LLSD::String&);
+ void erase(const LLSD::String&) override;
LLSD& ref(const LLSD::String&);
- virtual const LLSD& ref(const LLSD::String&) const;
+ const LLSD& ref(const LLSD::String&) const override;
- virtual int size() const { return mData.size(); }
+ int size() const override { return mData.size(); }
DataMap& map() final override { return mData; }
const DataMap& map() const final override { return mData; }
- virtual void dumpStats() const;
- virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
+ void dumpStats() const override;
+ void calcStats(S32 type_counts[], S32 share_counts[]) const override;
};
ImplMap& ImplMap::makeMap(LLSD::Impl*& var)
@@ -483,7 +485,7 @@ namespace
{
//std::cout << " " << (*iter).first << ": " << (*iter).second << std::endl;
Impl::calcStats((*iter).second, type_counts, share_counts);
- iter++;
+ ++iter;
}
// Add in the values for this map
@@ -491,7 +493,7 @@ namespace
}
- class ImplArray : public LLSD::Impl
+ class ImplArray final : public LLSD::Impl
{
private:
typedef std::vector DataVector;
@@ -504,28 +506,28 @@ namespace
public:
ImplArray() { }
- virtual ImplArray& makeArray(Impl*&);
+ ImplArray& makeArray(Impl*&) override;
- virtual LLSD::Type type() const { return LLSD::TypeArray; }
+ LLSD::Type type() const override { return LLSD::TypeArray; }
- virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
+ LLSD::Boolean asBoolean() const override { return !mData.empty(); }
using LLSD::Impl::get; // Unhiding get(LLSD::String)
using LLSD::Impl::erase; // Unhiding erase(LLSD::String)
using LLSD::Impl::ref; // Unhiding ref(LLSD::String)
- virtual int size() const;
- virtual LLSD get(LLSD::Integer) const;
+ int size() const override;
+ LLSD get(LLSD::Integer) const override;
void set(LLSD::Integer, const LLSD&);
void insert(LLSD::Integer, const LLSD&);
LLSD& append(const LLSD&);
- virtual void erase(LLSD::Integer);
+ void erase(LLSD::Integer) override;
LLSD& ref(LLSD::Integer);
- virtual const LLSD& ref(LLSD::Integer) const;
+ const LLSD& ref(LLSD::Integer) const override;
DataVector& array() final override { return mData; }
const DataVector& array() const final override { return mData; }
- virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
+ void calcStats(S32 type_counts[], S32 share_counts[]) const override;
};
ImplArray& ImplArray::makeArray(Impl*& var)
@@ -629,7 +631,7 @@ namespace
while (iter != endArray())
{ // Add values for all items held in the array
Impl::calcStats((*iter), type_counts, share_counts);
- iter++;
+ ++iter;
}
// Add in the values for this array
@@ -701,7 +703,7 @@ void LLSD::Impl::assign(Impl*& var, const Impl* other)
void LLSD::Impl::assignUndefined(Impl*& var)
{
- reset(var, 0);
+ reset(var, nullptr);
}
void LLSD::Impl::assign(Impl*& var, LLSD::Boolean v)
@@ -777,7 +779,7 @@ void LLSD::Impl::calcStats(S32 type_counts[], S32 share_counts[]) const
S32 tp = S32(type());
if (0 <= tp && tp < LLSD::TypeLLSDNumTypes)
{
- type_counts[tp]++;
+ type_counts[tp]++;
if (shared())
{
share_counts[tp]++;
@@ -811,10 +813,10 @@ namespace
}
-LLSD::LLSD() : impl(0) { ALLOC_LLSD_OBJECT; }
-LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::reset(impl, 0); }
+LLSD::LLSD() : impl(nullptr) { ALLOC_LLSD_OBJECT; }
+LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::reset(impl, nullptr); }
-LLSD::LLSD(const LLSD& other) : impl(0) { ALLOC_LLSD_OBJECT; assign(other); }
+LLSD::LLSD(const LLSD& other) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(other); }
void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); }
@@ -823,17 +825,17 @@ void LLSD::clear() { Impl::assignUndefined(impl); }
LLSD::Type LLSD::type() const { return safe(impl).type(); }
// Scalar Constructors
-LLSD::LLSD(Boolean v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
-LLSD::LLSD(Integer v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
-LLSD::LLSD(Real v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
-LLSD::LLSD(const UUID& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
-LLSD::LLSD(const String& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
-LLSD::LLSD(const Date& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
-LLSD::LLSD(const URI& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
-LLSD::LLSD(const Binary& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(Boolean v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(Integer v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(Real v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const UUID& v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const String& v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const Date& v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const URI& v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const Binary& v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
// Convenience Constructors
-LLSD::LLSD(F32 v) : impl(0) { ALLOC_LLSD_OBJECT; assign((Real)v); }
+LLSD::LLSD(F32 v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign((Real)v); }
// Scalar Assignment
void LLSD::assign(Boolean v) { safe(impl).assign(impl, v); }
@@ -858,7 +860,7 @@ const LLSD::Binary& LLSD::asBinary() const { return safe(impl).asBinary(); }
const LLSD::String& LLSD::asStringRef() const { return safe(impl).asStringRef(); }
// const char * helpers
-LLSD::LLSD(const char* v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const char* v) : impl(nullptr) { ALLOC_LLSD_OBJECT; assign(v); }
void LLSD::assign(const char* v)
{
if(v) assign(std::string(v));
@@ -925,7 +927,7 @@ static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
// sStorage will point to the result of the last call. This will actually
// be one leak, but since this is used only when running under the
// debugger, it should not be an issue.
- static char *sStorage = NULL;
+ static char *sStorage = nullptr;
delete[] sStorage;
std::string out_string;
{
diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h
index e6c34e32b..1d8950bfc 100644
--- a/indra/llcommon/llsd.h
+++ b/indra/llcommon/llsd.h
@@ -389,9 +389,9 @@ public:
using an arbitrary pointer or scalar type to std::string.
*/
//@{
- LLSD(const void*); ///< construct from aribrary pointers
- void assign(const void*); ///< assign from arbitrary pointers
- LLSD& operator=(const void*); ///< assign from arbitrary pointers
+ LLSD(const void*) = delete; ///< construct from aribrary pointers
+ void assign(const void*) = delete; ///< assign from arbitrary pointers
+ LLSD& operator=(const void*) = delete; ///< assign from arbitrary pointers
bool has(Integer) const; ///< has() only works for Maps
//@}
diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp
index db89b293e..a31128dd8 100644
--- a/indra/llcommon/llsdjson.cpp
+++ b/indra/llcommon/llsdjson.cpp
@@ -79,6 +79,17 @@ LLSD LlsdFromJson(const nlohmann::json &val)
return result;
}
+LLSD LlsdFromJsonString(const std::string& str)
+{
+ auto json = nlohmann::json::parse(str, nullptr, false);
+ if (json.is_discarded())
+ {
+ LL_WARNS() << "Cannot parse invalid json string:\n" << str << LL_ENDL;
+ return LLSD();
+ }
+ return LlsdFromJson(json);
+}
+
//=========================================================================
nlohmann::json LlsdToJson(const LLSD &val)
{
diff --git a/indra/llcommon/llsdjson.h b/indra/llcommon/llsdjson.h
index bc76acbc8..5c45f697e 100644
--- a/indra/llcommon/llsdjson.h
+++ b/indra/llcommon/llsdjson.h
@@ -54,6 +54,7 @@
/// For maps and arrays child entries will be converted and added to the structure.
/// Order is preserved for an array but not for objects.
LLSD LlsdFromJson(const nlohmann::json &val);
+LLSD LlsdFromJsonString(const std::string& body);
/// Convert an LLSD object into Parsed JSON object maintaining member names and
/// array indexs.
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index f8690f63b..3f85810ee 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -118,7 +118,7 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
fail_if_not_legacy = true;
}
- if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
+ if (!strnicmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
{
legacy_no_header = true;
inbuf = (int)str.gcount();
@@ -141,9 +141,8 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
}
header = hdr_buf;
- std::string::size_type start = std::string::npos;
+ std::string::size_type start = header.find_first_not_of(" ");
std::string::size_type end = std::string::npos;
- start = header.find_first_not_of(" ");
if (start != std::string::npos)
{
end = header.find_first_of(" ?", start);
@@ -1304,8 +1303,8 @@ S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32
}
bool need_comma = false;
- LLSD::map_const_iterator iter = data.beginMap();
- LLSD::map_const_iterator end = data.endMap();
+ auto iter = data.beginMap();
+ auto end = data.endMap();
for(; iter != end; ++iter)
{
if(need_comma) ostr << ",";
@@ -1390,22 +1389,22 @@ S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32
// *FIX: memory inefficient.
const std::vector& buffer = data.asBinary();
ostr << "b(" << buffer.size() << ")\"";
- if(buffer.size())
+ if(!buffer.empty())
{
if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY)
{
std::ios_base::fmtflags old_flags = ostr.flags();
ostr.setf( std::ios::hex, std::ios::basefield );
ostr << "0x";
- for (size_t i = 0; i < buffer.size(); i++)
+ for (unsigned char i : buffer)
{
- ostr << (int) buffer[i];
+ ostr << static_cast(i);
}
ostr.flags(old_flags);
}
else
{
- ostr.write((const char*)&buffer[0], buffer.size());
+ ostr.write(reinterpret_cast(&buffer[0]), buffer.size());
}
}
ostr << "\"";
@@ -1442,9 +1441,9 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option
{
ostr.put('{');
U32 size_nbo = htonl(data.size());
- ostr.write((const char*)(&size_nbo), sizeof(U32));
- LLSD::map_const_iterator iter = data.beginMap();
- LLSD::map_const_iterator end = data.endMap();
+ ostr.write(reinterpret_cast(&size_nbo), sizeof(U32));
+ auto iter = data.beginMap();
+ auto end = data.endMap();
for(; iter != end; ++iter)
{
ostr.put('k');
@@ -1459,7 +1458,7 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option
{
ostr.put('[');
U32 size_nbo = htonl(data.size());
- ostr.write((const char*)(&size_nbo), sizeof(U32));
+ ostr.write(reinterpret_cast(&size_nbo), sizeof(U32));
for (const auto& entry : data.array())
{
format_count += format(entry, ostr);
@@ -1525,7 +1524,7 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option
const std::vector& buffer = data.asBinary();
U32 size_nbo = htonl(buffer.size());
ostr.write((const char*)(&size_nbo), sizeof(U32));
- if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ if(!buffer.empty()) ostr.write((const char*)&buffer[0], buffer.size());
break;
}
@@ -1702,12 +1701,12 @@ int deserialize_string_raw(
// *FIX: This is memory inefficient.
S32 len = strtol(buf + 1, NULL, 0);
if((max_bytes>0)&&(len>max_bytes)) return LLSDParser::PARSE_FAILURE;
- std::vector buf;
+ std::vector buf2;
if(len)
{
- buf.resize(len);
- count += (int)fullread(istr, (char *)&buf[0], len);
- value.assign(buf.begin(), buf.end());
+ buf2.resize(len);
+ count += (int)fullread(istr, (char *)&buf2[0], len);
+ value.assign(buf2.begin(), buf2.end());
}
c = istr.get();
++count;
@@ -2091,7 +2090,18 @@ std::string zip_llsd(LLSD& data)
}
have = CHUNK-strm.avail_out;
- output = (U8*) realloc(output, cur_size+have);
+ U8* new_output = (U8*) realloc(output, cur_size+have);
+ if (new_output == NULL)
+ {
+ LL_WARNS() << "Failed to compress LLSD block: can't reallocate memory, current size: " << cur_size << " bytes; requested " << cur_size + have << " bytes." << LL_ENDL;
+ deflateEnd(&strm);
+ if (output)
+ {
+ free(output);
+ }
+ return std::string();
+ }
+ output = new_output;
memcpy(output+cur_size, out, have);
cur_size += have;
}
@@ -2110,15 +2120,6 @@ std::string zip_llsd(LLSD& data)
deflateEnd(&strm);
free(output);
-#if 0 //verify results work with unzip_llsd
- std::istringstream test(result);
- LLSD test_sd;
- if (!unzip_llsd(test_sd, test, result.size()))
- {
- LL_ERRS() << "Invalid compression result!" << LL_ENDL;
- }
-#endif
-
return result;
}
@@ -2169,7 +2170,19 @@ bool unzip_llsd(LLSD& data, std::istream& is, S32 size)
U32 have = CHUNK-strm.avail_out;
- result = (U8*) realloc(result, cur_size + have);
+ U8* new_result = (U8*)realloc(result, cur_size + have);
+ if (new_result == NULL)
+ {
+ LL_WARNS() << "Failed to unzip LLSD block: can't reallocate memory, current size: " << cur_size << " bytes; requested " << cur_size + have << " bytes." << LL_ENDL;
+ inflateEnd(&strm);
+ if (result)
+ {
+ free(result);
+ }
+ delete in;
+ return false;
+ }
+ result = new_result;
memcpy(result+cur_size, out, have);
cur_size += have;
@@ -2215,6 +2228,11 @@ bool unzip_llsd(LLSD& data, std::istream& is, S32 size)
//and trailers are different for the formats.
U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32 size )
{
+ if (size == 0)
+ {
+ LL_WARNS() << "No data to unzip." << LL_ENDL;
+ return NULL;
+ }
U8* result = NULL;
U32 cur_size = 0;
z_stream strm;
@@ -2254,7 +2272,23 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32
}
U32 have = CHUNK-strm.avail_out;
- result = (U8*) realloc(result, cur_size + have);
+ U8* new_result = (U8*) realloc(result, cur_size + have);
+ if (new_result == NULL)
+ {
+ LL_WARNS() << "Failed to unzip LLSD NavMesh block: can't reallocate memory, current size: " << cur_size
+ << " bytes; requested " << cur_size + have
+ << " bytes; total syze: ." << size << " bytes."
+ << LL_ENDL;
+ inflateEnd(&strm);
+ if (result)
+ {
+ free(result);
+ }
+ delete [] in;
+ valid = false;
+ return NULL;
+ }
+ result = new_result;
memcpy(result+cur_size, out, have);
cur_size += have;
diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp
index 86992aee6..e14ae9027 100644
--- a/indra/llinventory/llinventory.cpp
+++ b/indra/llinventory/llinventory.cpp
@@ -1165,8 +1165,10 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
// mType, these are the two asset types that are IT_WEARABLE:
static U32 AT_BODYPART = 13; // LLAssetType::AT_BODYPART
// Viewer local values:
- static U32 WT_UNKNOWN = 16; // LLWearableType::WT_UNKNOWN
- static U32 WT_COUNT = 17; // LLWearableType::WT_COUNT
+ static U32 WT_UNKNOWN = 17; // LLWearableType::WT_UNKNOWN
+ static U32 WT_COUNT = 18; // LLWearableType::WT_COUNT
+ // Keep WT_UNKNOWN and WT_COUNT
+ // in sync with llwearabletype.h
// The last 8 bits of mFlags contain the wearable type.
static U32 II_FLAGS_SUBTYPE_MASK = 0xff; // LLInventoryItemFlags::II_FLAGS_SUBTYPE_MASK
diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp
index a4aaae365..158c2edc5 100644
--- a/indra/llinventory/llparcel.cpp
+++ b/indra/llinventory/llparcel.cpp
@@ -40,6 +40,8 @@
#include "llsdutil_math.h"
#include "message.h"
#include "u64.h"
+#include "llregionflags.h"
+#include
static const F32 SOME_BIG_NUMBER = 1000.0f;
static const F32 SOME_BIG_NEG_NUMBER = -1000.0f;
@@ -742,8 +744,8 @@ void LLParcel::unpackMessage(LLMessageSystem* msg)
void LLParcel::packAccessEntries(LLMessageSystem* msg,
const std::map& list)
{
- access_map_const_iterator cit = list.begin();
- access_map_const_iterator end = list.end();
+ LLAccessEntry::map::const_iterator cit = list.begin();
+ LLAccessEntry::map::const_iterator end = list.end();
if (cit == end)
{
@@ -794,9 +796,28 @@ void LLParcel::unpackAccessEntries(LLMessageSystem* msg,
}
+void LLParcel::unpackExperienceEntries(LLMessageSystem* msg, U32 type)
+{
+ LLUUID id;
+
+ S32 i;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_List);
+ for (i = 0; i < count; i++)
+ {
+ msg->getUUIDFast(_PREHASH_List, _PREHASH_ID, id, i);
+
+ if (id.notNull())
+ {
+ mExperienceKeys[id] = type;
+ }
+ }
+}
+
+
+
void LLParcel::expirePasses(S32 now)
{
- access_map_iterator itor = mAccessList.begin();
+ LLAccessEntry::map::iterator itor = mAccessList.begin();
while (itor != mAccessList.end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -886,7 +907,7 @@ BOOL LLParcel::addToAccessList(const LLUUID& agent_id, S32 time)
// Can't add owner to these lists
return FALSE;
}
- access_map_iterator itor = mAccessList.begin();
+ LLAccessEntry::map::iterator itor = mAccessList.begin();
while (itor != mAccessList.end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -931,7 +952,7 @@ BOOL LLParcel::addToBanList(const LLUUID& agent_id, S32 time)
return FALSE;
}
- access_map_iterator itor = mBanList.begin();
+ LLAccessEntry::map::iterator itor = mBanList.begin();
while (itor != mBanList.end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -970,7 +991,7 @@ BOOL remove_from_access_array(std::map* list,
const LLUUID& agent_id)
{
BOOL removed = FALSE;
- access_map_iterator itor = list->begin();
+ LLAccessEntry::map::iterator itor = list->begin();
while (itor != list->end())
{
const LLAccessEntry& entry = (*itor).second;
@@ -1091,7 +1112,7 @@ void LLParcel::startSale(const LLUUID& buyer_id, BOOL is_buyer_group)
mSaleTimerExpires.start();
mSaleTimerExpires.setTimerExpirySec(U64Microseconds(DEFAULT_USEC_SALE_TIMEOUT));
mStatus = OS_LEASE_PENDING;
- mClaimDate = time(NULL);
+ mClaimDate = time(nullptr);
setAuctionID(0);
// clear the autoreturn whenever land changes hands
setCleanOtherTime(0);
@@ -1313,3 +1334,58 @@ LLParcel::ECategory category_ui_string_to_category(const std::string& s)
// is a distinct option from "None" and "Other"
return LLParcel::C_ANY;
}
+
+LLAccessEntry::map LLParcel::getExperienceKeysByType(U32 type) const
+{
+ LLAccessEntry::map access;
+ LLAccessEntry entry;
+ xp_type_map_t::const_iterator it = mExperienceKeys.begin();
+ for(/**/; it != mExperienceKeys.end(); ++it)
+ {
+ if(it->second == type)
+ {
+ entry.mID = it->first;
+ access[entry.mID] = entry;
+ }
+ }
+ return access;
+}
+
+void LLParcel::clearExperienceKeysByType(U32 type)
+{
+ xp_type_map_t::iterator it = mExperienceKeys.begin();
+ while(it != mExperienceKeys.end())
+ {
+ if(it->second == type)
+ {
+ mExperienceKeys.erase(it++);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+void LLParcel::setExperienceKeyType(const LLUUID& experience_key, U32 type)
+{
+ if (type == EXPERIENCE_KEY_TYPE_NONE)
+ {
+ mExperienceKeys.erase(experience_key);
+ }
+ else
+ {
+ if (countExperienceKeyType(type) < PARCEL_MAX_EXPERIENCE_LIST)
+ {
+ mExperienceKeys[experience_key] = type;
+ }
+ }
+}
+
+U32 LLParcel::countExperienceKeyType(U32 type)
+{
+ return std::count_if(
+ boost::begin(mExperienceKeys | boost::adaptors::map_values),
+ boost::end(mExperienceKeys | boost::adaptors::map_values),
+ std::bind2nd(std::equal_to(), type));
+}
diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h
index e91f9a320..c1322c387 100644
--- a/indra/llinventory/llparcel.h
+++ b/indra/llinventory/llparcel.h
@@ -60,6 +60,9 @@ const S32 PARCEL_MAX_ACCESS_LIST = 300;
//for access/ban lists.
const F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f;
+// Maximum number of experiences
+const S32 PARCEL_MAX_EXPERIENCE_LIST = 24;
+
// Weekly charge for listing a parcel in the directory
const S32 PARCEL_DIRECTORY_FEE = 30;
@@ -141,9 +144,11 @@ class LLSD;
class LLAccessEntry
{
public:
+
+ typedef std::map map;
+
LLAccessEntry()
- : mID(),
- mTime(0),
+ : mTime(0),
mFlags(0)
{}
@@ -152,8 +157,6 @@ public:
U32 mFlags; // Not used - currently should always be zero
};
-typedef std::map::iterator access_map_iterator;
-typedef std::map::const_iterator access_map_const_iterator;
class LLParcel
{
@@ -331,6 +334,9 @@ public:
void unpackAccessEntries(LLMessageSystem* msg,
std::map* list);
+ void unpackExperienceEntries(LLMessageSystem* msg, U32 type);
+
+
void setAABBMin(const LLVector3& min) { mAABBMin = min; }
void setAABBMax(const LLVector3& max) { mAABBMax = max; }
@@ -707,6 +713,17 @@ public:
std::map mTempBanList;
std::map mTempAccessList;
+ typedef std::map xp_type_map_t;
+
+ void setExperienceKeyType(const LLUUID& experience_key, U32 type);
+ U32 countExperienceKeyType(U32 type);
+ U32 getExperienceKeyType(const LLUUID& experience_key)const;
+ LLAccessEntry::map getExperienceKeysByType(U32 type)const;
+ void clearExperienceKeysByType(U32 type);
+
+private:
+ xp_type_map_t mExperienceKeys;
+
};
diff --git a/indra/llinventory/llparcelflags.h b/indra/llinventory/llparcelflags.h
index 9d4884e32..fbfc2af1d 100644
--- a/indra/llinventory/llparcelflags.h
+++ b/indra/llinventory/llparcelflags.h
@@ -1,31 +1,25 @@
/**
* @file llparcelflags.h
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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$
*/
@@ -97,8 +91,10 @@ const U32 PF_DEFAULT = PF_ALLOW_FLY
| PF_USE_ESTATE_VOICE_CHAN;
// Access list flags
-const U32 AL_ACCESS = (1 << 0);
-const U32 AL_BAN = (1 << 1);
+const U32 AL_ACCESS = (1 << 0);
+const U32 AL_BAN = (1 << 1);
+const U32 AL_ALLOW_EXPERIENCE = (1 << 3);
+const U32 AL_BLOCK_EXPERIENCE = (1 << 4);
//const U32 AL_RENTER = (1 << 2);
// Block access return values. BA_ALLOWED is the only success case
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
index 2f843490d..67a7c997e 100644
--- a/indra/llmath/lloctree.h
+++ b/indra/llmath/lloctree.h
@@ -704,7 +704,7 @@ public:
#endif
}
- notifyRemoval(data);
+ this->notifyRemoval(data);
checkAlive();
}
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index c131b436c..b5f5bafc1 100644
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -45,6 +45,7 @@ set(llmessage_SOURCE_FILES
llclassifiedflags.cpp
lldatapacker.cpp
lldispatcher.cpp
+ llexperiencecache.cpp
llfiltersd2xmlrpc.cpp
llhost.cpp
llhttpclient.cpp
@@ -133,11 +134,13 @@ set(llmessage_HEADER_FILES
llcipher.h
llcircuit.h
llclassifiedflags.h
+ llcororesponder.h
llcurl.h
lldatapacker.h
lldbstrings.h
lldispatcher.h
lleventflags.h
+ llexperiencecache.h
llfiltersd2xmlrpc.h
llfollowcamparams.h
llhost.h
@@ -227,7 +230,8 @@ target_link_libraries(
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${XMLRPCEPI_LIBRARIES}
- )
+ ${PTHREAD_LIBRARY}
+)
# tests
if (LL_TESTS)
diff --git a/indra/llmessage/llavatarname.cpp b/indra/llmessage/llavatarname.cpp
index fdcfed161..a02d7c012 100644
--- a/indra/llmessage/llavatarname.cpp
+++ b/indra/llmessage/llavatarname.cpp
@@ -139,7 +139,7 @@ void LLAvatarName::fromString(const std::string& full_name)
mLegacyLastName = full_name.substr(index+1);
if (mLegacyLastName != "Resident")
{
- mUsername = mLegacyFirstName + "." + mLegacyLastName;
+ mUsername = mLegacyFirstName + '.' + mLegacyLastName;
mDisplayName = full_name;
LLStringUtil::toLower(mUsername);
}
@@ -184,7 +184,7 @@ std::string LLAvatarName::getCompleteName(bool linefeed) const
name = mDisplayName;
if (sUseUsernames)
{
- name += (linefeed ? "\n(" : " (") + mUsername + ")";
+ name += (linefeed ? "\n(" : " (") + mUsername + ')';
}
}
}
@@ -238,7 +238,7 @@ std::string LLAvatarName::getUserName() const
}
else
{
- name = mLegacyFirstName + " " + mLegacyLastName;
+ name = mLegacyFirstName + ' ' + mLegacyLastName;
}
return name;
}
diff --git a/indra/llmessage/llavatarname.h b/indra/llmessage/llavatarname.h
index 5902d34d8..4f0df57cc 100644
--- a/indra/llmessage/llavatarname.h
+++ b/indra/llmessage/llavatarname.h
@@ -94,7 +94,7 @@ public:
{
case 1 : return getCompleteName();
case 2 : return getDisplayName();
- case 3 : return getLegacyName() + (mIsDisplayNameDefault ? "" : " (" + mDisplayName + ")"); break;
+ case 3 : return getLegacyName() + (mIsDisplayNameDefault ? "" : " (" + mDisplayName + ')'); break;
default : return getLegacyName();
}
}
diff --git a/indra/llmessage/llcororesponder.h b/indra/llmessage/llcororesponder.h
new file mode 100644
index 000000000..bacec85a8
--- /dev/null
+++ b/indra/llmessage/llcororesponder.h
@@ -0,0 +1,63 @@
+/**
+ * @file llcororesponder.h
+ * @brief A responder purposed to call coro functions, to ease transition to LLCoro
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ *
+ * Copyright (C) 2020, Liru Færs
+ *
+ * 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
+#include "llhttpclient.h"
+
+struct LLCoroResponderBase : public LLHTTPClient::ResponderWithCompleted
+{
+ const AIHTTPReceivedHeaders& getHeaders() const { return mReceivedHeaders; }
+ const LLSD& getContent() const { return mContent; }
+
+ char const* getName() const override final { return "LLCoroResponder"; }
+protected:
+ LLCoroResponderBase() {}
+};
+
+struct LLCoroResponder final : public LLCoroResponderBase
+{
+ typedef std::function cb_t;
+ LLCoroResponder(const cb_t& cb) : mCB(cb) {}
+ void httpCompleted() override { mCB(*this); }
+private:
+ const cb_t mCB;
+};
+
+struct LLCoroResponderRaw final : public LLCoroResponderBase
+{
+ typedef std::function cb_t;
+ LLCoroResponderRaw(const cb_t& cb) : mCB(cb) {}
+ void completedRaw(const LLChannelDescriptors& channels, const buffer_ptr_t& buffer) override
+ {
+ std::string content;
+ decode_raw_body(channels, buffer, content);
+ mCB(*this, content);
+ }
+private:
+ const cb_t mCB;
+};
+
diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
new file mode 100644
index 000000000..482b87091
--- /dev/null
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -0,0 +1,921 @@
+/**
+ * @file llexperiencecache.cpp
+ * @brief llexperiencecache and related class definitions
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, 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 "llexperiencecache.h"
+
+#include "llavatarname.h"
+#include "llcororesponder.h"
+#include "llsdserialize.h"
+#include "lleventfilter.h"
+#include "lldir.h"
+#include
+#include
+#include
+#include
+#include
+
+//=========================================================================
+namespace LLExperienceCacheImpl
+{
+ void mapKeys(const LLSD& legacyKeys);
+ F64 getErrorRetryDeltaTime(S32 status, const AIHTTPReceivedHeaders& headers);
+ bool maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age);
+
+ static const std::string PRIVATE_KEY = "private_id";
+ static const std::string EXPERIENCE_ID = "public_id";
+
+ static const std::string MAX_AGE("max-age");
+ static const boost::char_separator EQUALS_SEPARATOR("=");
+ static const boost::char_separator COMMA_SEPARATOR(",");
+
+ // *TODO$: this seems to be tied to mapKeys which is used by bootstrap.... but I don't think that bootstrap is used.
+ typedef std::map KeyMap;
+ KeyMap privateToPublicKeyMap;
+}
+
+//=========================================================================
+const std::string LLExperienceCache::PRIVATE_KEY = "private_id";
+const std::string LLExperienceCache::MISSING = "DoesNotExist";
+
+const std::string LLExperienceCache::AGENT_ID = "agent_id";
+const std::string LLExperienceCache::GROUP_ID = "group_id";
+const std::string LLExperienceCache::EXPERIENCE_ID = "public_id";
+const std::string LLExperienceCache::NAME = "name";
+const std::string LLExperienceCache::PROPERTIES = "properties";
+const std::string LLExperienceCache::EXPIRES = "expiration";
+const std::string LLExperienceCache::DESCRIPTION = "description";
+const std::string LLExperienceCache::QUOTA = "quota";
+const std::string LLExperienceCache::MATURITY = "maturity";
+const std::string LLExperienceCache::METADATA = "extended_metadata";
+const std::string LLExperienceCache::SLURL = "slurl";
+
+// should be in sync with experience-api/experiences/models.py
+const int LLExperienceCache::PROPERTY_INVALID = 1 << 0;
+const int LLExperienceCache::PROPERTY_PRIVILEGED = 1 << 3;
+const int LLExperienceCache::PROPERTY_GRID = 1 << 4;
+const int LLExperienceCache::PROPERTY_PRIVATE = 1 << 5;
+const int LLExperienceCache::PROPERTY_DISABLED = 1 << 6;
+const int LLExperienceCache::PROPERTY_SUSPENDED = 1 << 7;
+
+// default values
+const F64 LLExperienceCache::DEFAULT_EXPIRATION = 600.0;
+const S32 LLExperienceCache::DEFAULT_QUOTA = 128; // this is megabytes
+const int LLExperienceCache::SEARCH_PAGE_SIZE = 30;
+
+//=========================================================================
+LLExperienceCache::LLExperienceCache():
+ mShutdown(false)
+{
+}
+
+LLExperienceCache::~LLExperienceCache()
+{
+
+}
+
+void LLExperienceCache::initSingleton()
+{
+ mCacheFileName = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml");
+
+ LL_INFOS("ExperienceCache") << "Loading " << mCacheFileName << LL_ENDL;
+ llifstream cache_stream(mCacheFileName.c_str());
+
+ if (cache_stream.is_open())
+ {
+ cache_stream >> (*this);
+ }
+}
+
+void LLExperienceCache::cleanup()
+{
+ LL_INFOS("ExperienceCache") << "Saving " << mCacheFileName << LL_ENDL;
+
+ llofstream cache_stream(mCacheFileName.c_str());
+ if (cache_stream.is_open())
+ {
+ cache_stream << (*this);
+ }
+ mShutdown = true;
+}
+
+//-------------------------------------------------------------------------
+void LLExperienceCache::importFile(std::istream& istr)
+{
+ LLSD data;
+ S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
+ if (parse_count < 1) return;
+
+ LLSD experiences = data["experiences"];
+
+ LLUUID public_key;
+ for (const auto& it : experiences.map())
+ {
+ public_key.set(it.first);
+ mCache[public_key] = it.second;
+ }
+
+ LL_DEBUGS("ExperienceCache") << "importFile() loaded " << mCache.size() << LL_ENDL;
+}
+
+void LLExperienceCache::exportFile(std::ostream& ostr) const
+{
+ LLSD experiences;
+
+ cache_t::const_iterator it = mCache.begin();
+ for (; it != mCache.end(); ++it)
+ {
+ if (!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() ||
+ it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID))
+ continue;
+
+ experiences[it->first.asString()] = it->second;
+ }
+
+ LLSD data;
+ data["experiences"] = experiences;
+
+ LLSDSerialize::toPrettyXML(data, ostr);
+}
+
+// *TODO$: Rider: This method does not seem to be used... it may be useful in testing.
+void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration)
+{
+ LLExperienceCacheImpl::mapKeys(legacyKeys);
+ for (auto experience : legacyKeys.array())
+ {
+ if (experience.has(EXPERIENCE_ID))
+ {
+ if (!experience.has(EXPIRES))
+ {
+ experience[EXPIRES] = initialExpiration;
+ }
+ processExperience(experience[EXPERIENCE_ID].asUUID(), experience);
+ }
+ else
+ {
+ LL_WARNS("ExperienceCache")
+ << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID
+ << LL_ENDL;
+ }
+ }
+}
+
+LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found)
+{
+ if (private_key.isNull())
+ return LLUUID::null;
+
+ LLExperienceCacheImpl::KeyMap::const_iterator it = LLExperienceCacheImpl::privateToPublicKeyMap.find(private_key);
+ if (it == LLExperienceCacheImpl::privateToPublicKeyMap.end())
+ {
+ if (null_if_not_found)
+ {
+ return LLUUID::null;
+ }
+ return private_key;
+ }
+ LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL;
+ return it->second;
+}
+
+//=========================================================================
+void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD& experience)
+{
+ LL_INFOS("ExperienceCache") << "Processing experience \"" << experience[NAME] << "\" with key " << public_key.asString() << LL_ENDL;
+
+ mCache[public_key]=experience;
+ LLSD & row = mCache[public_key];
+
+ if(row.has(EXPIRES))
+ {
+ row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds();
+ }
+
+ if(row.has(EXPERIENCE_ID))
+ {
+ mPendingQueue.erase(row[EXPERIENCE_ID].asUUID());
+ }
+
+ //signal
+ signal_map_t::iterator sig_it = mSignalMap.find(public_key);
+ if (sig_it != mSignalMap.end())
+ {
+ signal_ptr signal = sig_it->second;
+ (*signal)(experience);
+
+ mSignalMap.erase(public_key);
+ }
+}
+
+const LLExperienceCache::cache_t& LLExperienceCache::getCached()
+{
+ return mCache;
+}
+
+void LLExperienceCache::requestExperiencesCoro(const LLCoroResponder& responder, RequestQueue_t requests)
+{
+ //LL_INFOS("requestExperiencesCoro") << "url: " << url << LL_ENDL;
+
+ LLSD result = responder.getContent();
+ auto status = responder.getStatus();
+
+ if (!responder.isGoodStatus(status))
+ {
+ F64 now = LLFrameTimer::getTotalSeconds();
+
+ auto headers = responder.getHeaders();
+ // build dummy entries for the failed requests
+ for (auto request : requests)
+ {
+ LLSD exp = get(request);
+ //leave the properties alone if we already have a cache entry for this xp
+ if (exp.isUndefined())
+ {
+ exp[PROPERTIES] = PROPERTY_INVALID;
+ }
+ exp[EXPIRES] = now + LLExperienceCacheImpl::getErrorRetryDeltaTime(status, headers);
+ exp[EXPERIENCE_ID] = request;
+ exp["key_type"] = EXPERIENCE_ID;
+ exp["uuid"] = request;
+ exp["error"] = status;
+ exp[QUOTA] = DEFAULT_QUOTA;
+
+ processExperience(request, exp);
+ }
+ return;
+ }
+
+ LLSD experiences = result["experience_keys"];
+
+ for (const auto& row : experiences.array())
+ {
+ LLUUID public_key = row[EXPERIENCE_ID].asUUID();
+
+ LL_DEBUGS("ExperienceCache") << "Received result for " << public_key
+ << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL;
+
+ processExperience(public_key, row);
+ }
+
+ LLSD error_ids = result["error_ids"];
+
+ for (const auto& err : error_ids.array())
+ {
+ LLUUID id = err.asUUID();
+ LLSD exp;
+ exp[EXPIRES] = DEFAULT_EXPIRATION;
+ exp[EXPERIENCE_ID] = id;
+ exp[PROPERTIES] = PROPERTY_INVALID;
+ exp[MISSING] = true;
+ exp[QUOTA] = DEFAULT_QUOTA;
+
+ processExperience(id, exp);
+ LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL;
+ }
+
+}
+
+void LLExperienceCache::requestExperiences()
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ std::string urlBase = mCapability("GetExperienceInfo");
+ if (urlBase.empty())
+ {
+ LL_DEBUGS("ExperienceCache") << "No Experience capability." << LL_ENDL;
+ return;
+ }
+
+ if (*urlBase.rbegin() != '/')
+ {
+ urlBase += "/";
+ }
+ urlBase += "id/";
+
+
+ F64 now = LLFrameTimer::getTotalSeconds();
+
+ const U32 EXP_URL_SEND_THRESHOLD = 3000;
+ constexpr U32 EXP_PAGE_SIZE = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH;
+
+ std::ostringstream ostr;
+ ostr << urlBase << "?page_size=" << EXP_PAGE_SIZE;
+ RequestQueue_t requests;
+
+ while (!mRequestQueue.empty())
+ {
+ RequestQueue_t::iterator it = mRequestQueue.begin();
+ LLUUID key = (*it);
+ mRequestQueue.erase(it);
+ requests.insert(key);
+
+ ostr << "&" << EXPERIENCE_ID << "=" << key.asString();
+ mPendingQueue[key] = now;
+
+ if (mRequestQueue.empty() || (ostr.tellp() > EXP_URL_SEND_THRESHOLD))
+ { // request is placed in the coprocedure pool for the ExpCache cache. Throttling is done by the pool itself.
+ LLHTTPClient::get(ostr.str(), new LLCoroResponder(
+ boost::bind(&LLExperienceCache::requestExperiencesCoro, this, _1, requests) ));
+
+ ostr.str(std::string());
+ ostr << urlBase << "?page_size=" << EXP_PAGE_SIZE;
+ requests.clear();
+ }
+ }
+
+}
+
+
+bool LLExperienceCache::isRequestPending(const LLUUID& public_key)
+{
+ bool isPending = false;
+ const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
+
+ PendingQueue_t::const_iterator it = mPendingQueue.find(public_key);
+
+ if(it != mPendingQueue.end())
+ {
+ F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS;
+ isPending = (it->second > expire_time);
+ }
+
+ return isPending;
+}
+
+void LLExperienceCache::setCapabilityQuery(LLExperienceCache::CapabilityQuery_t queryfn)
+{
+ mCapability = queryfn;
+}
+
+
+void LLExperienceCache::idleCoro()
+{
+ const F32 SECS_BETWEEN_REQUESTS = 0.5f;
+ const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
+
+ {
+ static LLFrameTimer sRequestTimer;
+ if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) return;
+
+ if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
+ {
+ eraseExpired();
+ }
+
+ if (!mRequestQueue.empty())
+ {
+ requestExperiences();
+ }
+ }
+
+ // The coroutine system will likely be shut down by the time we get to this point
+ // (or at least no further cycling will occur on it since the user has decided to quit.)
+}
+
+void LLExperienceCache::erase(const LLUUID& key)
+{
+ cache_t::iterator it = mCache.find(key);
+
+ if(it != mCache.end())
+ {
+ mCache.erase(it);
+ }
+}
+
+void LLExperienceCache::eraseExpired()
+{
+ F64 now = LLFrameTimer::getTotalSeconds();
+ cache_t::iterator it = mCache.begin();
+ while (it != mCache.end())
+ {
+ cache_t::iterator cur = it;
+ LLSD& exp = cur->second;
+ ++it;
+
+ //LL_INFOS("ExperienceCache") << "Testing experience \"" << exp[NAME] << "\" with exp time " << exp[EXPIRES].asReal() << "(now = " << now << ")" << LL_ENDL;
+
+ if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now)
+ {
+ if(!exp.has(EXPERIENCE_ID))
+ {
+ LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ;
+ mCache.erase(cur);
+ }
+ else
+ {
+ LLUUID id = exp[EXPERIENCE_ID].asUUID();
+ LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null;
+ if(private_key.notNull() || !exp.has("DoesNotExist"))
+ {
+ fetch(id, true);
+ }
+ else
+ {
+ LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ;
+ mCache.erase(cur);
+ }
+ }
+ }
+ }
+}
+
+bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/)
+{
+ if(!key.isNull() && !isRequestPending(key) && (refresh || mCache.find(key)==mCache.end()))
+ {
+ LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL;
+
+ mRequestQueue.insert(key);
+ return true;
+ }
+ return false;
+}
+
+void LLExperienceCache::insert(const LLSD& experience_data)
+{
+ if(experience_data.has(EXPERIENCE_ID))
+ {
+ processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data);
+ }
+ else
+ {
+ LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL;
+ }
+}
+
+const LLSD& LLExperienceCache::get(const LLUUID& key)
+{
+ static const LLSD empty;
+
+ if(key.isNull())
+ return empty;
+ cache_t::const_iterator it = mCache.find(key);
+
+ if (it != mCache.end())
+ {
+ return it->second;
+ }
+ fetch(key);
+
+ return empty;
+}
+
+void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::ExperienceGetFn_t slot)
+{
+ if(key.isNull())
+ return;
+
+ cache_t::const_iterator it = mCache.find(key);
+ if (it != mCache.end())
+ {
+ // ...name already exists in cache, fire callback now
+ callback_signal_t signal;
+ signal.connect(slot);
+
+ signal(it->second);
+ return;
+ }
+
+ fetch(key);
+
+ signal_ptr signal = boost::make_shared();
+
+ std::pair result = mSignalMap.insert(signal_map_t::value_type(key, signal));
+ if (!result.second)
+ signal = (*result.first).second;
+ signal->connect(slot);
+}
+
+//=========================================================================
+void LLExperienceCache::fetchAssociatedExperience(const LLUUID& objectId, const LLUUID& itemId, std::string url, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+
+ if (url.empty())
+ {
+ url = mCapability("GetMetadata");
+
+ if (url.empty())
+ {
+ LL_WARNS("ExperienceCache") << "No Metadata capability." << LL_ENDL;
+ return;
+ }
+ }
+
+ LLSD fields;
+ fields.append("experience");
+ LLSD data;
+ data["object-id"] = objectId;
+ data["item-id"] = itemId;
+ data["fields"] = fields;
+
+ LLHTTPClient::post(url, data, new LLCoroResponder(
+ boost::bind(&LLExperienceCache::fetchAssociatedExperienceCoro, this, _1, fn)));
+}
+
+void LLExperienceCache::fetchAssociatedExperienceCoro(const LLCoroResponder& responder, ExperienceGetFn_t fn)
+{
+ LLSD result = responder.getContent();
+ auto status = responder.getStatus();
+
+ if (!responder.isGoodStatus(status) || !result.has("experience"))
+ {
+ LLSD failure;
+ if (!status)
+ {
+ failure["error"] = status;
+ failure["message"] = responder.getReason();
+ }
+ else
+ {
+ failure["error"] = -1;
+ failure["message"] = "no experience";
+ }
+ if (fn != nullptr)
+ fn(failure);
+ return;
+ }
+
+ LLUUID expId = result["experience"].asUUID();
+ get(expId, fn);
+}
+
+//-------------------------------------------------------------------------
+void LLExperienceCache::findExperienceByName(const std::string text, int page, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ std::ostringstream url;
+
+ url << mCapability("FindExperienceByName") << "?page=" << page << "&page_size=" << SEARCH_PAGE_SIZE << "&query=" << LLURI::escape(text);
+
+ LLHTTPClient::get(url.str(), new LLCoroResponder(
+ boost::bind(&LLExperienceCache::findExperienceByNameCoro, this, _1, fn)));
+}
+
+void LLExperienceCache::findExperienceByNameCoro(const LLCoroResponder& responder, ExperienceGetFn_t fn)
+{
+ LLSD result = responder.getContent();
+
+ if (!responder.isGoodStatus(responder.getStatus()))
+ {
+ fn(LLSD());
+ return;
+ }
+
+ const LLSD& experiences = result["experience_keys"];
+ for (const auto& it : experiences.array())
+ {
+ insert(it);
+ }
+
+ fn(result);
+}
+
+//-------------------------------------------------------------------------
+void LLExperienceCache::getGroupExperiences(const LLUUID &groupId, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ // search for experiences owned by the current group
+ std::string url = mCapability("GroupExperiences");
+ if (url.empty())
+ {
+ LL_WARNS("ExperienceCache") << "No Group Experiences capability" << LL_ENDL;
+ return;
+ }
+
+ url += "?" + groupId.asString();
+
+ LLHTTPClient::get(url, new LLCoroResponder(
+ boost::bind(&LLExperienceCache::getGroupExperiencesCoro, this, _1, fn)));
+}
+
+void LLExperienceCache::getGroupExperiencesCoro(const LLCoroResponder& responder, ExperienceGetFn_t fn)
+{
+ LLSD result = responder.getContent();
+
+ if (!responder.isGoodStatus(responder.getStatus()))
+ {
+ fn(LLSD());
+ return;
+ }
+
+ const LLSD& experienceIds = result["experience_ids"];
+ fn(experienceIds);
+}
+
+//-------------------------------------------------------------------------
+void LLExperienceCache::getRegionExperiences(CapabilityQuery_t regioncaps, ExperienceGetFn_t fn)
+{
+ regionExperiences(regioncaps, LLSD(), false, fn);
+}
+
+void LLExperienceCache::setRegionExperiences(CapabilityQuery_t regioncaps, const LLSD &experiences, ExperienceGetFn_t fn)
+{
+ regionExperiences(regioncaps, experiences, true, fn);
+}
+
+void LLExperienceCache::regionExperiences(CapabilityQuery_t regioncaps, const LLSD &experiences, bool update, ExperienceGetFn_t fn)
+{
+ // search for experiences owned by the current group
+ std::string url = regioncaps("RegionExperiences");
+ if (url.empty())
+ {
+ LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL;
+ return;
+ }
+
+ auto httpRequest = new LLCoroResponder(
+ boost::bind(&LLExperienceCache::regionExperiencesCoro, this, _1, fn));
+
+ LLSD result;
+ if (update)
+ LLHTTPClient::post(url, experiences, httpRequest);
+ else
+ LLHTTPClient::get(url, httpRequest);
+}
+
+void LLExperienceCache::regionExperiencesCoro(const LLCoroResponder& responder, ExperienceGetFn_t fn)
+{
+ LLSD result = responder.getContent();
+
+ if (!responder.isGoodStatus(responder.getStatus()))
+ {
+// fn(LLSD());
+ return;
+ }
+
+ fn(result);
+
+}
+
+//-------------------------------------------------------------------------
+void LLExperienceCache::getExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ std::string url = mCapability("ExperiencePreferences") + "?" + experienceId.asString();
+
+ LLHTTPClient::get(url, new LLCoroResponder(
+ boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, fn)));
+}
+
+void LLExperienceCache::setExperiencePermission(const LLUUID &experienceId, const std::string &permission, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ std::string url = mCapability("ExperiencePreferences");
+ if (url.empty())
+ return;
+ LLSD permData;
+ LLSD data;
+ permData["permission"] = permission;
+ data[experienceId.asString()] = permData;
+
+ LLHTTPClient::put(url, data, new LLCoroResponder(
+ boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, fn)));
+}
+
+void LLExperienceCache::forgetExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ std::string url = mCapability("ExperiencePreferences") + "?" + experienceId.asString();
+ LLHTTPClient::del(url, new LLCoroResponder(
+ boost::bind(&LLExperienceCache::experiencePermissionCoro, this, _1, fn)));
+}
+
+void LLExperienceCache::experiencePermissionCoro(const LLCoroResponder& responder, ExperienceGetFn_t fn)
+{
+ // search for experiences owned by the current group
+
+ LLSD result = responder.getContent();
+
+ if (responder.isGoodStatus(responder.getStatus()))
+ {
+ fn(result);
+ }
+}
+
+//-------------------------------------------------------------------------
+void LLExperienceCache::getExperienceAdmin(const LLUUID &experienceId, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ std::string url = mCapability("IsExperienceAdmin");
+ if (url.empty())
+ {
+ LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL;
+ return;
+ }
+ url += "?experience_id=" + experienceId.asString();
+
+ LLHTTPClient::get(url, new LLCoroResponder(
+ boost::bind(fn, boost::bind(&LLCoroResponder::getContent, _1))));
+}
+
+//-------------------------------------------------------------------------
+void LLExperienceCache::updateExperience(LLSD updateData, ExperienceGetFn_t fn)
+{
+ if (mCapability == nullptr)
+ {
+ LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+ return;
+ }
+
+ std::string url = mCapability("UpdateExperience");
+ if (url.empty())
+ {
+ LL_WARNS("ExperienceCache") << "No Region Experiences capability" << LL_ENDL;
+ return;
+ }
+
+ updateData.erase(LLExperienceCache::QUOTA);
+ updateData.erase(LLExperienceCache::EXPIRES);
+ updateData.erase(LLExperienceCache::AGENT_ID);
+
+ LLHTTPClient::post(url, updateData, new LLCoroResponder(
+ boost::bind(fn, boost::bind(&LLCoroResponder::getContent, _1))));
+}
+
+//=========================================================================
+void LLExperienceCacheImpl::mapKeys(const LLSD& legacyKeys)
+{
+ for (const auto& exp : legacyKeys.array())
+ {
+ if (exp.has(LLExperienceCacheImpl::EXPERIENCE_ID) && exp.has(LLExperienceCacheImpl::PRIVATE_KEY))
+ {
+ LLExperienceCacheImpl::privateToPublicKeyMap[exp[LLExperienceCacheImpl::PRIVATE_KEY].asUUID()] =
+ exp[LLExperienceCacheImpl::EXPERIENCE_ID].asUUID();
+ }
+ }
+}
+
+// Return time to retry a request that generated an error, based on
+// error type and headers. Return value is seconds-since-epoch.
+F64 LLExperienceCacheImpl::getErrorRetryDeltaTime(S32 status, const AIHTTPReceivedHeaders& headers)
+{
+ // Retry-After takes priority
+ std::string retry_afters;
+ if (headers.getFirstValue("retry-after", retry_afters))
+ {
+ LLSD retry_after(retry_afters);
+ // We only support the delta-seconds type
+ S32 delta_seconds = retry_after.asInteger();
+ if (delta_seconds > 0)
+ {
+ // ...valid delta-seconds
+ return F64(delta_seconds);
+ }
+ }
+
+ // If no Retry-After, look for Cache-Control max-age
+ // Allow the header to override the default
+ std::string cache_control;
+ if (headers.getFirstValue("cache-control", cache_control))
+ {
+ S32 max_age = 0;
+ if (LLExperienceCacheImpl::maxAgeFromCacheControl(cache_control, &max_age))
+ {
+ LL_WARNS("ExperienceCache")
+ << "got EXPIRES from headers, max_age " << max_age
+ << LL_ENDL;
+ return (F64)max_age;
+ }
+ }
+
+ // No information in header, make a guess
+ if (status == 503)
+ {
+ // ...service unavailable, retry soon
+ const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
+ return SERVICE_UNAVAILABLE_DELAY;
+ }
+ else if (status == 499)
+ {
+ // ...we were probably too busy, retry quickly
+ const F64 BUSY_DELAY = 10.0; // 10 seconds
+ return BUSY_DELAY;
+
+ }
+ else
+ {
+ // ...other unexpected error
+ const F64 DEFAULT_DELAY = 3600.0; // 1 hour
+ return DEFAULT_DELAY;
+ }
+}
+
+bool LLExperienceCacheImpl::maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age)
+{
+ // Split the string on "," to get a list of directives
+ typedef boost::tokenizer > tokenizer;
+ tokenizer directives(cache_control, COMMA_SEPARATOR);
+
+ tokenizer::iterator token_it = directives.begin();
+ for ( ; token_it != directives.end(); ++token_it)
+ {
+ // Tokens may have leading or trailing whitespace
+ std::string token = *token_it;
+ LLStringUtil::trim(token);
+
+ if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
+ {
+ // ...this token starts with max-age, so let's chop it up by "="
+ tokenizer subtokens(token, EQUALS_SEPARATOR);
+ tokenizer::iterator subtoken_it = subtokens.begin();
+
+ // Must have a token
+ if (subtoken_it == subtokens.end()) return false;
+ std::string subtoken = *subtoken_it;
+
+ // Must exactly equal "max-age"
+ LLStringUtil::trim(subtoken);
+ if (subtoken != MAX_AGE) return false;
+
+ // Must have another token
+ ++subtoken_it;
+ if (subtoken_it == subtokens.end()) return false;
+ subtoken = *subtoken_it;
+
+ // Must be a valid integer
+ // *NOTE: atoi() returns 0 for invalid values, so we have to
+ // check the string first.
+ // *TODO: Do servers ever send "0000" for zero? We don't handle it
+ LLStringUtil::trim(subtoken);
+ if (subtoken == "0")
+ {
+ *max_age = 0;
+ return true;
+ }
+ S32 val = atoi( subtoken.c_str() );
+ if (val > 0 && val < S32_MAX)
+ {
+ *max_age = val;
+ return true;
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+
+
+
diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h
new file mode 100644
index 000000000..cddf5f67e
--- /dev/null
+++ b/indra/llmessage/llexperiencecache.h
@@ -0,0 +1,181 @@
+/**
+ * @file llexperiencecache.h
+ * @brief Caches information relating to experience keys
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, 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_LLEXPERIENCECACHE_H
+#define LL_LLEXPERIENCECACHE_H
+
+#include "linden_common.h"
+#include "llsingleton.h"
+#include "llframetimer.h"
+#include "llsd.h"
+#include
+
+struct LLCoroResponder;
+class LLSD;
+class LLUUID;
+
+
+class LLExperienceCache final : public LLSingleton < LLExperienceCache >
+{
+ friend class LLSingleton;
+ LLExperienceCache();
+
+public:
+ typedef std::function CapabilityQuery_t;
+ typedef std::function ExperienceGetFn_t;
+
+ void idleCoro();
+ void setCapabilityQuery(CapabilityQuery_t queryfn);
+ void cleanup();
+
+ //-------------------------------------------
+ // Cache methods
+ void erase(const LLUUID& key);
+ bool fetch(const LLUUID& key, bool refresh = false);
+ void insert(const LLSD& experience_data);
+ const LLSD& get(const LLUUID& key);
+ void get(const LLUUID& key, ExperienceGetFn_t slot); // If name information is in cache, callback will be called immediately.
+
+ bool isRequestPending(const LLUUID& public_key);
+
+ //-------------------------------------------
+ void fetchAssociatedExperience(const LLUUID& objectId, const LLUUID& itemId, ExperienceGetFn_t fn) { fetchAssociatedExperience(objectId, itemId, LLStringUtil::null, fn); }
+ void fetchAssociatedExperience(const LLUUID& objectId, const LLUUID& itemId, std::string url, ExperienceGetFn_t fn);
+ void findExperienceByName(const std::string text, int page, ExperienceGetFn_t fn);
+ void getGroupExperiences(const LLUUID &groupId, ExperienceGetFn_t fn);
+
+ // the Get/Set Region Experiences take a CapabilityQuery to get the capability since
+ // the region being queried may not be the region that the agent is standing on.
+ void getRegionExperiences(CapabilityQuery_t regioncaps, ExperienceGetFn_t fn);
+ void setRegionExperiences(CapabilityQuery_t regioncaps, const LLSD &experiences, ExperienceGetFn_t fn);
+
+ void getExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn);
+ void setExperiencePermission(const LLUUID &experienceId, const std::string &permission, ExperienceGetFn_t fn);
+ void forgetExperiencePermission(const LLUUID &experienceId, ExperienceGetFn_t fn);
+
+ void getExperienceAdmin(const LLUUID &experienceId, ExperienceGetFn_t fn);
+
+ void updateExperience(LLSD updateData, ExperienceGetFn_t fn);
+ //-------------------------------------------
+ static const std::string NAME; // "name"
+ static const std::string EXPERIENCE_ID; // "public_id"
+ static const std::string AGENT_ID; // "agent_id"
+ static const std::string GROUP_ID; // "group_id"
+ static const std::string PROPERTIES; // "properties"
+ static const std::string EXPIRES; // "expiration"
+ static const std::string DESCRIPTION; // "description"
+ static const std::string QUOTA; // "quota"
+ static const std::string MATURITY; // "maturity"
+ static const std::string METADATA; // "extended_metadata"
+ static const std::string SLURL; // "slurl"
+
+ static const std::string MISSING; // "DoesNotExist"
+
+ // should be in sync with experience-api/experiences/models.py
+ static const int PROPERTY_INVALID; // 1 << 0
+ static const int PROPERTY_PRIVILEGED; // 1 << 3
+ static const int PROPERTY_GRID; // 1 << 4
+ static const int PROPERTY_PRIVATE; // 1 << 5
+ static const int PROPERTY_DISABLED; // 1 << 6
+ static const int PROPERTY_SUSPENDED; // 1 << 7
+
+private:
+ virtual ~LLExperienceCache();
+
+ void initSingleton() override;
+
+ // Callback types for get()
+ typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t;
+ typedef boost::shared_ptr signal_ptr;
+ // May have multiple callbacks for a single ID, which are
+ // represented as multiple slots bound to the signal.
+ // Avoid copying signals via pointers.
+ typedef std::map signal_map_t;
+ typedef std::map cache_t;
+
+ typedef uuid_set_t RequestQueue_t;
+ typedef std::map PendingQueue_t;
+
+ //--------------------------------------------
+ static const std::string PRIVATE_KEY; // "private_id"
+
+ // default values
+ static const F64 DEFAULT_EXPIRATION; // 600.0
+ static const S32 DEFAULT_QUOTA; // 128 this is megabytes
+ static const int SEARCH_PAGE_SIZE;
+
+//--------------------------------------------
+ void processExperience(const LLUUID& public_key, const LLSD& experience);
+
+//--------------------------------------------
+ cache_t mCache;
+ signal_map_t mSignalMap;
+ RequestQueue_t mRequestQueue;
+ PendingQueue_t mPendingQueue;
+
+ LLFrameTimer mEraseExpiredTimer; // Periodically clean out expired entries from the cache
+ CapabilityQuery_t mCapability;
+ std::string mCacheFileName;
+ bool mShutdown;
+
+ void eraseExpired();
+ void requestExperiencesCoro(const LLCoroResponder& responder, RequestQueue_t);
+ void requestExperiences();
+
+ void fetchAssociatedExperienceCoro(const LLCoroResponder& responder, ExperienceGetFn_t);
+ void findExperienceByNameCoro(const LLCoroResponder& responder, ExperienceGetFn_t);
+ void getGroupExperiencesCoro(const LLCoroResponder& responder, ExperienceGetFn_t);
+ void regionExperiences(CapabilityQuery_t regioncaps, const LLSD& experiences, bool update, ExperienceGetFn_t fn);
+ void regionExperiencesCoro(const LLCoroResponder& responder, ExperienceGetFn_t fn);
+ void experiencePermissionCoro(const LLCoroResponder& responder, ExperienceGetFn_t fn);
+
+ void bootstrap(const LLSD& legacyKeys, int initialExpiration);
+ void exportFile(std::ostream& ostr) const;
+ void importFile(std::istream& istr);
+
+ //
+ const cache_t& getCached();
+
+ // maps an experience private key to the experience id
+ LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false);
+
+ //=====================================================================
+ inline friend std::ostream &operator << (std::ostream &os, const LLExperienceCache &cache)
+ {
+ cache.exportFile(os);
+ return os;
+ }
+
+ inline friend std::istream &operator >> (std::istream &is, LLExperienceCache &cache)
+ {
+ cache.importFile(is);
+ return is;
+ }
+};
+
+#endif // LL_LLEXPERIENCECACHE_H
diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h
index 70194342a..95faff21b 100644
--- a/indra/llmessage/llregionflags.h
+++ b/indra/llmessage/llregionflags.h
@@ -168,19 +168,20 @@ const U32 ESTATE_ACCESS_ALL = ESTATE_ACCESS_ALLOWED_AGENTS
| ESTATE_ACCESS_BANNED_AGENTS
| ESTATE_ACCESS_MANAGERS;
-// for EstateOwnerRequest, estateaccessdelta message
-const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1 << 0;
-const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1 << 1;
+// for EstateOwnerRequest, estateaccessdelta, estateexperiencedelta messages
+const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1U << 0;
+const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1U << 1;
-const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1 << 2;
-const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1 << 3;
-const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1 << 4;
-const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1 << 5;
-const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1 << 6;
-const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1 << 7;
-const U32 ESTATE_ACCESS_MANAGER_ADD = 1 << 8;
-const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1 << 9;
-const U32 ESTATE_ACCESS_NO_REPLY = 1 << 10;
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1U << 2;
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1U << 3;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1U << 4;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1U << 5;
+const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1U << 6;
+const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1U << 7;
+const U32 ESTATE_ACCESS_MANAGER_ADD = 1U << 8;
+const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1U << 9;
+const U32 ESTATE_ACCESS_NO_REPLY = 1U << 10;
+const U32 ESTATE_ACCESS_FAILED_BAN_ESTATE_MANAGER = 1U << 11;
const S32 ESTATE_MAX_MANAGERS = 15;
const S32 ESTATE_MAX_ACCESS_IDS = 500; // max for access, banned
@@ -191,6 +192,26 @@ const U32 SWD_OTHERS_LAND_ONLY = (1 << 0);
const U32 SWD_ALWAYS_RETURN_OBJECTS = (1 << 1);
const U32 SWD_SCRIPTED_ONLY = (1 << 2);
+// Controls experience key validity in the estate
+const U32 EXPERIENCE_KEY_TYPE_NONE = 0;
+const U32 EXPERIENCE_KEY_TYPE_BLOCKED = 1;
+const U32 EXPERIENCE_KEY_TYPE_ALLOWED = 2;
+const U32 EXPERIENCE_KEY_TYPE_TRUSTED = 3;
+
+const U32 EXPERIENCE_KEY_TYPE_FIRST = EXPERIENCE_KEY_TYPE_BLOCKED;
+const U32 EXPERIENCE_KEY_TYPE_LAST = EXPERIENCE_KEY_TYPE_TRUSTED;
+
+//
+const U32 ESTATE_EXPERIENCE_TRUSTED_ADD = 1U << 2;
+const U32 ESTATE_EXPERIENCE_TRUSTED_REMOVE = 1U << 3;
+const U32 ESTATE_EXPERIENCE_ALLOWED_ADD = 1U << 4;
+const U32 ESTATE_EXPERIENCE_ALLOWED_REMOVE = 1U << 5;
+const U32 ESTATE_EXPERIENCE_BLOCKED_ADD = 1U << 6;
+const U32 ESTATE_EXPERIENCE_BLOCKED_REMOVE = 1U << 7;
+
+const S32 ESTATE_MAX_EXPERIENCE_IDS = 8;
+
+
#endif
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index 6598a402c..a9201ef46 100644
--- a/indra/llmessage/message_prehash.cpp
+++ b/indra/llmessage/message_prehash.cpp
@@ -1393,6 +1393,8 @@ char const* const _PREHASH_AppearanceVersion = LLMessageStringTable::getInstance
char const* const _PREHASH_CofVersion = LLMessageStringTable::getInstance()->getString("CofVersion");
char const* const _PREHASH_AppearanceHover = LLMessageStringTable::getInstance()->getString("AppearanceHover");
char const* const _PREHASH_HoverHeight = LLMessageStringTable::getInstance()->getString("HoverHeight");
+char const* const _PREHASH_Experience = LLMessageStringTable::getInstance()->getString("Experience");
+char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->getString("ExperienceID");
// Aurora Sim
char const* const _PREHASH_RegionSizeX = LLMessageStringTable::getInstance()->getString("RegionSizeX");
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
index e57e2379e..390ea8ff7 100644
--- a/indra/llmessage/message_prehash.h
+++ b/indra/llmessage/message_prehash.h
@@ -1393,6 +1393,8 @@ extern char const* const _PREHASH_AppearanceVersion;
extern char const* const _PREHASH_CofVersion;
extern char const* const _PREHASH_AppearanceHover;
extern char const* const _PREHASH_HoverHeight;
+extern char const* const _PREHASH_Experience;
+extern char const* const _PREHASH_ExperienceID;
// Aurora Sim
extern char const* const _PREHASH_RegionSizeX;
diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt
index 3a903080e..7ec2f5c5f 100644
--- a/indra/llplugin/CMakeLists.txt
+++ b/indra/llplugin/CMakeLists.txt
@@ -7,6 +7,7 @@ include(LLCommon)
include(LLMath)
include(LLMessage)
include(LLRender)
+include(Boost)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
@@ -46,14 +47,6 @@ set(llplugin_HEADER_FILES
set_source_files_properties(${llplugin_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
-if(NOT WORD_SIZE EQUAL 32)
- if(WINDOWS)
- # add_definitions(/FIXED:NO)
- else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
- add_definitions(-fPIC)
- endif(WINDOWS)
-endif (NOT WORD_SIZE EQUAL 32)
-
list(APPEND llplugin_SOURCE_FILES ${llplugin_HEADER_FILES})
add_library (llplugin ${llplugin_SOURCE_FILES})
@@ -67,6 +60,8 @@ else()
)
endif()
+set_target_properties(llplugin PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
+
add_subdirectory(slplugin)
# # Add tests
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index a6b041c46..daa7f4c8a 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -623,12 +623,14 @@ bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifie
return result;
}
-void LLPluginClassMedia::scrollEvent(int x, int y, MASK modifiers)
+void LLPluginClassMedia::scrollEvent(int x, int y, int clicks_x, int clicks_y, MASK modifiers)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "scroll_event");
message.setValueS32("x", x);
message.setValueS32("y", y);
+ message.setValueS32("clicks_x", clicks_x);
+ message.setValueS32("clicks_y", clicks_y);
message.setValue("modifiers", translateModifiers(modifiers));
sendMessage(message);
@@ -711,9 +713,9 @@ void LLPluginClassMedia::sendPickFileResponse(const std::vector fil
}
LLSD file_list = LLSD::emptyArray();
- for (std::vector::const_iterator in_iter = files.begin(); in_iter != files.end(); ++in_iter)
+ for (const auto& file : files)
{
- file_list.append(LLSD::String(*in_iter));
+ file_list.append(LLSD::String(file));
}
message.setValueLLSD("file_list", file_list);
diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h
index ebb9a36b5..7e8c2e4eb 100644
--- a/indra/llplugin/llpluginclassmedia.h
+++ b/indra/llplugin/llpluginclassmedia.h
@@ -75,7 +75,7 @@ public:
void setAutoScale(bool auto_scale);
void setZoomFactor(F64 zoom_factor) { mZoomFactor = zoom_factor; }
- void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; };
+ void setBackgroundColor(const LLColor4& color) { mBackgroundColor = color; };
void setOwner(LLPluginClassMediaOwner *owner) { mOwner = owner; };
@@ -107,7 +107,7 @@ public:
bool keyEvent(EKeyEventType type, int key_code, MASK modifiers, LLSD native_key_data);
- void scrollEvent(int x, int y, MASK modifiers);
+ void scrollEvent(int x, int y, int clicks_x, int clicks_y, MASK modifiers);
// enable/disable media plugin debugging messages and info spam
void enableMediaPluginDebugging( bool enable );
diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt
index 93a125ea1..f8ab56916 100644
--- a/indra/llplugin/slplugin/CMakeLists.txt
+++ b/indra/llplugin/slplugin/CMakeLists.txt
@@ -1,7 +1,6 @@
project(SLPlugin)
include(00-Common)
-include(Linking)
include(LLCommon)
include(LLPlugin)
include(Linking)
@@ -69,12 +68,7 @@ target_link_libraries(SLPlugin
${LLCOMMON_LIBRARIES}
${APRUTIL_LIBRARIES}
${PLUGIN_API_WINDOWS_LIBRARIES}
-)
-
-add_dependencies(SLPlugin
- ${LLPLUGIN_LIBRARIES}
- ${LLMESSAGE_LIBRARIES}
- ${LLCOMMON_LIBRARIES}
+ ${PTHREAD_LIBRARY}
)
if (DARWIN)
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
index e0dbd13d4..d69c73199 100644
--- a/indra/llprimitive/llprimitive.h
+++ b/indra/llprimitive/llprimitive.h
@@ -320,7 +320,7 @@ public:
// - Vir
struct LLTEContents
{
- static const U32 MAX_TES = 32;
+ static const U32 MAX_TES = 45;
U8 image_data[MAX_TES*16];
U8 colors[MAX_TES*4];
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 11b8c800e..3caf919eb 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -37,6 +37,7 @@ set(llui_SOURCE_FILES
lldraghandle.cpp
lleditmenuhandler.cpp
llfiltereditor.cpp
+ llflatlistview.cpp
llfloater.cpp
llflyoutbutton.cpp
llfocusmgr.cpp
@@ -116,6 +117,7 @@ set(llui_HEADER_FILES
lldraghandle.h
lleditmenuhandler.h
llfiltereditor.h
+ llflatlistview.h
llfloater.h
llflyoutbutton.h
llfocusmgr.h
diff --git a/indra/llui/lfidbearer.cpp b/indra/llui/lfidbearer.cpp
index cc7a3f988..6ac6714d5 100644
--- a/indra/llui/lfidbearer.cpp
+++ b/indra/llui/lfidbearer.cpp
@@ -20,13 +20,39 @@
#include "linden_common.h"
#include "lfidbearer.h"
#include "llmenugl.h"
+#include "lluictrlfactory.h"
-std::vector LFIDBearer::sMenus = {};
-LFIDBearer* LFIDBearer::sActive = nullptr;
+const std::array LFIDBearer::sMenuStrings
+{
+ "menu_avs_list.xml" // 0
+, "menu_groups_list.xml" // 1
+, "menu_objects_list.xml" // 2
+, "menu_experiences.xml" // 3
+};
+std::array LFIDBearer::sMenus {};
+
+const LFIDBearer* LFIDBearer::sActive = nullptr;
+LFIDBearer::Type LFIDBearer::sActiveType = LFIDBearer::AVATAR;
+uuid_vec_t LFIDBearer::sActiveIDs {};
+
+void LFIDBearer::buildMenus()
+{
+ auto& factory = LLUICtrlFactory::instance();
+ for (auto i = 0; i < COUNT; ++i)
+ sMenus[i] = factory.buildMenu(sMenuStrings[i], LLMenuGL::sMenuContainer);
+}
+
+LLMenuGL* LFIDBearer::showMenu(LLView* self, const std::string& menu_name, S32 x, S32 y, std::function on_menu_built)
+{
+ auto menu = LLUICtrlFactory::instance().buildMenu(menu_name, LLMenuGL::sMenuContainer);
+ if (on_menu_built) on_menu_built(menu);
+ showMenu(self, menu, x, y);
+ return menu;
+}
void LFIDBearer::showMenu(LLView* self, LLMenuGL* menu, S32 x, S32 y)
{
- sActive = this; // Menu listeners rely on this
+ setActive(); // Menu listeners rely on this
menu->buildDrawLabels();
menu->updateParent(LLMenuGL::sMenuContainer);
LLMenuGL::showPopup(self, menu, x, y);
diff --git a/indra/llui/lfidbearer.h b/indra/llui/lfidbearer.h
index 8b4c7684d..3f434a5a7 100644
--- a/indra/llui/lfidbearer.h
+++ b/indra/llui/lfidbearer.h
@@ -1,6 +1,7 @@
/* Copyright (C) 2019 Liru Færs
*
* LFIDBearer is a class that holds an ID or IDs that menus can use
+ * This class also bears the type of ID/IDs that it is holding
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -26,20 +27,47 @@ class LLView;
struct LFIDBearer
{
+ enum Type : S8
+ {
+ MULTIPLE = -2,
+ NONE = -1,
+ AVATAR = 0,
+ GROUP,
+ OBJECT,
+ EXPERIENCE,
+ COUNT
+ };
+
virtual ~LFIDBearer() { if (sActive == this) sActive = nullptr; }
virtual LLUUID getStringUUIDSelectedItem() const = 0;
- virtual uuid_vec_t getSelectedIDs() const = 0;
- virtual S32 getNumSelected() const = 0;
+ virtual uuid_vec_t getSelectedIDs() const { return { getStringUUIDSelectedItem() }; }
+ virtual Type getSelectedType() const { return AVATAR; }
- template static T* getActive() { return static_cast(sActive); }
- static LLUUID getActiveSelectedID() { return sActive->getStringUUIDSelectedItem(); }
- static uuid_vec_t getActiveSelectedIDs() { return sActive->getSelectedIDs(); }
- static S32 getActiveNumSelected() { return sActive->getNumSelected(); }
+ template static const T* getActive() { return static_cast(sActive); }
+ static const LLUUID& getActiveSelectedID() { return sActiveIDs.empty() ? LLUUID::null : sActiveIDs[0]; }
+ static const uuid_vec_t& getActiveSelectedIDs() { return sActiveIDs; }
+ static size_t getActiveNumSelected() { return sActiveIDs.size(); }
+ static const Type& getActiveType() { return sActiveType; }
+ void setActive() const
+ {
+ sActive = this;
+ sActiveType = getSelectedType();
+ sActiveIDs = getSelectedIDs();
+ //sActiveIDs or even some kinda hybrid map, if Type is MULTIPLE fill the vals? and remove a buncha virtual functions?
+ }
+
+ static void buildMenus();
+ LLMenuGL* showMenu(LLView* self, const std::string& menu_name, S32 x, S32 y, std::function on_menu_built = nullptr);
void showMenu(LLView* self, LLMenuGL* menu, S32 x, S32 y);
- static void addCommonMenu(LLMenuGL* menu) { sMenus.push_back(menu); }
protected:
- static std::vector sMenus; // Menus that recur, such as general avatars or groups menus
- static LFIDBearer* sActive;
+ // Menus that recur, such as general avatars or groups menus
+ static const std::array sMenuStrings;
+ static std::array sMenus;
+
+private:
+ static const LFIDBearer* sActive;
+ static Type sActiveType;
+ static uuid_vec_t sActiveIDs;
};
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index dd39a5ab0..284aa7750 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -446,8 +446,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name)
if (!mAllowTextEntry)
{
- mButton->setLabelUnselected(name);
- mButton->setLabelSelected(name);
+ mButton->setLabel(name);
}
}
@@ -465,9 +464,7 @@ void LLComboBox::updateLabel()
// the combo button label.
if (!mAllowTextEntry)
{
- std::string label = getSelectedItemLabel();
- mButton->setLabelUnselected(label);
- mButton->setLabelSelected(label);
+ mButton->setLabel(getSelectedItemLabel());
}
}
@@ -513,13 +510,16 @@ void LLComboBox::onFocusLost()
void LLComboBox::setButtonVisible(BOOL visible)
{
+ static LLUICachedControl drop_shadow_button ("DropShadowButton", 0);
+
mButton->setVisible(visible);
if (mTextEntry)
{
LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
if (visible)
{
- text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
}
//mTextEntry->setRect(text_entry_rect);
mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
@@ -558,18 +558,21 @@ S32 LLComboBox::getCurrentIndex() const
void LLComboBox::updateLayout()
{
+ static LLUICachedControl drop_shadow_button ("DropShadowButton", 0);
LLRect rect = getLocalRect();
if (mAllowTextEntry)
{
- S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton");
- mButton->setRect(LLRect( getRect().getWidth() - llmax(8,mArrowImage->getWidth()) - 2 * shadow_size,
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
+ S32 shadow_size = drop_shadow_button;
+ mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size,
rect.mTop, rect.mRight, rect.mBottom));
mButton->setTabStop(FALSE);
+ mButton->setHAlign(LLFontGL::HCENTER);
if (!mTextEntry)
{
LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * shadow_size;
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
// clear label on button
std::string cur_label = mButton->getLabelSelected();
mTextEntry = new LLLineEditor(std::string("combo_text_entry"),
@@ -718,6 +721,7 @@ void LLComboBox::showList()
mList->setVisible(TRUE);
setUseBoundingRect(TRUE);
+// updateBoundingRect();
}
void LLComboBox::hideList()
@@ -744,6 +748,7 @@ void LLComboBox::hideList()
{
gFocusMgr.setTopCtrl(NULL);
}
+// updateBoundingRect();
}
}
@@ -1029,9 +1034,7 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor)
void LLComboBox::updateSelection()
{
- if(mSuppressAutoComplete) {
- return;
- }
+ if(mSuppressAutoComplete) return;
LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor());
// user-entered portion of string, based on assumption that any selected
@@ -1242,3 +1245,25 @@ BOOL LLComboBox::selectItemRange( S32 first, S32 last )
return mList->selectItemRange(first, last);
}
+
+/* Singu Note: This isn't very necessary for now, let's not bother.
+static LLRegisterWidget register_icons_combo_box("icons_combo_box");
+
+LLIconsComboBox::Params::Params()
+: icon_column("icon_column", ICON_COLUMN),
+ label_column("label_column", LABEL_COLUMN)
+{}
+
+LLIconsComboBox::LLIconsComboBox(const LLIconsComboBox::Params& p)
+: LLComboBox(p),
+ mIconColumnIndex(p.icon_column),
+ mLabelColumnIndex(p.label_column)
+{}
+
+const std::string LLIconsComboBox::getSelectedItemLabel(S32 column) const
+{
+ mButton->setImageOverlay(LLComboBox::getSelectedItemLabel(mIconColumnIndex), mButton->getImageOverlayHAlign());
+
+ return LLComboBox::getSelectedItemLabel(mLabelColumnIndex);
+}
+*/
diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h
index d72283cd9..966ab352e 100644
--- a/indra/llui/lleditmenuhandler.h
+++ b/indra/llui/lleditmenuhandler.h
@@ -49,7 +49,7 @@ public:
virtual void cut() {};
virtual BOOL canCut() const { return FALSE; }
- virtual void copy() {};
+ virtual void copy() const {};
virtual BOOL canCopy() const { return FALSE; }
virtual void paste() {};
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
new file mode 100644
index 000000000..205bea49d
--- /dev/null
+++ b/indra/llui/llflatlistview.cpp
@@ -0,0 +1,1464 @@
+/**
+ * @file llflatlistview.cpp
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "lluictrlfactory.h"
+
+#include "llflatlistview.h"
+
+static const LLRegisterWidget flat_list_view("flat_list_view");
+
+const LLSD SELECTED_EVENT = LLSD().with("selected", true);
+const LLSD UNSELECTED_EVENT = LLSD().with("selected", false);
+
+//forward declaration
+bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2);
+
+LLFlatListView::Params::Params()
+: allow_select("allow_select"),
+ multi_select("multi_select"),
+ keep_one_selected("keep_one_selected"),
+ keep_selection_visible_on_reshape("keep_selection_visible_on_reshape",false),
+ item_pad("item_pad"),
+ no_items_text("no_items_text")
+{};
+
+void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
+{
+ S32 delta = height - getRect().getHeight();
+ LLScrollContainer::reshape(width, height, called_from_parent);
+ setItemsNoScrollWidth(width);
+ rearrangeItems();
+
+ if (delta!= 0 && mKeepSelectionVisibleOnReshape)
+ {
+ ensureSelectedVisible();
+ }
+}
+
+const LLRect& LLFlatListView::getItemsRect() const
+{
+ return mItemsPanel->getRect();
+}
+
+bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/)
+{
+ if (!item) return false;
+ if (value.isUndefined()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item->getParent() == mItemsPanel) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item, value);
+ switch (pos)
+ {
+ case ADD_TOP:
+ mItemPairs.push_front(new_pair);
+ //in LLView::draw() children are iterated in backorder
+ mItemsPanel->addChildInBack(item);
+ break;
+ case ADD_BOTTOM:
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item);
+ break;
+ default:
+ break;
+ }
+
+ //_4 is for MASK
+ item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+
+ // Children don't accept the focus
+ item->setTabStop(false);
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+ return true;
+}
+
+bool LLFlatListView::addItemPairs(pairs_list_t panel_list, bool rearrange /*= true*/)
+{
+ if (!mItemComparator)
+ {
+ LL_WARNS_ONCE() << "No comparator specified for inserting FlatListView items." << LL_ENDL;
+ return false;
+ }
+ if (panel_list.size() == 0)
+ {
+ return false;
+ }
+
+ // presort list so that it will be easier to sort elements into mItemPairs
+ panel_list.sort(ComparatorAdaptor(*mItemComparator));
+
+ pairs_const_iterator_t new_pair_it = panel_list.begin();
+ item_pair_t* new_pair = *new_pair_it;
+ pairs_iterator_t pair_it = mItemPairs.begin();
+ item_pair_t* item_pair = *pair_it;
+
+ // sort panel_list into mItemPars
+ while (new_pair_it != panel_list.end() && pair_it != mItemPairs.end())
+ {
+ if (!new_pair->first || new_pair->first->getParent() == mItemsPanel)
+ {
+ // iterator already used or we are reusing existing panel
+ new_pair_it++;
+ new_pair = *new_pair_it;
+ }
+ else if (mItemComparator->compare(new_pair->first, item_pair->first))
+ {
+ LLPanel* panel = new_pair->first;
+
+ mItemPairs.insert(pair_it, new_pair);
+ mItemsPanel->addChild(panel);
+
+ //_4 is for MASK
+ panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+ // Children don't accept the focus
+ panel->setTabStop(false);
+ }
+ else
+ {
+ pair_it++;
+ item_pair = *pair_it;
+ }
+ }
+
+ // Add what is left of panel_list into the end of mItemPairs.
+ for (; new_pair_it != panel_list.end(); ++new_pair_it)
+ {
+ item_pair_t* item_pair = *new_pair_it;
+ LLPanel *panel = item_pair->first;
+ if (panel && panel->getParent() != mItemsPanel)
+ {
+ mItemPairs.push_back(item_pair);
+ mItemsPanel->addChild(panel);
+
+ //_4 is for MASK
+ panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, item_pair, _4));
+ panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, item_pair, _4));
+ // Children don't accept the focus
+ panel->setTabStop(false);
+ }
+ }
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+ return true;
+}
+
+
+bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/)
+{
+ if (!after_item) return false;
+ if (!item_to_add) return false;
+ if (value.isUndefined()) return false;
+
+ if (mItemPairs.empty()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item_to_add->getParent() == mItemsPanel) return false;
+
+ item_pair_t* after_pair = getItemPair(after_item);
+ if (!after_pair) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item_to_add, value);
+ if (after_pair == mItemPairs.back())
+ {
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item_to_add);
+ }
+ else
+ {
+ pairs_iterator_t it = mItemPairs.begin();
+ for (; it != mItemPairs.end(); ++it)
+ {
+ if (*it == after_pair)
+ {
+ // insert new elements before the element at position of passed iterator.
+ mItemPairs.insert(++it, new_pair);
+ mItemsPanel->addChild(item_to_add);
+ break;
+ }
+ }
+ }
+
+ //_4 is for MASK
+ item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ return true;
+}
+
+
+bool LLFlatListView::removeItem(LLPanel* item, bool rearrange)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair, rearrange);
+}
+
+bool LLFlatListView::removeItemByValue(const LLSD& value, bool rearrange)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair, rearrange);
+}
+
+bool LLFlatListView::removeItemByUUID(const LLUUID& uuid, bool rearrange)
+{
+ return removeItemByValue(LLSD(uuid), rearrange);
+}
+
+LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const
+{
+ if (value.isUndefined()) return nullptr;
+
+ item_pair_t* pair = getItemPair(value);
+ if (pair) return pair->first;
+ return nullptr;
+}
+
+bool LLFlatListView::valueExists(const LLSD& value) const
+{
+ if (value.isUndefined()) return false;
+ item_pair_t* pair = getItemPair(value);
+ return pair != nullptr;
+}
+
+bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/)
+{
+ return selectItemByValue(LLSD(uuid), select);
+}
+
+
+LLSD LLFlatListView::getSelectedValue() const
+{
+ if (mSelectedItemPairs.empty()) return LLSD();
+
+ item_pair_t* first_selected_pair = mSelectedItemPairs.front();
+ return first_selected_pair->second;
+}
+
+void LLFlatListView::getSelectedValues(std::vector& selected_values) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_values.push_back((*it)->second);
+ }
+}
+
+LLUUID LLFlatListView::getSelectedUUID() const
+{
+ const LLSD& value = getSelectedValue();
+ if (value.isDefined() && value.isUUID())
+ {
+ return value.asUUID();
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+void LLFlatListView::getSelectedUUIDs(uuid_vec_t& selected_uuids) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_uuids.push_back((*it)->second.asUUID());
+ }
+}
+
+LLPanel* LLFlatListView::getSelectedItem() const
+{
+ if (mSelectedItemPairs.empty()) return nullptr;
+
+ return mSelectedItemPairs.front()->first;
+}
+
+void LLFlatListView::getSelectedItems(std::vector& selected_items) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_items.push_back((*it)->first);
+ }
+}
+
+void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/)
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* pair_to_deselect = *it;
+ LLPanel* item = pair_to_deselect->first;
+ item->setValue(UNSELECTED_EVENT);
+ }
+
+ mSelectedItemPairs.clear();
+
+ if (mCommitOnSelectionChange && !no_commit_on_deselection)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
+{
+ mNoItemsCommentTextbox->setValue(comment_text);
+}
+
+U32 LLFlatListView::size(const bool only_visible_items) const
+{
+ if (only_visible_items)
+ {
+ U32 size = 0;
+ for (pairs_const_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ if ((*iter)->first->getVisible())
+ ++size;
+ }
+ return size;
+ }
+ else
+ {
+ return mItemPairs.size();
+ }
+}
+
+void LLFlatListView::clear()
+{
+ // This will clear mSelectedItemPairs, calling all appropriate callbacks.
+ resetSelection();
+
+ // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex.
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ mItemsPanel->removeChild((*it)->first);
+ (*it)->first->die();
+ delete *it;
+ }
+ mItemPairs.clear();
+
+ // also set items panel height to zero. Reshape it to allow reshaping of non-item children
+ LLRect rc = mItemsPanel->getRect();
+ rc.mBottom = rc.mTop;
+ mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
+ mItemsPanel->setRect(rc);
+
+ setNoItemsCommentVisible(true);
+ notifyParentItemsRectChanged();
+}
+
+void LLFlatListView::sort()
+{
+ if (!mItemComparator)
+ {
+ LL_WARNS() << "No comparator specified for sorting FlatListView items." << LL_ENDL;
+ return;
+ }
+
+ mItemPairs.sort(ComparatorAdaptor(*mItemComparator));
+ rearrangeItems();
+}
+
+bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value)
+{
+ if (old_value.isUndefined() || new_value.isUndefined()) return false;
+ if (llsds_are_equal(old_value, new_value)) return false;
+
+ item_pair_t* item_pair = getItemPair(old_value);
+ if (!item_pair) return false;
+
+ item_pair->second = new_value;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PROTECTED STUFF
+//////////////////////////////////////////////////////////////////////////
+
+LLFlatListView::LLFlatListView(const std::string& name, const LLRect& rect, bool opaque, const LLColor4& color, const S32& item_pad, bool allow_select, bool multi_select, bool keep_one_selected, bool keep_selection_visible_on_reshape, const std::string& no_items_text)
+: LLScrollContainer(name, rect, nullptr, opaque, color)
+ , mItemComparator(nullptr)
+ , mItemsPanel(nullptr)
+ , mItemPad(item_pad)
+ , mAllowSelection(allow_select)
+ , mMultipleSelection(multi_select)
+ , mCommitOnSelectionChange(false)
+ , mKeepOneItemSelected(keep_one_selected)
+ , mIsConsecutiveSelection(false)
+ , mKeepSelectionVisibleOnReshape(keep_selection_visible_on_reshape)
+ , mPrevNotifyParentRect(LLRect())
+ , mNoItemsCommentTextbox(nullptr)
+{
+ mBorderThickness = getBorderWidth();
+
+ LLRect scroll_rect = getRect();
+ LLRect items_rect;
+
+ setItemsNoScrollWidth(scroll_rect.getWidth());
+ items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0);
+
+ mItemsPanel = new LLPanel("items panel", items_rect);
+ addChild(mItemsPanel);
+
+ //we don't need to stretch in vertical direction on reshaping by a parent
+ //no bottom following!
+ mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+
+ mSelectedItemsBorder = new LLViewBorder(
+ "scroll border",
+ getLastSelectedItemRect(),
+ LLViewBorder::BEVEL_IN);
+ mSelectedItemsBorder->setVisible(false);
+ mItemsPanel->addChild(mSelectedItemsBorder);
+
+ {
+ // create textbox for "No Items" comment text
+ {
+ LLRect comment_rect = getRect();
+ comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight());
+ comment_rect.stretch(-getBorderWidth());
+ mNoItemsCommentTextbox = new LLTextBox(no_items_text, comment_rect, no_items_text);
+ }
+ mNoItemsCommentTextbox->setBorderVisible(false);
+
+ {
+ mNoItemsCommentTextbox->setFollows(FOLLOWS_ALL);
+ }
+ }
+};
+
+LLFlatListView::~LLFlatListView()
+{
+ delete_and_clear(mItemPairs);
+}
+
+// virtual
+void LLFlatListView::draw()
+{
+ // Highlight border if a child of this container has keyboard focus
+ if ( mSelectedItemsBorder->getVisible() )
+ {
+ mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() );
+ }
+ LLScrollContainer::draw();
+}
+
+// virtual
+BOOL LLFlatListView::postBuild()
+{
+ setTabStop(true);
+ return LLScrollContainer::postBuild();
+}
+
+void LLFlatListView::rearrangeItems()
+{
+ static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0);
+
+ setNoItemsCommentVisible(0==size());
+
+ if (mItemPairs.empty()) return;
+
+ //calculating required height - assuming items can be of different height
+ //list should accommodate all its items
+ S32 height = 0;
+
+ S32 invisible_children_count = 0;
+ pairs_iterator_t it = mItemPairs.begin();
+ for (; it != mItemPairs.end(); ++it)
+ {
+ LLPanel* item = (*it)->first;
+
+ // skip invisible child
+ if (!item->getVisible())
+ {
+ ++invisible_children_count;
+ continue;
+ }
+
+ height += item->getRect().getHeight();
+ }
+
+ // add paddings between items, excluding invisible ones
+ height += mItemPad * (mItemPairs.size() - invisible_children_count - 1);
+
+ LLRect rc = mItemsPanel->getRect();
+ S32 width = mItemsNoScrollWidth;
+
+ // update width to avoid horizontal scrollbar
+ if (height > getRect().getHeight() - 2 * mBorderThickness)
+ width -= scrollbar_size;
+
+ //changes the bottom, end of the list goes down in the scroll container
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height);
+ mItemsPanel->setRect(rc);
+
+ //reshaping items
+ S32 item_new_top = height;
+ pairs_iterator_t it2, first_it = mItemPairs.begin();
+ for (it2 = first_it; it2 != mItemPairs.end(); ++it2)
+ {
+ LLPanel* item = (*it2)->first;
+
+ // skip invisible child
+ if (!item->getVisible())
+ continue;
+
+ LLRect rc = item->getRect();
+ rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight());
+ item->reshape(rc.getWidth(), rc.getHeight());
+ item->setRect(rc);
+
+ // move top for next item in list
+ item_new_top -= (rc.getHeight() + mItemPad);
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
+{
+ if (!item_pair) return;
+
+ if (!item_pair->first)
+ {
+ LL_WARNS() << "Attempt to selet an item pair containing null panel item" << LL_ENDL;
+ return;
+ }
+
+ setFocus(TRUE);
+
+ bool select_item = !isSelected(item_pair);
+
+ //*TODO find a better place for that enforcing stuff
+ if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return;
+
+ if ( (mask & MASK_SHIFT) && !(mask & MASK_CONTROL)
+ && mMultipleSelection && !mSelectedItemPairs.empty() )
+ {
+ item_pair_t* last_selected_pair = mSelectedItemPairs.back();
+
+ // If item_pair is already selected - do nothing
+ if (last_selected_pair == item_pair)
+ return;
+
+ bool grab_items = false;
+ bool reverse = false;
+ pairs_list_t pairs_to_select;
+
+ // Pick out items from list between last selected and current clicked item_pair.
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* cur = *iter;
+ if (cur == last_selected_pair || cur == item_pair)
+ {
+ // We've got reverse selection if last grabed item isn't a new selection.
+ reverse = grab_items && (cur != item_pair);
+ grab_items = !grab_items;
+ // Skip last selected and current clicked item pairs.
+ continue;
+ }
+ if (!cur->first->getVisible())
+ {
+ // Skip invisible item pairs.
+ continue;
+ }
+ if (grab_items)
+ {
+ pairs_to_select.push_back(cur);
+ }
+ }
+
+ if (reverse)
+ {
+ pairs_to_select.reverse();
+ }
+
+ pairs_to_select.push_back(item_pair);
+
+ for (pairs_iterator_t
+ iter = pairs_to_select.begin(),
+ iter_end = pairs_to_select.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* pair_to_select = *iter;
+ if (isSelected(pair_to_select))
+ {
+ // Item was already selected but there is a need to keep order from last selected pair to new selection.
+ // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair().
+ mSelectedItemPairs.remove(pair_to_select);
+ mSelectedItemPairs.push_back(pair_to_select);
+ }
+ else
+ {
+ selectItemPair(pair_to_select, true);
+ }
+ }
+
+ if (!select_item)
+ {
+ // Update last selected item border.
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+ }
+ return;
+ }
+
+ //no need to do additional commit on selection reset
+ if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(true);
+
+ //only CTRL usage allows to deselect an item, usual clicking on an item cannot deselect it
+ if (mask & MASK_CONTROL)
+ selectItemPair(item_pair, select_item);
+ else
+ selectItemPair(item_pair, true);
+}
+
+void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask)
+{
+ if (!item_pair)
+ return;
+
+ // Forbid deselecting of items on right mouse button click if mMultipleSelection flag is set on,
+ // because some of derived classes may have context menu and selected items must be kept.
+ if ( !(mask & MASK_CONTROL) && mMultipleSelection && isSelected(item_pair) )
+ return;
+
+ // else got same behavior as at onItemMouseClick
+ onItemMouseClick(item_pair, mask);
+}
+
+BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)
+{
+ BOOL reset_selection = (mask != MASK_SHIFT);
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_RETURN:
+ {
+ if (mSelectedItemPairs.size() && mask == MASK_NONE)
+ {
+ mOnReturnSignal(this, getValue());
+ handled = TRUE;
+ }
+ break;
+ }
+ case KEY_UP:
+ {
+ if ( !selectNextItemPair(true, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the previous accordion
+ if (notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
+ resetSelection();
+ }
+ break;
+ }
+ case KEY_DOWN:
+ {
+ if ( !selectNextItemPair(false, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the next accordion
+ if ( notifyParent(LLSD().with("action","select_next")) > 0 ) //message was processed
+ resetSelection();
+ }
+ break;
+ }
+ case KEY_ESCAPE:
+ {
+ if (mask == MASK_NONE)
+ {
+ setFocus(FALSE); // pass focus to the game area (EXT-8357)
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() )
+ {
+ ensureSelectedVisible();
+ /*
+ LLRect visible_rc = getVisibleContentRect();
+ LLRect selected_rc = getLastSelectedItemRect();
+
+ if ( !visible_rc.contains (selected_rc) )
+ {
+ // But scroll in Items panel coordinates
+ scrollToShowRect(selected_rc);
+ }
+
+ // In case we are in accordion tab notify parent to show selected rectangle
+ LLRect screen_rc;
+ localRectToScreen(selected_rc, &screen_rc);
+ notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/
+
+ handled = TRUE;
+ }
+
+ return handled ? handled : LLScrollContainer::handleKeyHere(key, mask);
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const
+{
+ llassert(item);
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (item_pair->first == item) return item_pair;
+ }
+ return nullptr;
+}
+
+//compares two LLSD's
+bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2)
+{
+ llassert(llsd_1.isDefined());
+ llassert(llsd_2.isDefined());
+
+ if (llsd_1.type() != llsd_2.type()) return false;
+
+ if (!llsd_1.isMap())
+ {
+ if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID();
+
+ //assumptions that string representaion is enough for other types
+ return llsd_1.asString() == llsd_2.asString();
+ }
+
+ if (llsd_1.size() != llsd_2.size()) return false;
+
+ LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap();
+ LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap();
+ for (S32 i = 0; i < llsd_1.size(); ++i)
+ {
+ if ((*llsd_1_it).first != (*llsd_2_it).first) return false;
+ if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false;
+ ++llsd_1_it;
+ ++llsd_2_it;
+ }
+ return true;
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const
+{
+ llassert(value.isDefined());
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (llsds_are_equal(item_pair->second, value)) return item_pair;
+ }
+ return nullptr;
+}
+
+bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
+{
+ llassert(item_pair);
+
+ if (!mAllowSelection && select) return false;
+
+ if (isSelected(item_pair) == select) return true; //already in specified selection state
+ if (select)
+ {
+ mSelectedItemPairs.push_back(item_pair);
+ }
+ else
+ {
+ mSelectedItemPairs.remove(item_pair);
+ }
+
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT);
+
+ if (mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+ // By default mark it as not consecutive selection
+ mIsConsecutiveSelection = false;
+
+ return true;
+}
+
+void LLFlatListView::scrollToShowFirstSelectedItem()
+{
+ if (!mSelectedItemPairs.size()) return;
+
+ LLRect selected_rc = mSelectedItemPairs.front()->first->getRect();
+
+ if (selected_rc.isValid())
+ {
+ scrollToShowRect(selected_rc);
+ }
+}
+
+LLRect LLFlatListView::getLastSelectedItemRect()
+{
+ if (!mSelectedItemPairs.size())
+ {
+ return LLRect::null;
+ }
+
+ return mSelectedItemPairs.back()->first->getRect();
+}
+
+void LLFlatListView::selectFirstItem ()
+{
+ // No items - no actions!
+ if (0 == size()) return;
+
+ // Select first visible item
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ // skip invisible items
+ if ( (*iter)->first->getVisible() )
+ {
+ selectItemPair(*iter, true);
+ ensureSelectedVisible();
+ break;
+ }
+ }
+}
+
+void LLFlatListView::selectLastItem ()
+{
+ // No items - no actions!
+ if (0 == size()) return;
+
+ // Select last visible item
+ for (pairs_list_t::reverse_iterator
+ r_iter = mItemPairs.rbegin(),
+ r_iter_end = mItemPairs.rend();
+ r_iter != r_iter_end; ++r_iter)
+ {
+ // skip invisible items
+ if ( (*r_iter)->first->getVisible() )
+ {
+ selectItemPair(*r_iter, true);
+ ensureSelectedVisible();
+ break;
+ }
+ }
+}
+
+void LLFlatListView::ensureSelectedVisible()
+{
+ LLRect selected_rc = getLastSelectedItemRect();
+
+ if (selected_rc.isValid())
+ {
+ scrollToShowRect(selected_rc);
+ }
+}
+
+
+// virtual
+bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection)
+{
+ // No items - no actions!
+ if (0 == size())
+ return false;
+
+ if (!mIsConsecutiveSelection)
+ {
+ // Leave only one item selected if list has not consecutive selection
+ if (mSelectedItemPairs.size() && !reset_selection)
+ {
+ item_pair_t* cur_sel_pair = mSelectedItemPairs.back();
+ resetSelection();
+ selectItemPair (cur_sel_pair, true);
+ }
+ }
+
+ if (mSelectedItemPairs.size())
+ {
+ item_pair_t* to_sel_pair = nullptr;
+ item_pair_t* cur_sel_pair = nullptr;
+
+ // Take the last selected pair
+ cur_sel_pair = mSelectedItemPairs.back();
+ // Bases on given direction choose next item to select
+ if (is_up_direction)
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.rend();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.end();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+
+ if (to_sel_pair)
+ {
+ bool select = true;
+ if (reset_selection)
+ {
+ // Reset current selection if we were asked about it
+ resetSelection();
+ }
+ else
+ {
+ // If item already selected and no reset request than we should deselect last selected item.
+ select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
+ }
+ // Select/Deselect next item
+ selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
+ // Mark it as consecutive selection
+ mIsConsecutiveSelection = true;
+ return true;
+ }
+ }
+ else
+ {
+ // If there weren't selected items then choose the first one bases on given direction
+ // Force selection to first item
+ if (is_up_direction)
+ selectLastItem();
+ else
+ selectFirstItem();
+ // Mark it as consecutive selection
+ mIsConsecutiveSelection = true;
+ return true;
+ }
+
+ return false;
+}
+
+BOOL LLFlatListView::canSelectAll() const
+{
+ return 0 != size() && mAllowSelection && mMultipleSelection;
+}
+
+void LLFlatListView::selectAll()
+{
+ if (!mAllowSelection || !mMultipleSelection)
+ return;
+
+ mSelectedItemPairs.clear();
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ mSelectedItemPairs.push_back(item_pair);
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(SELECTED_EVENT);
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+bool LLFlatListView::isSelected(item_pair_t* item_pair) const
+{
+ llassert(item_pair);
+
+ pairs_const_iterator_t it_end = mSelectedItemPairs.end();
+ return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end;
+}
+
+bool LLFlatListView::removeItemPair(item_pair_t* item_pair, bool rearrange)
+{
+ llassert(item_pair);
+
+ bool deleted = false;
+ bool selection_changed = false;
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* _item_pair = *it;
+ if (_item_pair == item_pair)
+ {
+ mItemPairs.erase(it);
+ deleted = true;
+ break;
+ }
+ }
+
+ if (!deleted) return false;
+
+ for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* selected_item_pair = *it;
+ if (selected_item_pair == item_pair)
+ {
+ it = mSelectedItemPairs.erase(it);
+ selection_changed = true;
+ break;
+ }
+ }
+
+ mItemsPanel->removeChild(item_pair->first);
+ item_pair->first->die();
+ delete item_pair;
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+
+ if (selection_changed && mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ return true;
+}
+
+void LLFlatListView::notifyParentItemsRectChanged()
+{
+ S32 comment_height = 0;
+
+ // take into account comment text height if exists
+ if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible())
+ {
+ // top text padding inside the textbox is included into the height
+ comment_height = mNoItemsCommentTextbox->getTextPixelHeight();
+
+ // take into account a distance from parent's top border to textbox's top
+ comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop;
+ }
+
+ LLRect req_rect = getItemsRect();
+
+ // get maximum of items total height and comment text height
+ req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height));
+
+ // take into account border size.
+ req_rect.stretch(getBorderWidth());
+
+ if (req_rect == mPrevNotifyParentRect)
+ return;
+
+ mPrevNotifyParentRect = req_rect;
+
+ LLSD params;
+ params["action"] = "size_changes";
+ params["width"] = req_rect.getWidth();
+ params["height"] = req_rect.getHeight();
+
+ if (getParent()) // dummy widgets don't have a parent
+ getParent()->notifyParent(params);
+}
+
+void LLFlatListView::setNoItemsCommentVisible(bool visible) const
+{
+ if (mNoItemsCommentTextbox)
+ {
+ mSelectedItemsBorder->setVisible(!visible);
+ mNoItemsCommentTextbox->setVisible(visible);
+ }
+}
+
+void LLFlatListView::getItems(std::vector& items) const
+{
+ if (mItemPairs.empty()) return;
+
+ items.clear();
+ for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ items.push_back((*it)->first);
+ }
+}
+
+void LLFlatListView::getValues(std::vector& values) const
+{
+ if (mItemPairs.empty()) return;
+
+ values.clear();
+ for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ values.push_back((*it)->second);
+ }
+}
+
+// virtual
+void LLFlatListView::onFocusReceived()
+{
+ if (size())
+ {
+ mSelectedItemsBorder->setVisible(TRUE);
+ }
+ gEditMenuHandler = this;
+}
+// virtual
+void LLFlatListView::onFocusLost()
+{
+ mSelectedItemsBorder->setVisible(FALSE);
+ // Route menu back to the default
+ if (gEditMenuHandler == this)
+ {
+ gEditMenuHandler = nullptr;
+ }
+}
+
+//virtual
+S32 LLFlatListView::notify(const LLSD& info)
+{
+ if (info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if (str_action == "select_first")
+ {
+ setFocus(true);
+ selectFirstItem();
+ return 1;
+ }
+ else if (str_action == "select_last")
+ {
+ setFocus(true);
+ selectLastItem();
+ return 1;
+ }
+ }
+ else if (info.has("rearrange"))
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ return 1;
+ }
+ return 0;
+}
+
+void LLFlatListView::detachItems(std::vector& detached_items)
+{
+ LLSD action;
+ action.with("detach", LLSD());
+ // Clear detached_items list
+ detached_items.clear();
+ // Go through items and detach valid items, remove them from items panel
+ // and add to detached_items.
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ LLPanel* pItem = (*iter)->first;
+ if (1 == pItem->notify(action))
+ {
+ selectItemPair((*iter), false);
+ mItemsPanel->removeChild(pItem);
+ detached_items.push_back(pItem);
+ }
+ }
+ if (!detached_items.empty())
+ {
+ // Some items were detached, clean ourself from unusable memory
+ if (detached_items.size() == mItemPairs.size())
+ {
+ // This way will be faster if all items were disconnected
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ (*iter)->first = nullptr;
+ delete *iter;
+ }
+ mItemPairs.clear();
+ // Also set items panel height to zero.
+ // Reshape it to allow reshaping of non-item children.
+ LLRect rc = mItemsPanel->getRect();
+ rc.mBottom = rc.mTop;
+ mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
+ mItemsPanel->setRect(rc);
+ setNoItemsCommentVisible(true);
+ }
+ else
+ {
+ for (std::vector::const_iterator
+ detached_iter = detached_items.begin(),
+ detached_iter_end = detached_items.end();
+ detached_iter != detached_iter_end; ++detached_iter)
+ {
+ LLPanel* pDetachedItem = *detached_iter;
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* item_pair = *iter;
+ if (item_pair->first == pDetachedItem)
+ {
+ mItemPairs.erase(iter);
+ item_pair->first = nullptr;
+ delete item_pair;
+ break;
+ }
+ }
+ }
+ rearrangeItems();
+ }
+ notifyParentItemsRectChanged();
+ }
+}
+
+
+/************************************************************************/
+/* LLFlatListViewEx implementation */
+/************************************************************************/
+/* Singu Note: Let's not use this for now...
+LLFlatListViewEx::Params::Params()
+: no_items_msg("no_items_msg")
+, no_filtered_items_msg("no_filtered_items_msg")
+{
+
+}
+
+LLFlatListViewEx::LLFlatListViewEx(const Params& p)
+: LLFlatListView(p)
+, mNoFilteredItemsMsg(p.no_filtered_items_msg)
+, mNoItemsMsg(p.no_items_msg)
+, mForceShowingUnmatchedItems(false)
+, mHasMatchedItems(false)
+{
+
+}
+
+void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string)
+{
+ bool items_filtered = !filter_string.empty();
+ if (items_filtered)
+ {
+ // items were filtered
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
+ std::string text = mNoFilteredItemsMsg;
+ LLStringUtil::format(text, args);
+ setNoItemsCommentText(text);
+ }
+ else
+ {
+ // list does not contain any items at all
+ setNoItemsCommentText(mNoItemsMsg);
+ }
+
+}
+
+bool LLFlatListViewEx::getForceShowingUnmatchedItems()
+{
+ return mForceShowingUnmatchedItems;
+}
+
+void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show)
+{
+ mForceShowingUnmatchedItems = show;
+}
+
+void LLFlatListViewEx::setFilterSubString(const std::string& filter_str)
+{
+ if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString))
+ {
+ mFilterSubString = filter_str;
+ updateNoItemsMessage(mFilterSubString);
+ filterItems();
+ }
+}
+
+void LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action)
+{
+ if (!item) return;
+
+ // 0 signifies that filter is matched,
+ // i.e. we don't hide items that don't support 'match_filter' action, separators etc.
+ if (0 == item->notify(action))
+ {
+ mHasMatchedItems = true;
+ item->setVisible(true);
+ }
+ else
+ {
+ // TODO: implement (re)storing of current selection.
+ if (!mForceShowingUnmatchedItems)
+ {
+ selectItem(item, false);
+ }
+ item->setVisible(mForceShowingUnmatchedItems);
+ }
+}
+
+void LLFlatListViewEx::filterItems()
+{
+ typedef std::vector item_panel_list_t;
+
+ std::string cur_filter = mFilterSubString;
+ LLStringUtil::toUpper(cur_filter);
+
+ LLSD action;
+ action.with("match_filter", cur_filter);
+
+ item_panel_list_t items;
+ getItems(items);
+
+ mHasMatchedItems = false;
+ for (item_panel_list_t::iterator
+ iter = items.begin(),
+ iter_end = items.end();
+ iter != iter_end; ++iter)
+ {
+ LLPanel* pItem = (*iter);
+ updateItemVisibility(pItem, action);
+ }
+
+ sort();
+ notifyParentItemsRectChanged();
+}
+
+bool LLFlatListViewEx::hasMatchedItems()
+{
+ return mHasMatchedItems;
+}
+*/
+
+// Old-style fromXML stuffs
+// static
+LLView* LLFlatListView::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ // Stuff from scroll container
+ std::string name("flat_list_view");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL opaque = FALSE;
+ node->getAttributeBOOL("opaque", opaque);
+
+ LLColor4 color(0,0,0,0);
+ LLUICtrlFactory::getAttributeColor(node, "color", color);
+
+ // Stuff from flat list
+ bool allow_select = false;
+ node->getAttribute_bool("allow_select", allow_select);
+ bool multi_select = false;
+ node->getAttribute_bool("multi_select", multi_select);
+ bool keep_one_selected = false;
+ node->getAttribute_bool("keep_one_selected", keep_one_selected);
+ bool keep_selection_visible_on_reshape = false;
+ node->getAttribute_bool("keep_selection_visible_on_reshape", keep_selection_visible_on_reshape);
+ U32 item_pad;
+ node->getAttributeU32("item_pad", item_pad);
+ std::string no_items_text;
+ node->getAttributeString("no_items_text", no_items_text);
+
+ return new LLFlatListView(name, rect, opaque, color, item_pad, allow_select, multi_select, keep_one_selected, keep_selection_visible_on_reshape, no_items_text);
+}
+//
+
+//EOF
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
new file mode 100644
index 000000000..ade20412a
--- /dev/null
+++ b/indra/llui/llflatlistview.h
@@ -0,0 +1,540 @@
+/**
+ * @file llflatlistview.h
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
+ *
+ * $LicenseInfo:firstyear=2009&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_LLFLATLISTVIEW_H
+#define LL_LLFLATLISTVIEW_H
+
+#include "llpanel.h"
+#include "llscrollcontainer.h"
+#include "lltextbox.h"
+
+
+/**
+ * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's.
+ * LLSD can be associated with each added item, it can keep data from an item in digested form.
+ * Associated LLSD's can be of any type (singular, a map etc.).
+ * Items (LLPanel's subclasses) can be of different height.
+ * The list is LLPanel created in itself and grows in height while new items are added.
+ *
+ * The control can manage selection of its items when the flag "allow_select" is set. Also ability to select
+ * multiple items (by using CTRL) is enabled through setting the flag "multi_select" - if selection is not allowed that flag
+ * is ignored. The option "keep_one_selected" forces at least one item to be selected at any time (only for mouse events on items)
+ * since any item of the list was selected.
+ *
+ * Examples of using this control are presented in Picks panel (My Profile and Profile View), where this control is used to
+ * manage the list of pick items.
+ *
+ * ASSUMPTIONS AND STUFF
+ * - NULL pointers and undefined LLSD's are not accepted by any method of this class unless specified otherwise
+ * - Order of returned selected items are not guaranteed
+ * - The control assumes that all items being added are unique.
+ */
+class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
+{
+ LOG_CLASS(LLFlatListView);
+public:
+
+ /**
+ * Abstract comparator for comparing flat list items in a form of LLPanel
+ */
+ class ItemComparator
+ {
+ public:
+ ItemComparator() {};
+ virtual ~ItemComparator() {};
+
+ /** Returns true if item1 < item2, false otherwise */
+ virtual bool compare(const LLPanel* item1, const LLPanel* item2) const = 0;
+ };
+
+ /**
+ * Represents reverse comparator which acts as a decorator for a comparator that need to be reversed
+ */
+ class ItemReverseComparator : public ItemComparator
+ {
+ public:
+ ItemReverseComparator(const ItemComparator& comparator) : mComparator(comparator) {};
+ virtual ~ItemReverseComparator() {};
+
+ bool compare(const LLPanel* item1, const LLPanel* item2) const override
+ {
+ return mComparator.compare(item2, item1);
+ }
+
+ private:
+ const ItemComparator& mComparator;
+ };
+
+
+ struct Params : public LLInitParam::Block
+ {
+ /** turning on/off selection support */
+ Optional allow_select;
+
+ /** turning on/off multiple selection (works while clicking and holding CTRL)*/
+ Optional multi_select;
+
+ /** don't allow to deselect all selected items (for mouse events on items only) */
+ Optional keep_one_selected;
+
+ /** try to keep selection visible after reshape */
+ Optional keep_selection_visible_on_reshape;
+
+ /** padding between items */
+ Optional item_pad;
+
+ /** textbox with info message when list is empty*/
+ Optional no_items_text;
+
+ Params();
+ };
+
+ // disable traversal when finding widget to hand focus off to
+ /*virtual*/ BOOL canFocusChildren() const override { return FALSE; }
+
+ /**
+ * Connects callback to signal called when Return key is pressed.
+ */
+ boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); }
+
+ /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */
+ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) override;
+
+ /** Returns full rect of child panel */
+ const LLRect& getItemsRect() const;
+
+ LLRect getRequiredRect() override { return getItemsRect(); }
+
+ /** Returns distance between items */
+ const S32 getItemsPad() const { return mItemPad; }
+
+ /**
+ * Adds and item and LLSD value associated with it to the list at specified position
+ * @return true if the item was added, false otherwise
+ */
+ virtual bool addItem(LLPanel * item, const LLSD& value = LLUUID::null, EAddPosition pos = ADD_BOTTOM, bool rearrange = true);
+
+ /**
+ * Insert item_to_add along with associated value to the list right after the after_item.
+ * @return true if the item was successfully added, false otherwise
+ */
+ virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value = LLUUID::null);
+
+ /**
+ * Remove specified item
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItem(LLPanel* item, bool rearrange = true);
+
+ /**
+ * Remove an item specified by value
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByValue(const LLSD& value, bool rearrange = true);
+
+ /**
+ * Remove an item specified by uuid
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByUUID(const LLUUID& uuid, bool rearrange = true);
+
+ /**
+ * Get an item by value
+ * @return the item as LLPanel if associated with value, NULL otherwise
+ */
+ virtual LLPanel* getItemByValue(const LLSD& value) const;
+
+ /**
+ * Check for item by value in list
+ * @return bool whether item exists by value or not
+ */
+ virtual bool valueExists(const LLSD& value) const;
+
+ template
+ T* getTypedItemByValue(const LLSD& value) const
+ {
+ return dynamic_cast(getItemByValue(value));
+ }
+
+ /**
+ * Select or deselect specified item based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItem(LLPanel* item, bool select = true);
+
+ /**
+ * Select or deselect an item by associated value based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByValue(const LLSD& value, bool select = true);
+
+ /**
+ * Select or deselect an item by associated uuid based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByUUID(const LLUUID& uuid, bool select = true);
+
+ /**
+ * Get all panels stored in the list.
+ */
+ virtual void getItems(std::vector& items) const;
+
+ /**
+ * Get all items values.
+ */
+ virtual void getValues(std::vector& values) const;
+
+ /**
+ * Get LLSD associated with the first selected item
+ */
+ virtual LLSD getSelectedValue() const;
+
+ /**
+ * Get LLSD's associated with selected items.
+ * @param selected_values std::vector being populated with LLSD associated with selected items
+ */
+ virtual void getSelectedValues(std::vector& selected_values) const;
+
+
+ /**
+ * Get LLUUID associated with selected item
+ * @return LLUUID if such was associated with selected item
+ */
+ virtual LLUUID getSelectedUUID() const;
+
+ /**
+ * Get LLUUIDs associated with selected items
+ * @param selected_uuids An std::vector being populated with LLUUIDs associated with selected items
+ */
+ virtual void getSelectedUUIDs(uuid_vec_t& selected_uuids) const;
+
+ /** Get the top selected item */
+ virtual LLPanel* getSelectedItem() const;
+
+ /**
+ * Get selected items
+ * @param selected_items An std::vector being populated with pointers to selected items
+ */
+ virtual void getSelectedItems(std::vector& selected_items) const;
+
+
+ /**
+ * Resets selection of items.
+ *
+ * It calls onCommit callback if setCommitOnSelectionChange(bool b) was called with "true"
+ * argument for current Flat List.
+ * @param no_commit_on_deselection - if true onCommit callback will not be called
+ */
+ virtual void resetSelection(bool no_commit_on_deselection = false);
+
+ /**
+ * Sets comment text which will be shown in the list is it is empty.
+ *
+ * Textbox to hold passed text is created while this method is called at the first time.
+ *
+ * @param comment_text - string to be shown as a comment.
+ */
+ void setNoItemsCommentText( const std::string& comment_text);
+
+ /** Turn on/off multiple selection support */
+ void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; }
+
+ /** Turn on/off selection support */
+ void setAllowSelection(bool can_select) { mAllowSelection = can_select; }
+
+ /** Sets flag whether onCommit should be fired if selection was changed */
+ // FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly.
+ void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
+
+ /** Get number of selected items in the list */
+ U32 numSelected() const {return mSelectedItemPairs.size(); }
+
+ /** Get number of (visible) items in the list */
+ U32 size(const bool only_visible_items = true) const;
+
+ /** Removes all items from the list */
+ void clear() override;
+
+ /**
+ * Removes all items that can be detached from the list but doesn't destroy
+ * them, caller responsible to manage items after they are detached.
+ * Detachable item should accept "detach" action via notify() method,
+ * where it disconnect all callbacks, does other valuable routines and
+ * return 1.
+ */
+ void detachItems(std::vector& detached_items);
+
+ /**
+ * Set comparator to use for future sorts.
+ *
+ * This class does NOT manage lifetime of the comparator
+ * but assumes that the comparator is always alive.
+ */
+ void setComparator(const ItemComparator* comp) { mItemComparator = comp; }
+ void sort();
+
+ bool updateValue(const LLSD& old_value, const LLSD& new_value);
+
+ void scrollToShowFirstSelectedItem();
+
+ void selectFirstItem ();
+ void selectLastItem ();
+
+ S32 notify(const LLSD& info) override;
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); // Old-style
+
+ virtual ~LLFlatListView();
+protected:
+
+ /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */
+ typedef std::pair item_pair_t;
+
+ typedef std::list pairs_list_t;
+ typedef pairs_list_t::iterator pairs_iterator_t;
+ typedef pairs_list_t::const_iterator pairs_const_iterator_t;
+
+ /** An adapter for a ItemComparator */
+ struct ComparatorAdaptor
+ {
+ ComparatorAdaptor(const ItemComparator& comparator) : mComparator(comparator) {};
+
+ bool operator()(const item_pair_t* item_pair1, const item_pair_t* item_pair2) const
+ {
+ return mComparator.compare(item_pair1->first, item_pair2->first);
+ }
+
+ const ItemComparator& mComparator;
+ };
+
+
+ friend class LLUICtrlFactory;
+ LLFlatListView(const std::string& name, const LLRect& rect, bool opaque, const LLColor4& color, const S32& item_pad, bool allow_select, bool multi_select, bool keep_one_selected, bool keep_selection_visible_on_reshape, const std::string& no_items_text);
+
+ /** Manage selection on mouse events */
+ void onItemMouseClick(item_pair_t* item_pair, MASK mask);
+
+ void onItemRightMouseClick(item_pair_t* item_pair, MASK mask);
+
+ /**
+ * Updates position of items.
+ * It does not take into account invisible items.
+ */
+ virtual void rearrangeItems();
+
+ virtual item_pair_t* getItemPair(LLPanel* item) const;
+
+ virtual item_pair_t* getItemPair(const LLSD& value) const;
+
+ virtual bool selectItemPair(item_pair_t* item_pair, bool select);
+
+ virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection);
+
+ BOOL canSelectAll() const override;
+ void selectAll() override;
+
+ virtual bool isSelected(item_pair_t* item_pair) const;
+
+ virtual bool removeItemPair(item_pair_t* item_pair, bool rearrange);
+
+ bool addItemPairs(pairs_list_t panel_list, bool rearrange = true);
+
+ /**
+ * Notify parent about changed size of internal controls with "size_changes" action
+ *
+ * Size includes Items Rect width and either Items Rect height or comment text height.
+ * Comment text height is included if comment text is set and visible.
+ * List border size is also included into notified size.
+ */
+ void notifyParentItemsRectChanged();
+
+ BOOL handleKeyHere(KEY key, MASK mask) override;
+
+ BOOL postBuild() override;
+
+ void onFocusReceived() override;
+
+ void onFocusLost() override;
+
+ void draw() override;
+
+ LLRect getLastSelectedItemRect();
+
+ void ensureSelectedVisible();
+
+private:
+
+ void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;}
+
+ void setNoItemsCommentVisible(bool visible) const;
+
+protected:
+
+ /** Comparator to use when sorting the list. */
+ const ItemComparator* mItemComparator;
+
+
+private:
+
+ LLPanel* mItemsPanel;
+
+ S32 mItemsNoScrollWidth;
+
+ S32 mBorderThickness;
+
+ /** Items padding */
+ S32 mItemPad;
+
+ /** Selection support flag */
+ bool mAllowSelection;
+
+ /** Multiselection support flag, ignored if selection is not supported */
+ bool mMultipleSelection;
+
+ /**
+ * Flag specified whether onCommit be called if selection is changed in the list.
+ *
+ * Can be ignored in the resetSelection() method.
+ * @see resetSelection()
+ */
+ bool mCommitOnSelectionChange;
+
+ bool mKeepOneItemSelected;
+
+ bool mIsConsecutiveSelection;
+
+ bool mKeepSelectionVisibleOnReshape;
+
+ /** All pairs of the list */
+ pairs_list_t mItemPairs;
+
+ /** Selected pairs for faster access */
+ pairs_list_t mSelectedItemPairs;
+
+ /**
+ * Rectangle contained previous size of items parent notified last time.
+ * Is used to reduce amount of parentNotify() calls if size was not changed.
+ */
+ LLRect mPrevNotifyParentRect;
+
+ LLTextBox* mNoItemsCommentTextbox;
+
+ LLViewBorder* mSelectedItemsBorder;
+
+ commit_signal_t mOnReturnSignal;
+};
+
+/**
+ * Extends LLFlatListView functionality to show different messages when there are no items in the
+ * list depend on whether they are filtered or not.
+ *
+ * Class provides one message per case of empty list.
+ * It also provides protected updateNoItemsMessage() method to be called each time when derived list
+ * is changed to update base mNoItemsCommentTextbox value.
+ *
+ * It is implemented to avoid duplication of this functionality in concrete implementations of the
+ * lists. It is intended to be used as a base class for lists which should support two different
+ * messages for empty state. Can be improved to support more than two messages via state-to-message map.
+ */
+class LLFlatListViewEx : public LLFlatListView
+{
+public:
+ LOG_CLASS(LLFlatListViewEx);
+
+ struct Params : public LLInitParam::Block
+ {
+ /**
+ * Contains a message for empty list when it does not contain any items at all.
+ */
+ Optional no_items_msg;
+
+ /**
+ * Contains a message for empty list when its items are removed by filtering.
+ */
+ Optional no_filtered_items_msg;
+ Params();
+ };
+
+ // *WORKAROUND: two methods to overload appropriate Params due to localization issue:
+ // no_items_msg & no_filtered_items_msg attributes are not defined as translatable in VLT. See EXT-5931
+ void setNoItemsMsg(const std::string& msg) { mNoItemsMsg = msg; }
+ void setNoFilteredItemsMsg(const std::string& msg) { mNoFilteredItemsMsg = msg; }
+
+ bool getForceShowingUnmatchedItems();
+
+ void setForceShowingUnmatchedItems(bool show);
+
+ /**
+ * Sets up new filter string and filters the list.
+ */
+ void setFilterSubString(const std::string& filter_str);
+ std::string getFilterSubString() const { return mFilterSubString; }
+
+ /**
+ * Filters the list, rearranges and notifies parent about shape changes.
+ * Derived classes may want to overload rearrangeItems() to exclude repeated separators after filtration.
+ */
+ void filterItems();
+
+ /**
+ * Returns true if last call of filterItems() found at least one matching item
+ */
+ bool hasMatchedItems();
+
+protected:
+ LLFlatListViewEx(const Params& p);
+
+ /**
+ * Applies a message for empty list depend on passed argument.
+ *
+ * @param filter_string - if is not empty, message for filtered items will be set, otherwise for
+ * completely empty list. Value of filter string will be passed as search_term in SLURL.
+ */
+ void updateNoItemsMessage(const std::string& filter_string);
+
+ /**
+ * Applies visibility acording to action and LLFlatListView settings.
+ *
+ * @param item - item we are changing
+ * @param item - action - parameters to determin visibility from
+ */
+ void updateItemVisibility(LLPanel* item, const LLSD &action);
+
+private:
+ std::string mNoFilteredItemsMsg;
+ std::string mNoItemsMsg;
+ std::string mFilterSubString;
+ /**
+ * Show list items that don't match current filter
+ */
+ bool mForceShowingUnmatchedItems;
+ /**
+ * True if last call of filterItems() found at least one matching item
+ */
+ bool mHasMatchedItems;
+};
+
+#endif
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 1f851e2dd..dd2954c11 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -181,7 +181,6 @@ LLLineEditor::LLLineEditor(const std::string& name, const LLRect& rect,
// make the popup menu available
LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_texteditor.xml", LLMenuGL::sMenuContainer);
- menu->addSeparator();
//menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor"));
menu->setCanTearOff(FALSE);
menu->setVisible(FALSE);
@@ -1120,7 +1119,7 @@ BOOL LLLineEditor::canCopy() const
// copy selection to clipboard
-void LLLineEditor::copy()
+void LLLineEditor::copy() const
{
if( canCopy() )
{
@@ -3006,54 +3005,47 @@ void LLLineEditor::showContextMenu(S32 x, S32 y)
{
gEditMenuHandler = this;
-
- LLMenuGL* menu = (LLMenuGL*)mContextMenuHandle.get();
- if (menu)
+ if(menu->isOpen())
{
- if(menu->isOpen())
- {
- menu->setVisible(FALSE);
- }
-
- // spell_check="true" in xui
- if (!mReadOnly && mSpellCheckable)
- {
- // search for word matches
- S32 wordStart = 0;
- S32 wordLen = 0;
- S32 pos = calculateCursorFromMouse(x);
- if (getWordBoundriesAt(pos, &wordStart, &wordLen))
- {
- const auto selectedWord = wstring_to_utf8str(getWText().substr(wordStart, wordLen));
-
- if (!glggHunSpell->isSpelledRight(selectedWord))
- {
- //misspelled word here, and you have just right clicked on it!
-
- for (const auto& word : glggHunSpell->getSuggestionList(selectedWord))
- {
- menu->addChild(new LLMenuItemCallGL(word, spell_correct, nullptr, this));
- }
- menu->addChild(new LLMenuItemCallGL("Add Word", spell_add, nullptr, this));
- }
- }
-
- const std::string showstr("Show Misspellings"), hidestr("Hide Misspellings");
- bool show = !glggHunSpell->getSpellCheckHighlight();
- auto word = show ? showstr : hidestr;
- if (!menu->hasChild(word))
- {
- menu->addChild(new LLMenuItemCallGL(word, spell_show, nullptr, show ? &show : nullptr));
- if (auto child = menu->getChild(show ? hidestr : showstr, false, false))
- menu->removeChild(child);
- }
- }
-
- mLastContextMenuX = x;
- menu->buildDrawLabels();
- menu->updateParent(LLMenuGL::sMenuContainer);
- LLMenuGL::showPopup(this, menu, x, y);
+ menu->setVisible(FALSE);
}
+
+ // spell_check="true" in xui
+ if (!mReadOnly && mSpellCheckable)
+ {
+ constexpr auto spell_sep = "Spell Check Sep";
+ // Remove everything after the separator if we added it, because menus don't autodie yet.
+ menu->erase(menu->find(menu->findChild(spell_sep)), menu->end());
+ menu->addSeparator(spell_sep);
+
+ // search for word matches
+ S32 wordStart = 0;
+ S32 wordLen = 0;
+ S32 pos = calculateCursorFromMouse(x);
+ if (getWordBoundriesAt(pos, &wordStart, &wordLen))
+ {
+ const auto selectedWord = wstring_to_utf8str(getWText().substr(wordStart, wordLen));
+
+ if (!glggHunSpell->isSpelledRight(selectedWord))
+ {
+ //misspelled word here, and you have just right clicked on it!
+
+ for (const auto& word : glggHunSpell->getSuggestionList(selectedWord))
+ {
+ menu->addChild(new LLMenuItemCallGL(word, spell_correct, nullptr, this));
+ }
+ menu->addChild(new LLMenuItemCallGL("Add Word", spell_add, nullptr, this));
+ }
+ }
+
+ bool show = !glggHunSpell->getSpellCheckHighlight();
+ menu->addChild(new LLMenuItemCallGL(show ? "Show Misspellings" : "Hide Misspellings", spell_show, nullptr, show ? menu : nullptr));
+ }
+
+ mLastContextMenuX = x;
+ menu->buildDrawLabels();
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ LLMenuGL::showPopup(this, menu, x, y);
}
}
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 440407cd8..ec002a452 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -107,7 +107,7 @@ public:
virtual void cut();
virtual BOOL canCut() const;
- virtual void copy();
+ void copy() const override final;
virtual BOOL canCopy() const;
virtual void paste();
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 7b9bbe372..d7175b9aa 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -692,16 +692,17 @@ BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask)
// This class represents a vertical separator.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLMenuItemVerticalSeparatorGL
+class LLMenuItemVerticalSeparatorGL final
: public LLMenuItemSeparatorGL
{
public:
- LLMenuItemVerticalSeparatorGL( void );
+ LLMenuItemVerticalSeparatorGL(const std::string& name = LLStringUtil::null);
BOOL handleMouseDown(S32 x, S32 y, MASK mask) override { return FALSE; }
};
-LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
+LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL(const std::string& name)
+: LLMenuItemSeparatorGL(name)
{
setLabel( VERTICAL_SEPARATOR_LABEL );
}
@@ -826,15 +827,15 @@ U32 LLMenuItemTearOffGL::getNominalHeight( void ) const
// This class represents a blank, non-functioning item.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLMenuItemBlankGL : public LLMenuItemGL
+class LLMenuItemBlankGL final : public LLMenuItemGL
{
public:
- LLMenuItemBlankGL( void ) : LLMenuItemGL( LLStringUtil::null, LLStringUtil::null )
+ LLMenuItemBlankGL(const std::string& name = LLStringUtil::null) : LLMenuItemGL(name, LLStringUtil::null)
{
setEnabled(FALSE);
}
- virtual void onCommit( void ) {}
- virtual void draw( void ) {}
+ void onCommit() override {}
+ void draw() override {}
};
@@ -3166,12 +3167,7 @@ void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/)
item_list_t::iterator end_position = mItems.begin();
std::advance(end_position, end);
- for (item_list_t::iterator position_iter = start_position; position_iter != end_position; position_iter++)
- {
- LLUICtrl::removeChild(*position_iter);
- }
-
- mItems.erase(start_position, end_position);
+ erase(start_position, end_position);
if (arrange)
{
@@ -3193,7 +3189,7 @@ void LLMenuGL::insert(S32 position, LLView* ctrl, bool arrange /*= true*/)
std::advance(position_iter, position);
insert(position_iter, item, arrange);
}
-void LLMenuGL::insert(item_list_t::iterator position_iter, LLMenuItemGL* item, bool arrange /*= true*/)
+void LLMenuGL::insert(item_list_t::const_iterator position_iter, LLMenuItemGL* item, bool arrange /*= true*/)
{
mItems.insert(position_iter, item);
LLUICtrl::addChild(item);
@@ -3243,9 +3239,9 @@ BOOL LLMenuGL::append( LLMenuItemGL* item )
}
// add a separator to this menu
-BOOL LLMenuGL::addSeparator()
+BOOL LLMenuGL::addSeparator(const std::string& name)
{
- LLMenuItemGL* separator = new LLMenuItemSeparatorGL();
+ LLMenuItemGL* separator = new LLMenuItemSeparatorGL(name);
return addChild(separator);
}
@@ -3332,11 +3328,6 @@ void LLMenuGL::setTornOff(BOOL torn_off)
mTornOff = torn_off;
}
-U32 LLMenuGL::getItemCount()
-{
- return mItems.size();
-}
-
LLMenuItemGL* LLMenuGL::getItem(S32 number)
{
if (number >= 0 && number < (S32)mItems.size())
@@ -4197,10 +4188,10 @@ S32 LLMenuBarGL::getRightmostMenuEdge()
}
// add a vertical separator to this menu
-BOOL LLMenuBarGL::addSeparator()
+BOOL LLMenuBarGL::addSeparator(const std::string& name)
{
- LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
- return append( separator );
+ LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL(name);
+ return append(separator);
}
// add a menu - this will create a drop down menu.
@@ -5361,9 +5352,9 @@ BOOL LLPieMenu::append(LLMenuItemGL *item)
}
// virtual
-BOOL LLPieMenu::addSeparator()
+BOOL LLPieMenu::addSeparator(const std::string& name)
{
- return append( new LLMenuItemBlankGL() );
+ return append(new LLMenuItemBlankGL(name));
}
// virtual
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 45e21e18e..d63b294d8 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -490,7 +490,7 @@ public:
void setCanTearOff(BOOL tear_off);
// add a separator to this menu
- virtual BOOL addSeparator();
+ virtual BOOL addSeparator(const std::string& name = LLStringUtil::null);
// for branching menu items, bring sub menus up to root level of menu hierarchy
virtual void updateParent( LLView* parentp );
@@ -527,16 +527,31 @@ public:
// erase group of items from menu
void erase(S32 begin, S32 end, bool arrange = true);
+ typedef std::list item_list_t;
+ inline item_list_t::iterator erase(item_list_t::const_iterator first, item_list_t::const_iterator last)
+ {
+ for (auto it = first; it != last; ++it)
+ LLUICtrl::removeChild(*it);
+ return mItems.erase(first, last);
+ }
// add new item at position
void insert(S32 begin, LLView* ctrl, bool arrange = true);
- void insert(std::list::iterator position_iter, LLMenuItemGL* item, bool arrange = true);
+ void insert(item_list_t::const_iterator position_iter, LLMenuItemGL* item, bool arrange = true);
// find an item's position
- std::list::iterator find(LLMenuItemGL* item) { return std::find(mItems.begin(), mItems.end(), item); }
+ item_list_t::const_iterator find(LLMenuItemGL* item) const { return std::find(mItems.begin(), mItems.end(), item); }
+
+ // end of items, for use with other members that return an iterator
+ item_list_t::const_iterator end() const { return mItems.cend(); }
+
+ // get list of items
+ const item_list_t& getItems() const { return mItems; }
+
+ // number of menu items
+ item_list_t::size_type getItemCount() const { return mItems.size(); }
void setItemLastSelected(LLMenuItemGL* item); // must be in menu
- U32 getItemCount(); // number of menu items
LLMenuItemGL* getItem(S32 number); // 0 = first item
LLMenuItemGL* getHighlightedItem();
@@ -591,9 +606,8 @@ public:
virtual BOOL appendMenu( LLMenuGL* menu );
protected:
- // TODO: create accessor methods for these?
- typedef std::list< LLMenuItemGL* > item_list_t;
item_list_t mItems;
+ // TODO: create accessor methods for these?
LLMenuItemGL*mFirstVisibleItem;
LLMenuItemGL *mArrowUpItem, *mArrowDownItem;
@@ -764,7 +778,7 @@ public:
private:
virtual BOOL append(LLMenuItemGL* item);
public:
- virtual BOOL addSeparator();
+ virtual BOOL addSeparator(const std::string& name = LLStringUtil::null) override final;
virtual void arrange( void );
@@ -844,7 +858,7 @@ public:
/*virtual*/ BOOL jumpKeysActive();
// add a vertical separator to this menu
- virtual BOOL addSeparator();
+ virtual BOOL addSeparator(const std::string& name = LLStringUtil::null) override final;
// LLView Functionality
virtual BOOL handleHover( S32 x, S32 y, MASK mask );
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
index bf1693876..96912a0f5 100644
--- a/indra/llui/llscrollbar.cpp
+++ b/indra/llui/llscrollbar.cpp
@@ -2,31 +2,25 @@
* @file llscrollbar.cpp
* @brief Scrollbar UI widget
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -46,6 +40,7 @@
#include "llwindow.h"
#include "llcontrol.h"
#include "llrender.h"
+#include "lluictrlfactory.h"
LLScrollbar::LLScrollbar(
const std::string& name, LLRect rect,
@@ -402,7 +397,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
}
getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
handled = TRUE;
}
else
@@ -414,7 +409,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
if( !handled )
{
getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
handled = TRUE;
}
@@ -468,6 +463,13 @@ BOOL LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask)
return handled;
}
+BOOL LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // just treat a double click as a second click
+ return handleMouseDown(x, y, mask);
+}
+
+
void LLScrollbar::reshape(S32 width, S32 height, BOOL called_from_parent)
{
if (width == getRect().getWidth() && height == getRect().getHeight()) return;
@@ -509,7 +511,6 @@ void LLScrollbar::draw()
mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
}
-
// Draw background and thumb.
LLUIImage* rounded_rect_imagep = LLUI::getUIImage("Rounded_Square");
@@ -525,6 +526,9 @@ void LLScrollbar::draw()
}
else
{
+ // Thumb
+ LLRect outline_rect = mThumbRect;
+ outline_rect.stretch(2);
// Background
rounded_rect_imagep->drawSolid(mOrientation == HORIZONTAL ? SCROLLBAR_SIZE : 0,
mOrientation == VERTICAL ? SCROLLBAR_SIZE : 0,
@@ -532,9 +536,6 @@ void LLScrollbar::draw()
mOrientation == VERTICAL ? getRect().getHeight() - 2 * SCROLLBAR_SIZE : getRect().getHeight(),
mTrackColor);
- // Thumb
- LLRect outline_rect = mThumbRect;
- outline_rect.stretch(2);
if (gFocusMgr.getKeyboardFocus() == this)
{
@@ -636,3 +637,8 @@ void LLScrollbar::onLineDownBtnPressed( const LLSD& data )
{
changeLine( mStepSize, TRUE );
}
+
+void LLScrollbar::setThickness(S32 thickness)
+{
+ mThickness = thickness < 0 ? SCROLLBAR_SIZE : thickness;
+}
diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h
index fab34326c..2482cce2f 100644
--- a/indra/llui/llscrollbar.h
+++ b/indra/llui/llscrollbar.h
@@ -2,31 +2,25 @@
* @file llscrollbar.h
* @brief Scrollbar UI widget
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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$
*/
@@ -36,6 +30,7 @@
#include "stdtypes.h"
#include "lluictrl.h"
#include "v4color.h"
+#include "llbutton.h"
//
// Constants
@@ -59,6 +54,7 @@ public:
callback_t change_callback,
S32 step_size = 1);
+public:
virtual ~LLScrollbar();
virtual void setValue(const LLSD& value);
@@ -67,6 +63,7 @@ public:
virtual BOOL handleKeyHere(KEY key, MASK mask);
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
@@ -104,6 +101,9 @@ public:
void onLineUpBtnPressed(const LLSD& data);
void onLineDownBtnPressed(const LLSD& data);
+ S32 getThickness() const { return mThickness; }
+ void setThickness(S32 thickness);
+
void setTrackColor( const LLColor4& color ) { mTrackColor = color; }
void setThumbColor( const LLColor4& color ) { mThumbColor = color; }
void setHighlightColor( const LLColor4& color ) { mHighlightColor = color; }
@@ -145,5 +145,4 @@ private:
};
-
#endif // LL_SCROLLBAR_H
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index eee41b491..778329d1d 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -218,6 +218,15 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
return FALSE;
}
+BOOL LLScrollContainer::handleUnicodeCharHere(llwchar uni_char)
+{
+ if (mScrolledView && mScrolledView->handleUnicodeCharHere(uni_char))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks )
{
// Give event to my child views - they may have scroll bars
@@ -262,7 +271,6 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
EAcceptance* accept,
std::string& tooltip_msg)
{
- //S32 scrollbar_size = SCROLLBAR_SIZE;
// Scroll folder view if needed. Never accepts a drag or drop.
*accept = ACCEPT_NO;
BOOL handled = autoScroll(x, y);
@@ -413,6 +421,7 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height
}
}
+
void LLScrollContainer::draw()
{
S32 scrollbar_size = SCROLLBAR_SIZE;
@@ -525,7 +534,7 @@ bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
void LLScrollContainer::updateScroll()
{
- if (!mScrolledView)
+ if (!getVisible() || !mScrolledView)
{
return;
}
@@ -626,6 +635,7 @@ LLRect LLScrollContainer::getVisibleContentRect()
visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom);
return visible_rect;
}
+
LLRect LLScrollContainer::getContentWindowRect()
{
updateScroll();
@@ -729,6 +739,13 @@ S32 LLScrollContainer::getBorderWidth() const
return 0;
}
+void LLScrollContainer::setSize(S32 size)
+{
+ mSize = size;
+ mScrollbar[VERTICAL]->setThickness(size);
+ mScrollbar[HORIZONTAL]->setThickness(size);
+}
+
// virtual
LLXMLNodePtr LLScrollContainer::getXML(bool save_children) const
{
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
index 8239789b3..c40b95cbe 100644
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -2,31 +2,25 @@
* @file llscrollcontainer.h
* @brief LLScrollContainer class header file.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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$
*/
@@ -78,7 +72,7 @@ public:
void setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; }
LLRect getVisibleContentRect();
LLRect getContentWindowRect();
- const LLRect& getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
+ virtual const LLRect& getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
void pageUp(S32 overlap = 0);
void pageDown(S32 overlap = 0);
void goToTop();
@@ -90,6 +84,7 @@ public:
// LLView functionality
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual BOOL handleKeyHere(KEY key, MASK mask);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char);
virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
@@ -105,9 +100,14 @@ public:
virtual LLXMLNodePtr getXML(bool save_children) const;
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ S32 getSize() const { return mSize; }
+ void setSize(S32 thickness);
+
+protected:
+ LLView* mScrolledView;
+
private:
void init();
-
// internal scrollbar handlers
virtual void scrollHorizontal( S32 new_pos );
virtual void scrollVertical( S32 new_pos );
@@ -117,7 +117,6 @@ public:
private:
LLScrollbar* mScrollbar[SCROLLBAR_COUNT];
- LLView* mScrolledView;
S32 mSize;
BOOL mIsOpaque;
LLColor4 mBackgroundColor;
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index f46775f6f..78c3c750f 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -2036,6 +2036,11 @@ void LLScrollListCtrl::setFilter(const std::string& filter)
adjustScrollbar(unfiltered_count);
}
+void LLScrollListCtrl::setContextMenu(const std::string& menu)
+{
+ setContextMenu(LLUICtrlFactory::instance().buildMenu(menu, LLMenuGL::sMenuContainer));
+}
+
BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
{
@@ -2695,51 +2700,32 @@ void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node)
if (node->hasAttribute("menu_num"))
{
- // Some scroll lists use common menus identified by number
- // 0 is menu_avs_list.xml, 1 will be for groups, 2 could be for lists of objects
+ // Some UI uses common menus identified by number
+ // 0 is avatars, 1 will be for groups, others could be for lists of objects or locations or experiences
S32 menu_num;
node->getAttributeS32("menu_num", menu_num);
- setContextMenu(menu_num);
+ mPopupMenu = sMenus[menu_num];
}
else if (node->hasAttribute("menu_file"))
{
- std::string menu_file;
- node->getAttributeString("menu_file", menu_file);
- mPopupMenu = LLUICtrlFactory::getInstance()->buildMenu(menu_file, LLMenuGL::sMenuContainer);
+ std::string menu;
+ node->getAttributeString("menu_file", menu);
+ if (!menu.empty()) setContextMenu(menu);
}
-}
-
-// static
-LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
-{
- LLRect rect;
- createRect(node, rect, parent, LLRect());
-
- BOOL multi_select = false;
- node->getAttributeBOOL("multi_select", multi_select);
- BOOL draw_border = true;
- node->getAttributeBOOL("draw_border", draw_border);
- BOOL draw_heading = false;
- node->getAttributeBOOL("draw_heading", draw_heading);
- S32 search_column = 0;
- node->getAttributeS32("search_column", search_column);
- S32 sort_column = -1;
- node->getAttributeS32("sort_column", sort_column);
- BOOL sort_ascending = true;
- node->getAttributeBOOL("sort_ascending", sort_ascending);
-
- LLScrollListCtrl* scroll_list = new LLScrollListCtrl("scroll_list", rect, NULL, multi_select, draw_border, draw_heading);
if (node->hasAttribute("heading_height"))
{
S32 heading_height;
node->getAttributeS32("heading_height", heading_height);
- scroll_list->setHeadingHeight(heading_height);
+ setHeadingHeight(heading_height);
}
- scroll_list->setScrollListParameters(node);
- scroll_list->initFromXML(node, parent);
- scroll_list->setSearchColumn(search_column);
+ S32 search_column = 0;
+ node->getAttributeS32("search_column", search_column);
+ BOOL sort_ascending = true;
+ node->getAttributeBOOL("sort_ascending", sort_ascending);
+
+ setSearchColumn(search_column);
LLSD columns;
S32 index = 0;
@@ -2750,7 +2736,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac
{
if (child->hasName("column") || child->hasName("columns") || child->hasName(kidcolumn) || child->hasName(kidcolumns))
{
- std::string labelname("");
+ std::string labelname;
if (child->getAttributeString("label", labelname))
columns[index]["label"] = labelname;
else if (child->getAttributeString("image", labelname))
@@ -2773,9 +2759,9 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac
}
else // Singu Note: if a scroll list does not provide sort_direction, provide sort_ascending to sort as expected
{
- bool sort_ascending = true;
- child->getAttribute_bool("sort_ascending", sort_ascending);
- columns[index]["sort_ascending"] = sort_ascending;
+ bool col_sort_ascending = sort_ascending;
+ child->getAttribute_bool("sort_ascending", col_sort_ascending);
+ columns[index]["sort_ascending"] = col_sort_ascending;
}
S32 columnwidth = -1;
@@ -2801,12 +2787,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac
++index;
}
}
- scroll_list->setColumnHeadings(columns);
-
- if (sort_column >= 0)
- {
- scroll_list->sortByColumnIndex(sort_column, sort_ascending);
- }
+ setColumnHeadings(columns);
const std::string kidrow(nodename + "row");
const std::string kidrows(nodename + "rows");
@@ -2851,25 +2832,53 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac
}
}
if(explicit_column)
- scroll_list->addElement(row);
+ addElement(row);
else
{
LLSD entry_id;
if(id_found)
entry_id = id;
- scroll_list->addSimpleElement(value,ADD_BOTTOM,entry_id);
+ addSimpleElement(value,ADD_BOTTOM,entry_id);
}
}
}
- scroll_list->setCommentText(node->getTextContents());
+ S32 sort_column = -1;
+ node->getAttributeS32("sort_column", sort_column);
+ if (sort_column >= 0)
+ {
+ sortByColumnIndex(sort_column, sort_ascending);
+ }
+
+ setCommentText(node->getTextContents());
+}
+
+// static
+LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL multi_select = false;
+ node->getAttributeBOOL("multi_select", multi_select);
+
+ BOOL draw_border = true;
+ node->getAttributeBOOL("draw_border", draw_border);
+
+ BOOL draw_heading = false;
+ node->getAttributeBOOL("draw_heading", draw_heading);
+
+ LLScrollListCtrl* scroll_list = new LLScrollListCtrl("scroll_list", rect, NULL, multi_select, draw_border, draw_heading);
+
+ scroll_list->setScrollListParameters(node);
+ scroll_list->initFromXML(node, parent);
return scroll_list;
}
// LLEditMenuHandler functions
// virtual
-void LLScrollListCtrl::copy()
+void LLScrollListCtrl::copy() const
{
std::string buffer;
for (auto item : getAllSelected())
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index c09af350a..c1dcf8eed 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -202,7 +202,7 @@ public:
virtual S32 getFirstSelectedIndex() const;
std::vector getAllSelected() const;
uuid_vec_t getSelectedIDs() const override final; //Helper. Much like getAllSelected, but just provides a LLUUID vec
- S32 getNumSelected() const override final;
+ S32 getNumSelected() const;
LLScrollListItem* getLastSelectedItem() const { return mLastSelected; }
// iterate over all items
@@ -254,9 +254,18 @@ public:
bool filterItem(LLScrollListItem* item);
void setFilter(const std::string& filter);
- // support right-click context menus for avatar/group lists
+ // Context Menus
void setContextMenu(LLMenuGL* menu) { mPopupMenu = menu; }
- void setContextMenu(S32 index) { mPopupMenu = sMenus[index]; }
+ void setContextMenu(U8 index) { mPopupMenu = sMenus[index]; }
+ void setContextMenu(const std::string& menu);
+
+ Type getSelectedType() const override
+ {
+ for (auto i = 0; mPopupMenu && i < COUNT; ++i)
+ if (sMenus[i] == mPopupMenu)
+ return (Type)i;
+ return LFIDBearer::getSelectedType();
+ }
// Overridden from LLView
/*virtual*/ void draw();
@@ -313,7 +322,7 @@ public:
virtual void scrollToShowSelected();
// LLEditMenuHandler functions
- virtual void copy();
+ void copy() const override final;
virtual BOOL canCopy() const;
virtual void cut();
virtual BOOL canCut() const;
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index a9eb63a40..6de751197 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -1896,3 +1896,8 @@ void LLTabContainer::commitHoveredButton(S32 x, S32 y)
}
}
+S32 LLTabContainer::getTotalTabWidth() const
+{
+ return mTotalTabWidth;
+}
+
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index de5a51639..311e14b30 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -98,6 +98,7 @@ public:
S32 getIndexForPanel(LLPanel* panel);
S32 getPanelIndexByTitle(const std::string& title);
LLPanel* getPanelByName(const std::string& name);
+ S32 getTotalTabWidth() const;
void setCurrentTabName(const std::string& name);
void selectFirstTab();
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 25400455b..b0001df24 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -244,8 +244,6 @@ private:
///////////////////////////////////////////////////////////////////
-LLTextEditor::is_friend_signal_t* LLTextEditor::mIsFriendSignal = nullptr;
-LLTextEditor::is_blocked_signal_t* LLTextEditor::mIsObjectBlockedSignal = nullptr;
LLTextEditor::LLTextEditor(
const std::string& name,
@@ -372,36 +370,46 @@ LLTextEditor::~LLTextEditor()
menu->die();
mPopupMenuHandle.markDead();
}
- /* Singu Note: Static this, we'll use it wherever we can!
- delete mIsFriendSignal;
- delete mIsObjectBlockedSignal;*/
}
const std::string& LLTextEditor::getMenuSegmentUrl() const
{
auto segment = getSegmentAtLocalPos(mLastContextMenuX, mLastContextMenuY);
- auto style = segment->getStyle();
+ auto style = segment ? segment->getStyle() : nullptr;
return style ? style->getLinkHREF() : LLStringUtil::null;
}
-static LLTextEditor* get_focused_text_editor()
+static LFIDBearer::Type get_type_from_url(const std::string& url)
{
- auto* te =
-#ifdef SHOW_ASSERT
- dynamic_cast
-#else
- static_cast
-#endif
- (gFocusMgr.getKeyboardFocus());
- llassert(te); // This listener only applies to text editors
- return te;
+ auto pos = url.find("/app/");
+ if (pos != std::string::npos && pos + 10 <= url.size())
+ {
+ auto type = url.substr(pos + 5, 5);
+ return type == "agent" ? LFIDBearer::AVATAR
+ : type == "group" ? LFIDBearer::GROUP
+ : type == "experience" ? LFIDBearer::EXPERIENCE
+ : LFIDBearer::OBJECT;
+ }
+ return LFIDBearer::NONE;
+}
+
+LLUUID LLTextEditor::getStringUUIDSelectedItem() const
+{
+ const auto& url = getMenuSegmentUrl();
+ const auto& type = get_type_from_url(url);
+ return type == LFIDBearer::NONE ? LLUUID::null : LLUUID(type == OBJECT ? LLUrlAction::getObjectId(url) : LLUrlAction::getUserID(url));
+}
+
+LFIDBearer::Type LLTextEditor::getSelectedType() const
+{
+ return get_type_from_url(getMenuSegmentUrl());
}
class CopyRawText : public LLMemberListener
{
bool handleEvent(LLPointer, const LLSD& userdata) override
{
- get_focused_text_editor()->copyRaw();
+ LFIDBearer::getActive()->copyRaw();
return true;
}
};
@@ -415,16 +423,11 @@ class TextEditorVisible : public LLMemberListener
}
};
-static const std::string& get_focused_url()
-{
- return get_focused_text_editor()->getMenuSegmentUrl();
-}
-
class ContextUrl : public LLMemberListener
{
bool handleEvent(LLPointer, const LLSD& userdata) override
{
- const auto& url = get_focused_url();
+ const auto& url = LFIDBearer::getActive()->getMenuSegmentUrl();
const auto& op = userdata.asStringRef();
if (op == "Open") LLUrlAction::openURL(url);
else if (op == "OpenInternal") LLUrlAction::openURLInternal(url);
@@ -433,10 +436,6 @@ class ContextUrl : public LLMemberListener
else if (op == "Block") LLUrlAction::blockObject(url);
else if (op == "Unblock") LLUrlAction::unblockObject(url);
else if (op == "Teleport") LLUrlAction::teleportToLocation(url);
- else if (op == "ShowProfile") LLUrlAction::showProfile(url);
- else if (op == "AddFriend") LLUrlAction::addFriend(url);
- else if (op == "RemoveFriend") LLUrlAction::removeFriend(url);
- else if (op == "SendIM") LLUrlAction::sendIM(url);
else if (op == "ShowOnMap") LLUrlAction::showLocationOnMap(url);
else if (op == "CopyLabel") LLUrlAction::copyLabelToClipboard(url);
else if (op == "CopyUrl") LLUrlAction::copyURLToClipboard(url);
@@ -444,67 +443,15 @@ class ContextUrl : public LLMemberListener
}
};
-class ContextIDUrl : public LLMemberListener
-{
-protected:
- std::string getID(const std::string& type) const
- {
- const auto& url = get_focused_url();
- // Empty works like avatar and group, "object" is an object (you needed to be told this)
- return type.empty() ? LLUrlAction::getUserID(url) : LLUrlAction::getObjectId(url);
- }
-};
-
-class ContextUrlCopy : public ContextIDUrl
+class ContextUrlCopy : public LLMemberListener
{
bool handleEvent(LLPointer, const LLSD& userdata) override
{
- LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(getID(userdata.asStringRef())));
+ LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(LFIDBearer::getActiveSelectedID().asString()));
return true;
}
};
-class ContextUrlExt : public ContextIDUrl
-{
- bool handleEvent(LLPointer, const LLSD& userdata) override
- {
- std::string cmd = userdata.asStringRef();
- std::string type;
- const auto sep = cmd.find(',');
- if (sep != std::string::npos)
- {
- type = cmd.substr(sep);
- cmd = cmd.substr(0, sep);
- }
- mExtCallback(cmd, LLUUID(getID(type)));
- return true;
- }
- LLTextEditor::ext_slurl_cb mExtCallback;
-public:
- ContextUrlExt(LLTextEditor::ext_slurl_cb cb) : mExtCallback(cb) {}
-};
-
-class ContextUrlExtVisible : public ContextIDUrl
-{
- bool handleEvent(LLPointer, const LLSD& userdata) override
- {
- std::string cmd = userdata["data"];
- std::string type;
- const auto sep = cmd.find(',');
- if (sep != std::string::npos)
- {
- type = cmd.substr(sep);
- cmd = cmd.substr(0, sep);
- }
-
- LLMenuGL::sMenuContainer->findControl(userdata["control"].asString())->setValue(mExtVCB(cmd, LLUUID(getID(type))));
- return true;
- }
- LLTextEditor::ext_slurl_visible_cb mExtVCB;
-public:
- ContextUrlExtVisible(LLTextEditor::ext_slurl_visible_cb vcb) : mExtVCB(vcb) {}
-};
-
void LLTextEditor::spell_correct(void* data)
{
@@ -581,14 +528,12 @@ void LLTextEditor::spell_add(void* data)
}
//static
-void LLTextEditor::addMenuListeners(ext_slurl_cb cb, ext_slurl_visible_cb vcb)
+void LLTextEditor::addMenuListeners()
{
(new CopyRawText)->registerListener(LLMenuGL::sMenuContainer, "CopyRawText");
(new TextEditorVisible)->registerListener(LLMenuGL::sMenuContainer, "TextEditorVisible");
(new ContextUrl)->registerListener(LLMenuGL::sMenuContainer, "Text.Url");
(new ContextUrlCopy)->registerListener(LLMenuGL::sMenuContainer, "Text.Url.CopyUUID");
- (new ContextUrlExt(cb))->registerListener(LLMenuGL::sMenuContainer, "Text.Url.Ext");
- (new ContextUrlExtVisible(vcb))->registerListener(LLMenuGL::sMenuContainer, "Text.Url.ExtVisible");
}
void LLTextEditor::setTrackColor( const LLColor4& color )
@@ -767,34 +712,6 @@ LLMenuGL* LLTextEditor::createUrlContextMenu(S32 x, S32 y, const std::string &in
// create and return the context menu from the XUI file
llassert(LLMenuGL::sMenuContainer != NULL);
auto menu = LLUICtrlFactory::instance().buildMenu(xui_file, LLMenuGL::sMenuContainer);
- if (menu)
- {
- if (mIsFriendSignal)
- {
- bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url)));
- LLView* addFriendButton = menu->findChild("add_friend");
- LLView* removeFriendButton = menu->findChild("remove_friend");
-
- if (addFriendButton && removeFriendButton)
- {
- addFriendButton->setVisible(!isFriend);
- removeFriendButton->setVisible(isFriend);
- }
- }
-
- if (mIsObjectBlockedSignal)
- {
- bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url));
- LLView* blockButton = menu->findChild("block_object");
- LLView* unblockButton = menu->findChild("unblock_object");
-
- if (blockButton && unblockButton)
- {
- blockButton->setVisible(!is_blocked);
- unblockButton->setVisible(is_blocked);
- }
- }
- }
return menu;
}
@@ -1575,9 +1492,7 @@ BOOL LLTextEditor::handleRightMouseDown( S32 x, S32 y, MASK mask )
mLastContextMenuX = x;
mLastContextMenuY = y;
- menu->buildDrawLabels();
- menu->updateParent(LLMenuGL::sMenuContainer);
- LLMenuGL::showPopup(this, menu, x, y);
+ showMenu(this, menu, x, y);
}
return TRUE;
}
@@ -2302,7 +2217,7 @@ BOOL LLTextEditor::canCopy() const
}
// copy selection to clipboard
-void LLTextEditor::copy(bool raw)
+void LLTextEditor::copy(bool raw) const
{
if( !canCopy() )
{
@@ -4295,6 +4210,12 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s
};
auto append_link = [&](const std::string& link, LLStyleSP link_style)
{
+ if (!link_style->isLink())
+ {
+ appendAndHighlightText(link, part, style);
+ return;
+ }
+
if (style) // Respect styling
{
const auto& text_style = *style;
@@ -4308,8 +4229,9 @@ void LLTextEditor::appendTextImpl(const std::string &new_text, const LLStyleSP s
if (always_underline) link_style->mUnderline = true;
appendAndHighlightText(link, part, link_style, !always_underline/*match.underlineOnHoverOnly()*/);
};
- const auto&& cb = force_replace_links ? boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3) : LLUrlLabelCallback();
- while (!text.empty() && LLUrlRegistry::instance().findUrl(text, match, cb))
+ const auto&& cb = force_replace_links ? boost::bind(&LLTextEditor::replaceUrl, this, _1, _2, _3) : static_cast(LLUrlRegistryNullCallback);
+ auto& urlr = LLUrlRegistry::instance();
+ while (!text.empty() && urlr.findUrl(text, match, cb))
{
start = match.getStart();
end = match.getEnd()+1;
@@ -4432,7 +4354,7 @@ void LLTextEditor::appendAndHighlightText(const std::string& new_text, S32 highl
std::string::size_type start = 0;
/*std::string::size_type pos = new_text.find('\n',start);
- while(pos!=-1)
+ while(pos != std::string::npos)
{
if(pos!=start)
{
@@ -5173,24 +5095,6 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer )
return TRUE;
}
-boost::signals2::connection LLTextEditor::setIsFriendCallback(const is_friend_signal_t::slot_type& cb)
-{
- if (!mIsFriendSignal)
- {
- mIsFriendSignal = new is_friend_signal_t();
- }
- return mIsFriendSignal->connect(cb);
-}
-
-boost::signals2::connection LLTextEditor::setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb)
-{
- if (!mIsObjectBlockedSignal)
- {
- mIsObjectBlockedSignal = new is_blocked_signal_t();
- }
- return mIsObjectBlockedSignal->connect(cb);
-}
-
//////////////////////////////////////////////////////////////////////////
// LLTextSegment
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index a18907749..6ce91abb7 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -43,6 +43,7 @@
#include "lleditmenuhandler.h"
#include "llpreeditor.h"
+#include "lfidbearer.h"
class LLFontGL;
class LLKeywordToken;
@@ -50,6 +51,7 @@ class LLMenuGL;
class LLTextCmd;
class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor
+, public LFIDBearer
{
public:
//
@@ -71,16 +73,12 @@ public:
const std::string& getMenuSegmentUrl() const;
+ LLUUID getStringUUIDSelectedItem() const override final;
+ Type getSelectedType() const override final;
+
typedef boost::signals2::signal keystroke_signal_t;
- typedef boost::signals2::signal is_friend_signal_t;
- typedef boost::signals2::signal is_blocked_signal_t;
- static boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb);
- static boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb);
-
- typedef std::function ext_slurl_cb;
- typedef std::function ext_slurl_visible_cb;
- static void addMenuListeners(ext_slurl_cb cb, ext_slurl_visible_cb vcb);
+ static void addMenuListeners();
void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback);
@@ -131,9 +129,9 @@ public:
virtual BOOL canRedo() const;
virtual void cut();
virtual BOOL canCut() const;
- void copy(bool raw);
- void copyRaw() { copy(true); }
- virtual void copy() { copy(false); }
+ void copy(bool raw) const;
+ void copyRaw() const { copy(true); }
+ void copy() const override final { copy(false); }
virtual BOOL canCopy() const;
virtual void paste();
virtual BOOL canPaste() const;
@@ -468,10 +466,6 @@ protected:
void (*mOnScrollEndCallback)(void*);
void *mOnScrollEndData;
- // Used to check if user with given ID is avatar's friend
- static is_friend_signal_t* mIsFriendSignal;
- static is_blocked_signal_t* mIsObjectBlockedSignal;
-
LLWString mPreeditWString;
LLWString mPreeditOverwrittenWString;
std::vector mPreeditPositions;
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index a737c446f..18330a1bc 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -1,5 +1,3 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check it.
-// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
/**
* @file llurlentry.cpp
* @author Martin Reddy
@@ -40,6 +38,7 @@
#include "lltrans.h"
//#include "lluicolortable.h"
#include "message.h"
+#include "llexperiencecache.h"
#include
#include //
@@ -72,7 +71,7 @@ std::string LLUrlEntryBase::getIcon(const std::string &url)
LLStyleSP LLUrlEntryBase::getStyle() const
{
- static LLUICachedControl color("HTMLLinkColor");
+ static const LLUICachedControl color("HTMLLinkColor");
LLStyleSP style_params(new LLStyle(true, color, LLStringUtil::null));
//style_params->mUnderline = true; // Singu Note: We're not gonna bother here, underlining on hover
return style_params;
@@ -230,7 +229,7 @@ static std::string getStringAfterToken(const std::string& str, const std::string
size_t pos = str.find(token);
if (pos == std::string::npos)
{
- return std::string();
+ return LLStringUtil::null;
}
pos += token.size();
@@ -385,9 +384,9 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const
if (path_parts == actual_parts)
{
// handle slurl with (X,Y,Z) coordinates
- LLStringUtil::convertToS32(path_array[path_parts-3],x);
- LLStringUtil::convertToS32(path_array[path_parts-2],y);
- LLStringUtil::convertToS32(path_array[path_parts-1],z);
+ LLStringUtil::convertToS32(path_array[path_parts-3].asString(),x);
+ LLStringUtil::convertToS32(path_array[path_parts-2].asString(),y);
+ LLStringUtil::convertToS32(path_array[path_parts-1].asString(),z);
if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0))
{
@@ -398,8 +397,8 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const
{
// handle slurl with (X,Y) coordinates
- LLStringUtil::convertToS32(path_array[path_parts-2],x);
- LLStringUtil::convertToS32(path_array[path_parts-1],y);
+ LLStringUtil::convertToS32(path_array[path_parts-2].asString(),x);
+ LLStringUtil::convertToS32(path_array[path_parts-1].asString(),y);
;
if((x>= 0 && x<= 256) && (y>= 0 && y<= 256))
{
@@ -409,7 +408,7 @@ bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const
else if (path_parts == (actual_parts-2))
{
// handle slurl with (X) coordinate
- LLStringUtil::convertToS32(path_array[path_parts-1],x);
+ LLStringUtil::convertToS32(path_array[path_parts-1].asString(),x);
if(x>= 0 && x<= 256)
{
return TRUE;
@@ -427,6 +426,7 @@ LLUrlEntrySLURL::LLUrlEntrySLURL()
// see http://slurl.com/about.php for details on the SLURL format
mPattern = boost::regex("https?://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?\\S*)?)?",
boost::regex::perl|boost::regex::icase);
+ mIcon = "Hand";
mMenuName = "menu_url_slurl.xml";
mTooltip = LLTrans::getString("TooltipSLURL");
}
@@ -643,10 +643,11 @@ bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const
std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
+ static std::string name_wait_str = LLTrans::getString("LoadingData");
if (!gCacheName)
{
// probably at the login screen, use short string for layout
- return LLTrans::getString("LoadingData");
+ return name_wait_str;
}
std::string agent_id_string = getIDStringFromUrl(url);
@@ -673,19 +674,16 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
}
else
{
- if (cb)
- {
- auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
- mAvatarNameCacheConnections.emplace(agent_id, connection);
- addObserver(agent_id_string, url, cb);
- }
- return LLTrans::getString("LoadingData");
+ auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
+ mAvatarNameCacheConnections.emplace(agent_id, connection);
+ addObserver(agent_id_string, url, cb);
+ return name_wait_str;
}
}
LLStyleSP LLUrlEntryAgent::getStyle() const
{
- static LLUICachedControl color("HTMLAgentColor");
+ static const LLUICachedControl color("HTMLAgentColor");
LLStyleSP style_params(new LLStyle(true, color, LLStringUtil::null));
return style_params;
}
@@ -762,10 +760,11 @@ void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id,
std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
+ static std::string name_wait_str = LLTrans::getString("LoadingData");
if (!gCacheName)
{
// probably at the login screen, use short string for layout
- return LLTrans::getString("LoadingData");
+ return name_wait_str;
}
std::string agent_id_string = getIDStringFromUrl(url);
@@ -788,19 +787,16 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab
}
else
{
- if (cb)
- {
- auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2));
- mAvatarNameCacheConnections.emplace(agent_id, connection);
- addObserver(agent_id_string, url, cb);
- }
- return LLTrans::getString("LoadingData");
+ auto connection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2));
+ mAvatarNameCacheConnections.emplace(agent_id, connection);
+ addObserver(agent_id_string, url, cb);
+ return name_wait_str;
}
}
LLStyleSP LLUrlEntryAgentName::getStyle() const
{
- static LLUICachedControl color("HTMLAgentColor");
+ static const LLUICachedControl color("HTMLAgentColor");
LLStyleSP style_params(new LLStyle(true, color, LLStringUtil::null));
return style_params;
}
@@ -927,13 +923,10 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa
}
else
{
- if (cb)
- {
- gCacheName->getGroup(group_id,
- boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
- this, _1, _2, _3));
- addObserver(group_id_string, url, cb);
- }
+ gCacheName->getGroup(group_id,
+ boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
+ this, _1, _2, _3));
+ addObserver(group_id_string, url, cb);
return LLTrans::getString("LoadingData");
}
}
@@ -1037,7 +1030,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC
std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id
// Add an observer to call LLUrlLabelCallback when we have parcel name.
- if (cb) addObserver(parcel_id_string, url, cb);
+ addObserver(parcel_id_string, url, cb);
LLUUID parcel_id(parcel_id_string);
@@ -1084,11 +1077,8 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
parcel_data.sim_name.c_str(), region_x, region_y, region_z);
}
- for (std::set::iterator iter = sParcelInfoObservers.begin();
- iter != sParcelInfoObservers.end();
- ++iter)
+ for (auto url_entry : sParcelInfoObservers)
{
- LLUrlEntryParcel* url_entry = *iter;
if (url_entry)
{
url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
@@ -1432,8 +1422,9 @@ std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelC
LLStyleSP LLUrlEntryNoLink::getStyle() const
{
// Don't render as URL (i.e. no context menu or hand cursor).
- // Singu Note: What the heck? No, that's misleading!!
- return LLUrlEntryBase::getStyle();
+ LLStyleSP style(new LLStyle());
+ style->setLinkHREF(" ");
+ return style;
}
@@ -1502,13 +1493,65 @@ std::string LLUrlEntryEmail::getUrl(const std::string &string) const
return escapeUrl(string);
}
+LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/\\w+\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mIcon = "Generic_Experience";
+ mMenuName = "menu_url_experience.xml";
+}
+
+std::string LLUrlEntryExperienceProfile::getLabel(const std::string& url, const LLUrlLabelCallback& cb)
+{
+ if (!gCacheName)
+ {
+ // probably at the login screen, use short string for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string experience_id_string = getIDStringFromUrl(url);
+ if (experience_id_string.empty())
+ {
+ // something went wrong, just give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID experience_id(experience_id_string);
+ if (experience_id.isNull())
+ {
+ return LLTrans::getString("ExperienceNameNull");
+ }
+
+ const LLSD& experience_details = LLExperienceCache::instance().get(experience_id);
+ if (!experience_details.isUndefined())
+ {
+ std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString();
+ return experience_name_string.empty() ? LLTrans::getString("ExperienceNameUntitled") : experience_name_string;
+ }
+
+ addObserver(experience_id_string, url, cb);
+ LLExperienceCache::instance().get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1));
+ return LLTrans::getString("LoadingData");
+
+}
+
+void LLUrlEntryExperienceProfile::onExperienceDetails(const LLSD& experience_details)
+{
+ std::string name = experience_details[LLExperienceCache::NAME].asString();
+ if(name.empty())
+ {
+ name = LLTrans::getString("ExperienceNameUntitled");
+ }
+ callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), name, LLStringUtil::null);
+}
+
//
//
// LLUrlEntryJIRA describes a Jira Issue Tracker entry
//
LLUrlEntryJira::LLUrlEntryJira()
{
- mPattern = boost::regex("((?:ALCH|SV|BUG|CHOP|FIRE|MAINT|OPEN|SCR|STORM|SVC|VWR|WEB)-\\d+)",
+ mPattern = boost::regex("(\\b(?:ALCH|SV|BUG|CHOP|FIRE|MAINT|OPEN|SCR|STORM|SVC|VWR|WEB)-\\d+)",
boost::regex::perl);
mMenuName = "menu_url_http.xml";
mTooltip = LLTrans::getString("TooltipHttpUrl");
@@ -1532,10 +1575,9 @@ std::string LLUrlEntryJira::getUrl(const std::string &url) const
(url.find("SV") != std::string::npos) ?
"https://singularityviewer.atlassian.net/browse/%1%" :
(url.find("FIRE") != std::string::npos) ?
- "http://jira.phoenixviewer.com/browse/%1%" :
+ "https://jira.firestormviewer.com/browse/%1%" :
"http://jira.secondlife.com/browse/%1%"
) % url).str();
}
//
-
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 01f1fb1ae..6c382aa26 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -42,7 +42,7 @@ class LLAvatarName;
typedef boost::signals2::signal LLUrlLabelSignal;
-typedef LLUrlLabelSignal::slot_function_type LLUrlLabelCallback;
+typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback;
///
/// LLUrlEntryBase is the base class of all Url types registered in the
@@ -132,7 +132,7 @@ protected:
///
/// LLUrlEntryHTTP Describes generic http: and https: Urls
///
-class LLUrlEntryHTTP : public LLUrlEntryBase
+class LLUrlEntryHTTP final : public LLUrlEntryBase
{
public:
LLUrlEntryHTTP();
@@ -144,7 +144,7 @@ public:
///
/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels
///
-class LLUrlEntryHTTPLabel : public LLUrlEntryBase
+class LLUrlEntryHTTPLabel final : public LLUrlEntryBase
{
public:
LLUrlEntryHTTPLabel();
@@ -156,7 +156,7 @@ public:
///
/// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com
///
-class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase
+class LLUrlEntryHTTPNoProtocol final : public LLUrlEntryBase
{
public:
LLUrlEntryHTTPNoProtocol();
@@ -166,7 +166,7 @@ public:
std::string getTooltip(const std::string &url) const override;
};
-class LLUrlEntryInvalidSLURL : public LLUrlEntryBase
+class LLUrlEntryInvalidSLURL final : public LLUrlEntryBase
{
public:
LLUrlEntryInvalidSLURL();
@@ -180,7 +180,7 @@ public:
///
/// LLUrlEntrySLURL Describes http://slurl.com/... Urls
///
-class LLUrlEntrySLURL : public LLUrlEntryBase
+class LLUrlEntrySLURL final : public LLUrlEntryBase
{
public:
LLUrlEntrySLURL();
@@ -205,7 +205,7 @@ public:
///
/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls
///
-class LLUrlEntrySimpleSecondlifeURL : public LLUrlEntrySecondlifeURL
+class LLUrlEntrySimpleSecondlifeURL final : public LLUrlEntrySecondlifeURL
{
public:
LLUrlEntrySimpleSecondlifeURL();
@@ -281,7 +281,7 @@ private:
/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
/// that displays the full display name + user name for an avatar
/// such as "James Linden (james.linden)"
-class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName
+class LLUrlEntryAgentCompleteName final : public LLUrlEntryAgentName
{
public:
LLUrlEntryAgentCompleteName();
@@ -289,7 +289,7 @@ private:
/*virtual*/ std::string getName(const LLAvatarName& avatar_name) override;
};
-class LLUrlEntryAgentLegacyName : public LLUrlEntryAgentName
+class LLUrlEntryAgentLegacyName final : public LLUrlEntryAgentName
{
public:
LLUrlEntryAgentLegacyName();
@@ -302,7 +302,7 @@ private:
/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
/// that displays the just the display name for an avatar
/// such as "James Linden"
-class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName
+class LLUrlEntryAgentDisplayName final : public LLUrlEntryAgentName
{
public:
LLUrlEntryAgentDisplayName();
@@ -315,7 +315,7 @@ private:
/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
/// that displays the just the display name for an avatar
/// such as "james.linden"
-class LLUrlEntryAgentUserName : public LLUrlEntryAgentName
+class LLUrlEntryAgentUserName final : public LLUrlEntryAgentName
{
public:
LLUrlEntryAgentUserName();
@@ -323,11 +323,25 @@ private:
/*virtual*/ std::string getName(const LLAvatarName& avatar_name) override;
};
+///
+/// LLUrlEntryExperienceProfile Describes a Second Life experience profile Url, e.g.,
+/// secondlife:///app/experience/0e346d8b-4433-4d66-a6b0-fd37083abc4c/profile
+/// that displays the experience name
+class LLUrlEntryExperienceProfile final : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryExperienceProfile();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) override;
+private:
+ void onExperienceDetails(const LLSD& experience_details);
+};
+
+
///
/// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
///
-class LLUrlEntryGroup : public LLUrlEntryBase
+class LLUrlEntryGroup final : public LLUrlEntryBase
{
public:
LLUrlEntryGroup();
@@ -342,7 +356,7 @@ private:
/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
///
-class LLUrlEntryInventory : public LLUrlEntryBase
+class LLUrlEntryInventory final : public LLUrlEntryBase
{
public:
LLUrlEntryInventory();
@@ -354,7 +368,7 @@ private:
/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
///
-class LLUrlEntryObjectIM : public LLUrlEntryBase
+class LLUrlEntryObjectIM final : public LLUrlEntryBase
{
public:
LLUrlEntryObjectIM();
@@ -367,7 +381,7 @@ private:
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
///
-class LLUrlEntryParcel : public LLUrlEntryBase
+class LLUrlEntryParcel final : public LLUrlEntryBase
{
public:
struct LLParcelData
@@ -414,7 +428,7 @@ private:
/// LLUrlEntryPlace Describes a Second Life location Url, e.g.,
/// secondlife://Ahern/50/50/50
///
-class LLUrlEntryPlace : public LLUrlEntryBase
+class LLUrlEntryPlace final : public LLUrlEntryBase
{
public:
LLUrlEntryPlace();
@@ -426,7 +440,7 @@ public:
/// LLUrlEntryRegion Describes a Second Life location Url, e.g.,
/// secondlife:///app/region/Ahern/128/128/0
///
-class LLUrlEntryRegion : public LLUrlEntryBase
+class LLUrlEntryRegion final : public LLUrlEntryBase
{
public:
LLUrlEntryRegion();
@@ -438,7 +452,7 @@ public:
/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
/// secondlife:///app/teleport/Ahern/50/50/50/
///
-class LLUrlEntryTeleport : public LLUrlEntryBase
+class LLUrlEntryTeleport final : public LLUrlEntryBase
{
public:
LLUrlEntryTeleport();
@@ -450,7 +464,7 @@ public:
/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
/// with secondlife:// (used as a catch-all for cases not matched above)
///
-class LLUrlEntrySL : public LLUrlEntryBase
+class LLUrlEntrySL final : public LLUrlEntryBase
{
public:
LLUrlEntrySL();
@@ -461,7 +475,7 @@ public:
/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
/// with secondlife:// with the ability to specify a custom label.
///
-class LLUrlEntrySLLabel : public LLUrlEntryBase
+class LLUrlEntrySLLabel final : public LLUrlEntryBase
{
public:
LLUrlEntrySLLabel();
@@ -475,7 +489,7 @@ public:
/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g.,
/// secondlife:///app/worldmap/Ahern/50/50/50
///
-class LLUrlEntryWorldMap : public LLUrlEntryBase
+class LLUrlEntryWorldMap final : public LLUrlEntryBase
{
public:
LLUrlEntryWorldMap();
@@ -486,7 +500,7 @@ public:
///
/// LLUrlEntryNoLink lets us turn of URL detection with ... tags
///
-class LLUrlEntryNoLink : public LLUrlEntryBase
+class LLUrlEntryNoLink final : public LLUrlEntryBase
{
public:
LLUrlEntryNoLink();
@@ -498,7 +512,7 @@ public:
///
/// LLUrlEntryIcon describes an icon with ... tags
///
-class LLUrlEntryIcon : public LLUrlEntryBase
+class LLUrlEntryIcon final : public LLUrlEntryBase
{
public:
LLUrlEntryIcon();
@@ -510,7 +524,7 @@ public:
///
/// LLUrlEntryEmail Describes a generic mailto: Urls
///
-class LLUrlEntryEmail : public LLUrlEntryBase
+class LLUrlEntryEmail final : public LLUrlEntryBase
{
public:
LLUrlEntryEmail();
@@ -522,7 +536,7 @@ public:
///
/// LLUrlEntryJira describes a Jira Issue
///
-class LLUrlEntryJira : public LLUrlEntryBase
+class LLUrlEntryJira final : public LLUrlEntryBase
{
public:
LLUrlEntryJira();
diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp
index 717db6280..240e04ef0 100644
--- a/indra/llui/llurlmatch.cpp
+++ b/indra/llui/llurlmatch.cpp
@@ -58,7 +58,7 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std
mTooltip = tooltip;
mIcon = icon;
mStyle = style;
- mStyle->setLinkHREF(url);
+ mStyle->setLinkHREF(mStyle->isLink() ? LLStringUtil::null : url); // Singu Note: This hack exists in lieu of no link flag
mMenuName = menu;
mLocation = location;
mID = id;
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index 908531b0b..c7dba05e4 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -73,6 +73,7 @@ LLUrlRegistry::LLUrlRegistry()
registerUrl(new LLUrlEntryObjectIM());
registerUrl(new LLUrlEntryPlace());
registerUrl(new LLUrlEntryInventory());
+ registerUrl(new LLUrlEntryExperienceProfile());
//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
//so it should be registered in the end of list
registerUrl(new LLUrlEntrySL());
diff --git a/indra/lscript/lscript_byteformat.h b/indra/lscript/lscript_byteformat.h
index 49e78e717..679714e59 100644
--- a/indra/lscript/lscript_byteformat.h
+++ b/indra/lscript/lscript_byteformat.h
@@ -355,6 +355,10 @@ typedef enum e_lscript_state_event_type
LSTT_REMOTE_DATA,
LSTT_HTTP_RESPONSE,
LSTT_HTTP_REQUEST,
+ LSTT_EXPERMISSIONS,
+ LSTT_TRANSACTION_RESULT,
+ LSTT_PATH_UPDATE,
+ LSTT_EXPERMISSIONS_DENIED,
LSTT_EOF,
LSTT_STATE_BEGIN = LSTT_STATE_ENTRY,
@@ -397,7 +401,11 @@ const U64 LSCRIPTStateBitField[LSTT_EOF] =
0x0000000040000000, // LSTT_OBJECT_REZ
0x0000000080000000, // LSTT_REMOTE_DATA
0x0000000100000000LL, // LSTT_HTTP_RESPOSE
- 0x0000000200000000LL // LSTT_HTTP_REQUEST
+ 0x0000000200000000LL, // LSTT_HTTP_REQUEST
+ 0x0000000400000000LL, // LSTT_EXPERMISSIONS
+ 0x0000000800000000LL, // LSTT_TRANSACTION_RESULT
+ 0x0000001000000000LL, // LSTT_PATH_UPDATE
+ 0x0000002000000000LL, //LSTT_EXPERMISSIONS_DENIED
};
inline S32 get_event_handler_jump_position(U64 bit_field, LSCRIPTStateEventType type)
@@ -511,6 +519,7 @@ typedef enum e_lscript_runtime_faults
LSRF_TOO_MANY_LISTENS,
LSRF_NESTING_LISTS,
LSRF_CLI,
+ LSRF_INVALID_STATE,
LSRF_EOF
} LSCRIPTRunTimeFaults;
@@ -551,10 +560,10 @@ const U32 LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_EOF] =
(0x1 << 10),// SCRIPT_PERMISSION_TRACK_CAMERA
(0x1 << 11),// SCRIPT_PERMISSION_CONTROL_CAMERA
(0x1 << 12),// SCRIPT_PERMISSION_TELEPORT
- (0x1 << 13),// SCRIPT_PERMISSION_EXPERIENCE,
- (0x1 << 14),// SCRIPT_PERMISSION_SILENT_ESTATE_MANAGEMENT,
- (0x1 << 15),// SCRIPT_PERMISSION_OVERRIDE_ANIMATIONS,
- (0x1 << 16),// SCRIPT_PERMISSION_RETURN_OBJECTS,
+ (0x1 << 13),// SCRIPT_PERMISSION_EXPERIENCE
+ (0x1 << 14),// SCRIPT_PERMISSION_SILENT_ESTATE_MANAGEMENT
+ (0x1 << 15),// SCRIPT_PERMISSION_OVERRIDE_ANIMATIONS
+ (0x1 << 16),// SCRIPT_PERMISSION_RETURN_OBJECTS
};
// http_request string constants
diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l
index f7e592afb..3fb834d17 100644
--- a/indra/lscript/lscript_compile/indra.l
+++ b/indra/lscript/lscript_compile/indra.l
@@ -685,6 +685,23 @@ void parse_string();
"STATUS_INTERNAL_ERROR" { count(); yylval.ival = LSL_STATUS_INTERNAL_ERROR; return(INTEGER_CONSTANT); }
"STATUS_WHITELIST_FAILED" { count(); yylval.ival = LSL_STATUS_WHITELIST_FAILED; return(INTEGER_CONSTANT); }
+"XP_ERROR_NONE" { const char* sval= "no error"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_THROTTLED" { const char* sval= "exceeded throttle"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_EXPERIENCES_DISABLED" { const char* sval= "experiences are disabled"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_INVALID_PARAMETERS" { const char* sval= "invalid parameters"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_NOT_PERMITTED" { const char* sval= "operation not permitted"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_NO_EXPERIENCE" { const char* sval= "script not associated with an experience";yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_NOT_FOUND" { const char* sval= "not found"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_INVALID_EXPERIENCE" { const char* sval= "invalid experience"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_EXPERIENCE_DISABLED" { const char* sval= "experience is disabled"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_EXPERIENCE_SUSPENDED" { const char* sval= "experience is suspended"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_UNKNOWN_ERROR" { const char* sval= "unknown error"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_QUOTA_EXCEEDED" { const char* sval= "experience data quota exceeded"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_STORE_DISABLED" { const char* sval= "key-value store is disabled"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_STORAGE_EXCEPTION" { const char* sval= "key-value store communication failed"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_KEY_NOT_FOUND" { const char* sval= "key doesn't exist"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+"XP_ERROR_RETRY_UPDATE" { const char* sval= "retry update"; yylval.sval = new char[strlen(sval)+1]; strcpy(yylval.sval, sval); return(STRING_CONSTANT); }
+
"PROFILE_SCRIPT_NONE" { count(); yylval.ival = LSL_PROFILE_SCRIPT_NONE; return(INTEGER_CONSTANT); }
"PROFILE_SCRIPT_MEMORY" { count(); yylval.ival = LSL_PROFILE_SCRIPT_MEMORY; return(INTEGER_CONSTANT); }
diff --git a/indra/lscript/lscript_compile/indra.y b/indra/lscript/lscript_compile/indra.y
index febf5f675..3e4b6c6ce 100644
--- a/indra/lscript/lscript_compile/indra.y
+++ b/indra/lscript/lscript_compile/indra.y
@@ -179,6 +179,8 @@
%type money
%type email
%type run_time_permissions
+%type experience_permissions
+%type experience_permissions_denied
%type inventory
%type attach
%type dataserver
@@ -787,6 +789,16 @@ event
$$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
gAllocationManager->addAllocation($$);
}
+ | experience_permissions compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | experience_permissions_denied compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
| inventory compound_statement
{
$$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
@@ -1039,6 +1051,28 @@ run_time_permissions
}
;
+experience_permissions
+ : EXPERIENCE_PERMISSIONS '(' LLKEY IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptEXPEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+experience_permissions_denied
+ : EXPERIENCE_PERMISSIONS_DENIED '(' LLKEY IDENTIFIER ',' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ $$ = new LLScriptEXPDeniedEvent(gLine, gColumn, id1, id2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
inventory
: INVENTORY '(' INTEGER IDENTIFIER ')'
{
diff --git a/indra/lscript/lscript_compile/lscript_tree.cpp b/indra/lscript/lscript_compile/lscript_tree.cpp
index b737bff75..e5e7bb3d0 100644
--- a/indra/lscript/lscript_compile/lscript_tree.cpp
+++ b/indra/lscript/lscript_compile/lscript_tree.cpp
@@ -3712,6 +3712,155 @@ S32 LLScriptNoSensorEvent::getSize()
return 0;
}
+void LLScriptEXPEvent::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "experience_permissions( key ");
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ checkForDuplicateHandler(fp, this, scope, "experience_permissions");
+ if (scope->checkEntry(mName->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mName->mScopeEntry = scope->addEntry(mName->mName, LIT_VARIABLE, LST_KEY);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mName->mScopeEntry)
+ {
+ mName->mScopeEntry->mOffset = (S32)count;
+ mName->mScopeEntry->mSize = 4;
+ count += mName->mScopeEntry->mSize;
+ }
+ }
+ break;
+
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "experience_permissions";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mName->mName, strlen(mName->mName) + 1);
+#endif
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "experience_permissions( valuetype [ScriptTypes]LindenLab.SecondLife.Key ");
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )");
+ break;
+ default:
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptEXPEvent::getSize()
+{
+ // key = 4
+ return 4;
+}
+
+
+void LLScriptEXPDeniedEvent::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "experience_permissions_denied( key ");
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mReason->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ checkForDuplicateHandler(fp, this, scope, "experience_permissions_denied");
+ if (scope->checkEntry(mName->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mName->mScopeEntry = scope->addEntry(mName->mName, LIT_VARIABLE, LST_KEY);
+ }
+ if (scope->checkEntry(mReason->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mReason->mScopeEntry = scope->addEntry(mReason->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just trying to determine how much space the variable needs
+ if (mName->mScopeEntry)
+ {
+ mName->mScopeEntry->mOffset = (S32)count;
+ mName->mScopeEntry->mSize = 4;
+ count += mName->mScopeEntry->mSize;
+
+ mReason->mScopeEntry->mOffset = (S32)count;
+ mReason->mScopeEntry->mSize = 4;
+ count += mReason->mScopeEntry->mSize;
+ }
+ }
+ break;
+
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "experience_permissions_denied";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mName->mName, strlen(mName->mName) + 1);
+ chunk->addBytes(mReason->mName, strlen(mReason->mName) + 1);
+#endif
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "experience_permissions_denied( valuetype [ScriptTypes]LindenLab.SecondLife.Key ");
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", int32 ");
+ mReason->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )");
+ break;
+ default:
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mReason->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptEXPDeniedEvent::getSize()
+{
+ // key = 4 + integer
+ return LSCRIPTDataSize[LST_KEY]+LSCRIPTDataSize[LST_INTEGER];
+}
+
void LLScriptAtTarget::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
{
if (gErrorToText.getErrors())
@@ -8577,6 +8726,7 @@ void LLScriptReturn::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePa
}
}
prunearg = TRUE;
+ break;
case LSCP_TYPE:
// if there is a return expression, it must be promotable to the return type of the function
if (mExpression)
@@ -9775,7 +9925,13 @@ void LLScriptEventHandler::recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCom
mScopeEntry->mFunctionArgs.addType(LST_STRING);
mScopeEntry->mFunctionArgs.addType(LST_STRING);
break;
-
+ case LSTT_EXPERMISSIONS:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ break;
+ case LSTT_EXPERMISSIONS_DENIED:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
default:
break;
}
diff --git a/indra/lscript/lscript_compile/lscript_tree.h b/indra/lscript/lscript_compile/lscript_tree.h
index 8131003c3..c7be5396a 100644
--- a/indra/lscript/lscript_compile/lscript_tree.h
+++ b/indra/lscript/lscript_compile/lscript_tree.h
@@ -633,6 +633,39 @@ public:
LLScriptIdentifier *mRTPermissions;
};
+class LLScriptEXPEvent : public LLScriptEvent
+{
+public:
+ LLScriptEXPEvent(S32 line, S32 col, LLScriptIdentifier *name)
+ : LLScriptEvent(line, col, LSTT_EXPERMISSIONS), mName(name)
+ {
+ }
+
+ ~LLScriptEXPEvent() {}
+
+ void recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mName;
+};
+
+class LLScriptEXPDeniedEvent : public LLScriptEvent
+{
+public:
+ LLScriptEXPDeniedEvent(S32 line, S32 col, LLScriptIdentifier *name, LLScriptIdentifier *reason)
+ : LLScriptEvent(line, col, LSTT_EXPERMISSIONS_DENIED), mName(name), mReason(reason)
+ {
+ }
+
+ ~LLScriptEXPDeniedEvent() {}
+
+ void recurse(LLFILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mName;
+ LLScriptIdentifier *mReason;
+};
+
class LLScriptChatEvent : public LLScriptEvent
{
public:
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 57ad1b0b2..550d2d11d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -51,9 +51,9 @@ include(ZLIB)
include(URIPARSER)
-if(FMODSTUDIO)
+if(USE_FMODSTUDIO)
include_directories(${FMODSTUDIO_INCLUDE_DIR})
-endif(FMODSTUDIO)
+endif(USE_FMODSTUDIO)
if(USE_CRASHPAD)
include_directories(${CRASHPAD_INCLUDE_DIRS})
@@ -181,6 +181,7 @@ set(viewer_SOURCE_FILES
lleventinfo.cpp
lleventnotifier.cpp
lleventpoll.cpp
+ llexperiencelog.cpp
llexternaleditor.cpp
llface.cpp
llfasttimerview.cpp
@@ -221,6 +222,9 @@ set(viewer_SOURCE_FILES
llfloatereditui.cpp
llfloaterenvsettings.cpp
llfloaterevent.cpp
+ llfloaterexperiencepicker.cpp
+ llfloaterexperienceprofile.cpp
+ llfloaterexperiences.cpp
llfloaterexploreanimations.cpp
llfloaterexploresounds.cpp
llfloaterfeed.cpp
@@ -320,6 +324,7 @@ set(viewer_SOURCE_FILES
llhudtext.cpp
llhudview.cpp
llimpanel.cpp
+ llimprocessing.cpp
llimview.cpp
llinventoryactions.cpp
llinventorybridge.cpp
@@ -382,11 +387,16 @@ set(viewer_SOURCE_FILES
llpaneldisplay.cpp
llpaneleditwearable.cpp
llpanelevent.cpp
+ llpanelexperiencelisteditor.cpp
+ llpanelexperiencelog.cpp
+ llpanelexperiencepicker.cpp
+ llpanelexperiences.cpp
llpanelface.cpp
llpanelgeneral.cpp
llpanelgroup.cpp
llpanelgroupbulk.cpp
llpanelgroupbulkban.cpp
+ llpanelgroupexperiences.cpp
llpanelgroupgeneral.cpp
llpanelgroupinvite.cpp
llpanelgrouplandmoney.cpp
@@ -617,9 +627,6 @@ set(viewer_SOURCE_FILES
wlfPanel_AdvSettings.cpp
)
-set(VIEWER_BINARY_NAME "singularity-bin" CACHE STRING
- "The name of the viewer executable to create.")
-
set(viewer_HEADER_FILES
CMakeLists.txt
ViewerInstall.cmake
@@ -718,6 +725,7 @@ set(viewer_HEADER_FILES
lleventinfo.h
lleventnotifier.h
lleventpoll.h
+ llexperiencelog.h
llexternaleditor.h
llface.h
llfasttimerview.h
@@ -758,6 +766,9 @@ set(viewer_HEADER_FILES
llfloatereditui.h
llfloaterenvsettings.h
llfloaterevent.h
+ llfloaterexperiencepicker.h
+ llfloaterexperienceprofile.h
+ llfloaterexperiences.h
llfloaterexploreanimations.h
llfloaterexploresounds.h
llfloaterfeed.h
@@ -857,6 +868,7 @@ set(viewer_HEADER_FILES
llhudtext.h
llhudview.h
llimpanel.h
+ llimprocessing.h
llimview.h
llinventorybridge.h
llinventoryclipboard.h
@@ -919,12 +931,17 @@ set(viewer_HEADER_FILES
llpaneldisplay.h
llpaneleditwearable.h
llpanelevent.h
+ llpanelexperiencelisteditor.h
+ llpanelexperiencelog.h
+ llpanelexperiencepicker.h
+ llpanelexperiences.h
llpanelface.h
llpanelgeneral.h
llpanelgroup.h
llpanelgroupbulk.h
llpanelgroupbulkban.h
llpanelgroupbulkimpl.h
+ llpanelgroupexperiences.h
llpanelgroupgeneral.h
llpanelgroupinvite.h
llpanelgrouplandmoney.h
@@ -1165,12 +1182,12 @@ set(viewer_HEADER_FILES
source_group("CMake Rules" FILES ViewerInstall.cmake)
+#build_data.json creation moved to viewer_manifest.py MAINT-6413
# the viewer_version.txt file created here is for passing to viewer_manifest and autobuild
# the summary.json file is created for the benefit of the TeamCity builds, where
# it is used to provide descriptive information to the build results page
add_custom_target(generate_viewer_version ALL
COMMAND ${CMAKE_COMMAND} -E echo ${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION} > ${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
- COMMAND ${CMAKE_COMMAND} -E echo {"Type":"viewer","Version":"${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}"} > ${CMAKE_BINARY_DIR}/summary.json
COMMENT "Generating viewer_version.txt for manifest processing"
)
@@ -1239,11 +1256,11 @@ if (WINDOWS)
llwindebug.h
)
- if (NVAPI)
+ if (USE_NVAPI)
set(APPVWRW32_COMPILE_FLAGS "${APPVWRW32_COMPILE_FLAGS} -DUSE_NVAPI=1")
- else (NVAPI)
+ else (USE_NVAPI)
set(APPVWRW32_COMPILE_FLAGS "${APPVWRW32_COMPILE_FLAGS} -UUSE_NVAPI")
- endif (NVAPI)
+ endif (USE_NVAPI)
set_source_files_properties(llappviewerwin32.cpp PROPERTIES COMPILE_FLAGS "${APPVWRW32_COMPILE_FLAGS}")
# Replace the icons with the appropriate ones for the channel
@@ -1434,9 +1451,10 @@ if (OPENAL)
list(APPEND LLSTARTUP_COMPILE_DEFINITIONS "LL_OPENAL=1")
endif (OPENAL)
-if (FMODSTUDIO)
+if (USE_FMODSTUDIO)
list(APPEND LLSTARTUP_COMPILE_DEFINITIONS "LL_FMODSTUDIO=1")
-endif (FMODSTUDIO)
+ set_source_files_properties(llpanellogin.cpp PROPERTIES COMPILE_DEFINITIONS "LL_FMODSTUDIO=1")
+endif (USE_FMODSTUDIO)
set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_DEFINITIONS "${LLSTARTUP_COMPILE_DEFINITIONS}")
@@ -1463,8 +1481,7 @@ if(USE_PRECOMPILED_HEADERS)
${CMAKE_CURRENT_SOURCE_DIR}/llviewerprecompiledheaders.cpp)
endif(USE_PRECOMPILED_HEADERS)
-set(PACKAGE OFF CACHE BOOL
- "Add a package target that builds an installer package.")
+option(PACKAGE "Add a package target that builds an installer package." ON)
if (WINDOWS)
set(release_flags "/MAP")
@@ -1535,20 +1552,20 @@ if (WINDOWS)
)
endif (ADDRESS_SIZE EQUAL 64)
- if (FMODSTUDIO)
- if (WORD_SIZE EQUAL 64)
+ if (USE_FMODSTUDIO)
+ if (ADDRESS_SIZE EQUAL 64)
list(APPEND COPY_INPUT_DEPENDENCIES
${SHARED_LIB_STAGING_DIR}/Release/fmod64.dll
${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/fmod64.dll
${SHARED_LIB_STAGING_DIR}/Debug/fmodL64.dll
)
- else (WORD_SIZE EQUAL 64)
+ else (ADDRESS_SIZE EQUAL 64)
list(APPEND COPY_INPUT_DEPENDENCIES
${SHARED_LIB_STAGING_DIR}/Release/fmod.dll
${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/fmod.dll
)
- endif (WORD_SIZE EQUAL 64)
- endif (FMODSTUDIO)
+ endif (ADDRESS_SIZE EQUAL 64)
+ endif (USE_FMODSTUDIO)
if(MSVC_IDE)
set(VIEWER_BUILD_DEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
@@ -1567,7 +1584,7 @@ if (WINDOWS)
--branding_id=${VIEWER_BRANDING_ID}
--build=${CMAKE_CURRENT_BINARY_DIR}
--buildtype=${CMAKE_BUILD_TYPE}
- --configuration=${VIEWER_BUILD_DEST_DIR}
+ --configuration=${VIEWER_BUILD_DEST_DIR}
--dest=${VIEWER_BUILD_DEST_DIR}
--grid=${GRID}
--channel=${VIEWER_CHANNEL}
@@ -1611,7 +1628,7 @@ if (WINDOWS)
--buildtype=${CMAKE_BUILD_TYPE}
--channel=${VIEWER_CHANNEL}
--versionfile=${CMAKE_CURRENT_BINARY_DIR}/viewer_version.txt
- --configuration=${VIEWER_BUILD_DEST_DIR}
+ --configuration=${VIEWER_BUILD_DEST_DIR}
--dest=${VIEWER_BUILD_DEST_DIR}
--grid=${GRID}
--source=${CMAKE_CURRENT_SOURCE_DIR}
@@ -1806,7 +1823,7 @@ if (DARWIN)
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
)
- add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_libvlc media_plugin_webkit basic_plugin_filepicker)
+ add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_libvlc media_plugin_cef basic_plugin_filepicker)
if (PACKAGE)
add_custom_target(llpackage ALL DEPENDS ${VIEWER_BINARY_NAME})
@@ -1840,7 +1857,7 @@ if (INSTALL)
include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake)
endif (INSTALL)
-if (PACKAGE AND USE_CRASHPAD)
+if (FALSE)
# Breakpad symbol-file generation
set(SYMBOL_NAME ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${VIEWER_CHANNEL_NOSPACE}-${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}-symbols)
@@ -1868,7 +1885,7 @@ if (PACKAGE AND USE_CRASHPAD)
if (LINUX)
list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/packaged")
set(VIEWER_SYMBOL_FILE "${SYMBOL_NAME}-${AUTOBUILD_PLATFORM_NAME}.tar.bz2")
- set(VIEWER_EXE_GLOBS "${VIEWER_BRANDING_ID}-do-not-run-directly SLPlugin")
+ set(VIEWER_EXE_GLOBS "do-not-directly-run-${VIEWER_BINARY_NAME} SLPlugin")
set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*")
set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest)
endif (LINUX)
diff --git a/indra/newview/NACLantispam.cpp b/indra/newview/NACLantispam.cpp
index dfeb7a5c9..584f9e319 100644
--- a/indra/newview/NACLantispam.cpp
+++ b/indra/newview/NACLantispam.cpp
@@ -14,457 +14,300 @@
*/
#include "llviewerprecompiledheaders.h"
+
#include "NACLantispam.h"
-#include "llviewercontrol.h"
-#include "llnotificationsutil.h"
-#include "llviewerobjectlist.h"
#include "llagent.h"
+#include "llavataractions.h"
+#include "llcallbacklist.h" // For idle cleaning
+#include "llnotificationsutil.h"
#include "lltrans.h"
+#include "llviewerobjectlist.h"
+
#include
#include
+class NACLAntiSpamQueue
+{
+ friend class NACLAntiSpamRegistry;
+public:
+ const U32& getAmount() const { return mAmount; }
+ const U32& getTime() const { return mTime; }
+protected:
+ NACLAntiSpamQueue(const U32& time, const U32& amount) : mTime(time), mAmount(amount) {}
+ void setAmount(const U32& amount) { mAmount = amount; }
+ void setTime(const U32& time) { mTime = time; }
+ void block(const LLUUID& source) { mEntries[source.asString()].block(); }
+ void reset() { mEntries.clear(); }
+ // Returns 0 if unblocked/disabled, 1 if check results in a new block, 2 if by an existing block
+ U8 check(const LLUUID& source, const U32& multiplier)
+ {
+ const auto key = source.asString();
+ auto it = mEntries.find(key);
+ if (it != mEntries.end())
+ return it->second.blockIfNeeded(mAmount * multiplier, mTime);
+ mEntries[key]; // Default construct an Entry
+ return 0U;
+ }
+ void idle()
+ {
+ // Clean out old unblocked entries
+ const auto time_limit = mTime + 1; // One second after time has gone up, the next offense would reset anyway
+ for (auto it = mEntries.begin(); it != mEntries.end();)
+ {
+ const auto& entry = it->second;
+ if (entry.getBlocked() || entry.withinBlockTime(time_limit))
+ ++it;
+ else it = mEntries.erase(it);
+ }
+ }
+
+private:
+ class Entry
+ {
+ friend class NACLAntiSpamQueue;
+ protected:
+ void reset() { updateTime(); mAmount = 1; mBlocked = false; }
+ const U32& getAmount() const { return mAmount; }
+ const std::time_t& getTime() const { return mTime; }
+ void updateTime() { mTime = time(nullptr); }
+ void block() { mBlocked = true; }
+ bool withinBlockTime(const U32& time_limit) const { return (time(nullptr) - mTime) <= time_limit; }
+ U8 blockIfNeeded(const U32& amount, const U32& time_limit)
+ {
+ if (mBlocked) return 2U; // Already blocked
+ if (withinBlockTime(time_limit))
+ {
+ if (++mAmount > amount)
+ {
+ block();
+ return 1U;
+ }
+ }
+ else reset(); // Enough time has passed to forgive or not already in list
+ return 0U;
+ }
+ bool getBlocked() const { return mBlocked; }
+ private:
+ U32 mAmount = 1;
+ std::time_t mTime = time(nullptr);
+ bool mBlocked = false;
+ };
+ boost::unordered_map mEntries;
+ U32 mAmount, mTime;
+};
+
bool can_block(const LLUUID& id)
{
- if (id.isNull() || gAgent.getID() == id) return false; //Can't block system or self.
- if (const LLViewerObject* obj = gObjectList.findObject(id)) //From an object,
- return !obj->permYouOwner(); //not own object.
+ if (id.isNull() || gAgentID == id) return false; // Can't block system or self.
+ if (const LLViewerObject* obj = gObjectList.findObject(id)) // From an object,
+ return !obj->permYouOwner(); // not own object.
return true;
}
-U32 NACLAntiSpamRegistry::globalAmount;
-U32 NACLAntiSpamRegistry::globalTime;
-bool NACLAntiSpamRegistry::bGlobalQueue;
-NACLAntiSpamQueue* NACLAntiSpamRegistry::queues[NACLAntiSpamRegistry::QUEUE_MAX] = {0};
-boost::unordered_map NACLAntiSpamRegistry::globalEntries;
-boost::unordered_map::iterator NACLAntiSpamRegistry::it2;
+bool is_collision_sound(const std::string& sound)
+{
+ // The following sounds will be ignored for purposes of spam protection. They have been gathered from wiki documentation of frequent official sounds.
+ const std::array COLLISION_SOUNDS = {
+ "dce5fdd4-afe4-4ea1-822f-dd52cac46b08",
+ "51011582-fbca-4580-ae9e-1a5593f094ec",
+ "68d62208-e257-4d0c-bbe2-20c9ea9760bb",
+ "75872e8c-bc39-451b-9b0b-042d7ba36cba",
+ "6a45ba0b-5775-4ea8-8513-26008a17f873",
+ "992a6d1b-8c77-40e0-9495-4098ce539694",
+ "2de4da5a-faf8-46be-bac6-c4d74f1e5767",
+ "6e3fb0f7-6d9c-42ca-b86b-1122ff562d7d",
+ "14209133-4961-4acc-9649-53fc38ee1667",
+ "bc4a4348-cfcc-4e5e-908e-8a52a8915fe6",
+ "9e5c1297-6eed-40c0-825a-d9bcd86e3193",
+ "e534761c-1894-4b61-b20c-658a6fb68157",
+ "8761f73f-6cf9-4186-8aaa-0948ed002db1",
+ "874a26fd-142f-4173-8c5b-890cd846c74d",
+ "0e24a717-b97e-4b77-9c94-b59a5a88b2da",
+ "75cf3ade-9a5b-4c4d-bb35-f9799bda7fb2",
+ "153c8bf7-fb89-4d89-b263-47e58b1b4774",
+ "55c3e0ce-275a-46fa-82ff-e0465f5e8703",
+ "24babf58-7156-4841-9a3f-761bdbb8e237",
+ "aca261d8-e145-4610-9e20-9eff990f2c12",
+ "0642fba6-5dcf-4d62-8e7b-94dbb529d117",
+ "25a863e8-dc42-4e8a-a357-e76422ace9b5",
+ "9538f37c-456e-4047-81be-6435045608d4",
+ "8c0f84c3-9afd-4396-b5f5-9bca2c911c20",
+ "be582e5d-b123-41a2-a150-454c39e961c8",
+ "c70141d4-ba06-41ea-bcbc-35ea81cb8335",
+ "7d1826f4-24c4-4aac-8c2e-eff45df37783",
+ "063c97d3-033a-4e9b-98d8-05c8074922cb",
+ "00000000-0000-0000-0000-000000000120"
+ };
-// The following sounds will be ignored for purposes of spam protection. They have been gathered from wiki documentation of frequent official sounds.
-const std::string COLLISION_SOUNDS[] ={"dce5fdd4-afe4-4ea1-822f-dd52cac46b08","51011582-fbca-4580-ae9e-1a5593f094ec","68d62208-e257-4d0c-bbe2-20c9ea9760bb","75872e8c-bc39-451b-9b0b-042d7ba36cba","6a45ba0b-5775-4ea8-8513-26008a17f873","992a6d1b-8c77-40e0-9495-4098ce539694","2de4da5a-faf8-46be-bac6-c4d74f1e5767","6e3fb0f7-6d9c-42ca-b86b-1122ff562d7d","14209133-4961-4acc-9649-53fc38ee1667","bc4a4348-cfcc-4e5e-908e-8a52a8915fe6","9e5c1297-6eed-40c0-825a-d9bcd86e3193","e534761c-1894-4b61-b20c-658a6fb68157","8761f73f-6cf9-4186-8aaa-0948ed002db1","874a26fd-142f-4173-8c5b-890cd846c74d","0e24a717-b97e-4b77-9c94-b59a5a88b2da","75cf3ade-9a5b-4c4d-bb35-f9799bda7fb2","153c8bf7-fb89-4d89-b263-47e58b1b4774","55c3e0ce-275a-46fa-82ff-e0465f5e8703","24babf58-7156-4841-9a3f-761bdbb8e237","aca261d8-e145-4610-9e20-9eff990f2c12","0642fba6-5dcf-4d62-8e7b-94dbb529d117","25a863e8-dc42-4e8a-a357-e76422ace9b5","9538f37c-456e-4047-81be-6435045608d4","8c0f84c3-9afd-4396-b5f5-9bca2c911c20","be582e5d-b123-41a2-a150-454c39e961c8","c70141d4-ba06-41ea-bcbc-35ea81cb8335","7d1826f4-24c4-4aac-8c2e-eff45df37783","063c97d3-033a-4e9b-98d8-05c8074922cb","00000000-0000-0000-0000-000000000120"};
-const int COLLISION_SOUNDS_SIZE=29;
-
-// NaClAntiSpamQueueEntry
-
-NACLAntiSpamQueueEntry::NACLAntiSpamQueueEntry()
-{
- entryTime=0;
- entryAmount=0;
- blocked=false;
+ for (const auto& collision : COLLISION_SOUNDS)
+ if (collision == sound)
+ return true;
+ return false;
}
-void NACLAntiSpamQueueEntry::clearEntry()
-{
- entryTime=0;
- entryAmount=0;
- blocked=false;
-}
-U32 NACLAntiSpamQueueEntry::getEntryAmount() const
-{
- return entryAmount;
-}
-U32 NACLAntiSpamQueueEntry::getEntryTime() const
-{
- return entryTime;
-}
-void NACLAntiSpamQueueEntry::updateEntryAmount()
-{
- entryAmount++;
-}
-void NACLAntiSpamQueueEntry::updateEntryTime()
-{
- entryTime=time(0);
-}
-void NACLAntiSpamQueueEntry::setBlocked()
-{
- blocked=true;
-}
-bool NACLAntiSpamQueueEntry::getBlocked() const
-{
- return blocked;
-}
-
-// NaClAntiSpamQueue
-
-NACLAntiSpamQueue::NACLAntiSpamQueue(U32 time, U32 amount)
-{
- queueTime=time;
- queueAmount=amount;
-}
-void NACLAntiSpamQueue::setAmount(U32 amount)
-{
- queueAmount=amount;
-}
-void NACLAntiSpamQueue::setTime(U32 time)
-{
- queueTime=time;
-}
-U32 NACLAntiSpamQueue::getAmount() const
-{
- return queueAmount;
-}
-U32 NACLAntiSpamQueue::getTime() const
-{
- return queueTime;
-}
-void NACLAntiSpamQueue::clearEntries()
-{
- for(it = entries.begin(); it != entries.end(); it++)
- {
- it->second->clearEntry();
- }
-}
-void NACLAntiSpamQueue::purgeEntries()
-{
- for(it = entries.begin(); it != entries.end(); it++)
- {
- delete it->second;
- }
- entries.clear();
-}
-void NACLAntiSpamQueue::blockEntry(LLUUID& source)
-{
- it=entries.find(source.asString());
- if(it == entries.end())
- {
- entries[source.asString()]=new NACLAntiSpamQueueEntry();
- }
- entries[source.asString()]->setBlocked();
-}
-int NACLAntiSpamQueue::checkEntry(LLUUID& name, U32 multiplier)
-// Returns 0 if unblocked/disabled, 1 if check results in a new block, 2 if by an existing block
-{
- static LLCachedControl enabled(gSavedSettings,"AntiSpamEnabled",false);
- if(!enabled) return 0;
- it=entries.find(name.asString());
- if(it != entries.end())
- {
- if(it->second->getBlocked()) return 2;
- U32 eTime=it->second->getEntryTime();
- U32 currentTime=time(0);
- if((currentTime-eTime) <= queueTime)
- {
- it->second->updateEntryAmount();
- U32 eAmount=it->second->getEntryAmount();
- if(eAmount > (queueAmount*multiplier))
- {
- it->second->setBlocked();
- return 1;
- }
- else
- return 0;
- }
- else
- {
- it->second->clearEntry();
- it->second->updateEntryAmount();
- it->second->updateEntryTime();
- return 0;
- }
- }
- else
- {
- //LL_DEBUGS() << "[antispam] New queue entry:" << name.asString() << LL_ENDL;
- entries[name.asString()]=new NACLAntiSpamQueueEntry();
- entries[name.asString()]->updateEntryAmount();
- entries[name.asString()]->updateEntryTime();
- return 0;
- }
-}
-
// NaClAntiSpamRegistry
-static const char* QUEUE_NAME[NACLAntiSpamRegistry::QUEUE_MAX] = {
-"Chat",
-"Inventory",
-"Instant Message",
-"calling card",
-"sound",
-"Sound Preload",
-"Script Dialog",
-"Teleport"};
+constexpr std::array QUEUE_NAME = {
+ "Chat",
+ "Inventory",
+ "Instant Message",
+ "calling card",
+ "sound",
+ "Sound Preload",
+ "Script Dialog",
+ "Teleport"
+};
-NACLAntiSpamRegistry::NACLAntiSpamRegistry(U32 time, U32 amount)
+NACLAntiSpamRegistry::NACLAntiSpamRegistry()
{
- globalTime=time;
- globalAmount=amount;
- static LLCachedControl _NACL_AntiSpamGlobalQueue(gSavedSettings,"_NACL_AntiSpamGlobalQueue");
- bGlobalQueue=_NACL_AntiSpamGlobalQueue;
- for(int queue = 0; queue < QUEUE_MAX; ++queue)
+ auto control = gSavedSettings.getControl("_NACL_AntiSpamTime");
+ const U32 time = control->get().asInteger();
+ mConnections[0] = control->getSignal()->connect(boost::bind(&NACLAntiSpamRegistry::handleNaclAntiSpamTimeChanged, _2));
+
+ control = gSavedSettings.getControl("_NACL_AntiSpamAmount");
+ const U32 amount = control->get().asInteger();
+ mConnections[1] = control->getSignal()->connect(boost::bind(&NACLAntiSpamRegistry::handleNaclAntiSpamAmountChanged, _2));
+
+ control = gSavedSettings.getControl("_NACL_AntiSpamGlobalQueue");
+ mConnections[2] = control->getSignal()->connect(boost::bind(&NACLAntiSpamRegistry::handleNaclAntiSpamGlobalQueueChanged, _2));
+ initializeQueues(control->get(), time, amount);
+}
+
+void NACLAntiSpamRegistry::initializeQueues(bool global, const U32& time, const U32& amount)
+{
+ if (global) // If Global, initialize global queue
+ mGlobalQueue.reset(new NACLAntiSpamQueue(time, amount));
+ else
{
- queues[queue] = new NACLAntiSpamQueue(time,amount);
+ mQueues.reset(new std::array{{
+ NACLAntiSpamQueue(time, amount), // QUEUE_CHAT
+ NACLAntiSpamQueue(time, amount), // QUEUE_INVENTORY
+ NACLAntiSpamQueue(time, amount), // QUEUE_IM
+ NACLAntiSpamQueue(time, amount), // QUEUE_CALLING_CARD
+ NACLAntiSpamQueue(time, amount), // QUEUE_SOUND
+ NACLAntiSpamQueue(time, amount), // QUEUE_SOUND_PRELOAD
+ NACLAntiSpamQueue(time, amount), // QUEUE_SCRIPT_DIALOG
+ NACLAntiSpamQueue(time, amount) // QUEUE_TELEPORT
+ }});
}
}
-//static
-const char* NACLAntiSpamRegistry::getQueueName(U32 queue_id)
+
+constexpr const char* getQueueName(const NACLAntiSpamRegistry::Type& name)
{
- if(queue_id >= QUEUE_MAX)
- return "Unknown";
- return QUEUE_NAME[queue_id];
+ return name >= QUEUE_NAME.size() ? "Unknown" : QUEUE_NAME[name];
}
-//static
-void NACLAntiSpamRegistry::registerQueues(U32 time, U32 amount)
-{
- globalTime=time;
- globalAmount=amount;
- static LLCachedControl _NACL_AntiSpamGlobalQueue(gSavedSettings,"_NACL_AntiSpamGlobalQueue");
- bGlobalQueue=_NACL_AntiSpamGlobalQueue;
- for(int queue = 0; queue < QUEUE_MAX; ++queue)
- {
- queues[queue] = new NACLAntiSpamQueue(time,amount);
- }
-}
-//static
-/*void NACLAntiSpamRegistry::registerQueue(U32 name, U32 time, U32 amount)
-{
- it=queues.find(name);
- if(it == queues.end())
- {
- queues[name]=new NACLAntiSpamQueue(time,amount);
- }
-}*/
-//static
-void NACLAntiSpamRegistry::setRegisteredQueueTime(U32 name, U32 time)
-{
- if(name >= QUEUE_MAX || queues[name] == 0)
- {
- LL_ERRS("AntiSpam") << "CODE BUG: Attempting to set time of antispam queue that was outside of the reasonable range of queues or not created. Queue: " << getQueueName(name) << LL_ENDL;
- return;
- }
-
- queues[name]->setTime(time);
-}
-//static
-void NACLAntiSpamRegistry::setRegisteredQueueAmount(U32 name, U32 amount)
-{
- if(name >= QUEUE_MAX || queues[name] == 0)
- {
- LL_ERRS("AntiSpam") << "CODE BUG: Attempting to set amount for antispam queue that was outside of the reasonable range of queues or not created. Queue: " << getQueueName(name) << LL_ENDL;
- return;
- }
-
- queues[name]->setAmount(amount);
-}
-//static
+
void NACLAntiSpamRegistry::setAllQueueTimes(U32 time)
{
- globalTime=time;
- for(int queue = 0; queue < QUEUE_MAX; ++queue)
- if( queues[queue] )
- queues[queue]->setTime(time);
+ if (mGlobalQueue) mGlobalQueue->setTime(time);
+ else for(auto& queue : *mQueues) queue.setTime(time);
}
-//static
+
void NACLAntiSpamRegistry::setAllQueueAmounts(U32 amount)
{
- globalAmount=amount;
- for(int queue = 0; queue < QUEUE_MAX; ++queue)
+ if (mGlobalQueue) mGlobalQueue->setAmount(amount);
+ else for (U8 queue = 0U; queue < QUEUE_MAX; ++queue)
{
- if(!queues[queue]) continue;
- if(queue == QUEUE_SOUND || queue == QUEUE_SOUND_PRELOAD)
- queues[queue]->setAmount(amount*5);
+ auto& q = (*mQueues)[queue];
+ if (queue == QUEUE_SOUND || queue == QUEUE_SOUND_PRELOAD)
+ q.setAmount(amount*5);
else
- queues[queue]->setAmount(amount);
+ q.setAmount(amount);
}
}
-//static
-void NACLAntiSpamRegistry::clearRegisteredQueue(U32 name)
+
+void NACLAntiSpamRegistry::blockOnQueue(const Type& name, const LLUUID& source)
{
- if(name >= QUEUE_MAX || queues[name] == 0)
- {
- LL_ERRS("AntiSpam") << "CODE BUG: Attempting to clear antispam queue that was outside of the reasonable range of queues or not created. Queue: " << getQueueName(name) << LL_ENDL;
- return;
- }
-
- queues[name]->clearEntries();
+ if (name >= QUEUE_MAX)
+ LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was outside of the reasonable range of queues. Queue: " << getQueueName(name) << LL_ENDL;
+ else (mGlobalQueue ? *mGlobalQueue : (*mQueues)[name]).block(source);
}
-//static
-void NACLAntiSpamRegistry::purgeRegisteredQueue(U32 name)
-{
- if(name >= QUEUE_MAX || queues[name] == 0)
- {
- LL_ERRS("AntiSpam") << "CODE BUG: Attempting to purge antispam queue that was outside of the reasonable range of queues or not created. Queue: " << getQueueName(name) << LL_ENDL;
- return;
- }
-
- queues[name]->purgeEntries();
-}
-//static
-void NACLAntiSpamRegistry::blockOnQueue(U32 name, LLUUID& source)
-{
- if(bGlobalQueue)
- {
- NACLAntiSpamRegistry::blockGlobalEntry(source);
- }
- else
- {
- if(name >= QUEUE_MAX || queues[name] == 0)
- {
- LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was outside of the reasonable range of queues or not created. Queue: " << getQueueName(name) << LL_ENDL;
- return;
- }
- queues[name]->blockEntry(source);
- }
-}
-//static
-void NACLAntiSpamRegistry::blockGlobalEntry(LLUUID& source)
-{
- it2=globalEntries.find(source.asString());
- if(it2 == globalEntries.end())
- {
- globalEntries[source.asString()]=new NACLAntiSpamQueueEntry();
- }
- globalEntries[source.asString()]->setBlocked();
-}
-//static
-bool NACLAntiSpamRegistry::checkQueue(U32 name, LLUUID& source, U32 multiplier)
+
+bool NACLAntiSpamRegistry::checkQueue(const Type& name, const LLUUID& source, const LFIDBearer::Type& type, const U32& multiplier)
//returns true if blocked
{
+ if (name >= QUEUE_MAX)
+ {
+ LL_ERRS("AntiSpam") << "CODE BUG: Attempting to check antispam queue that was outside of the reasonable range of queues. Queue: " << getQueueName(name) << LL_ENDL;
+ return false;
+ }
+
if (!can_block(source)) return false;
- int result;
- if(bGlobalQueue)
+ auto& queue = mGlobalQueue ? *mGlobalQueue : (*mQueues)[name];
+ const auto result = queue.check(source, multiplier);
+ if (!result) return false; // Safe
+
+ if (result != 2 // Not previously blocked
+ && gSavedSettings.getBOOL("AntiSpamNotify")) // and Just blocked!
{
- result=NACLAntiSpamRegistry::checkGlobalEntry(source,multiplier);
- }
- else
- {
- if(name >= QUEUE_MAX || queues[name] == 0)
- {
- LL_ERRS("AntiSpam") << "CODE BUG: Attempting to check antispam queue that was outside of the reasonable range of queues or not created. Queue: " << getQueueName(name) << LL_ENDL;
- return false;
- }
- result=queues[name]->checkEntry(source,multiplier);
- }
- if(result == 0) //Safe
- return false;
- else if(result == 2) //Previously blocked
- {
- return true;
- }
- else //Just blocked!
- {
- if(gSavedSettings.getBOOL("AntiSpamNotify"))
- {
- LLSD args;
- args["SOURCE"] = source.asString().c_str();
- args["TYPE"] = LLTrans::getString(getQueueName(name));
- args["AMOUNT"] = boost::lexical_cast(multiplier * queues[name]->getAmount());
- args["TIME"] = boost::lexical_cast(queues[name]->getTime());
- LLNotificationsUtil::add("AntiSpamBlock", args);
- }
- return true;
+ const std::string get_slurl_for(const LLUUID& id, const LFIDBearer::Type& type);
+ const auto slurl = get_slurl_for(source, type);
+ LLSD args;
+ args["SOURCE"] = slurl.empty() ? source.asString() : slurl;
+ args["TYPE"] = LLTrans::getString(getQueueName(name));
+ args["AMOUNT"] = (LLSD::Integer)(multiplier * queue.getAmount());
+ args["TIME"] = (LLSD::Integer)queue.getTime();
+ LLNotificationsUtil::add("AntiSpamBlock", args);
}
+ return true;
}
-// Global queue stoof
-//static
-void NACLAntiSpamRegistry::setGlobalQueue(bool value)
+void NACLAntiSpamRegistry::idle()
{
- NACLAntiSpamRegistry::purgeAllQueues();
- bGlobalQueue=value;
+ if (mGlobalQueue) mGlobalQueue->idle();
+ else for (auto& queue : *mQueues) queue.idle();
}
-//static
-void NACLAntiSpamRegistry::setGlobalAmount(U32 amount)
+
+void NACLAntiSpamRegistry::resetQueues()
{
- globalAmount=amount;
+ if (mGlobalQueue) mGlobalQueue->reset();
+ else for (auto& queue : *mQueues) queue.reset();
+
+ LL_INFOS() << "AntiSpam Queues Reset" << LL_ENDL;
}
-//static
-void NACLAntiSpamRegistry::setGlobalTime(U32 time)
-{
- globalTime=time;
-}
-//static
-void NACLAntiSpamRegistry::clearAllQueues()
-{
- if(bGlobalQueue)
- NACLAntiSpamRegistry::clearGlobalEntries();
- else
- for(int queue = 0; queue < QUEUE_MAX; ++queue)
- {
- if(queues[queue]) queues[queue]->clearEntries();
- }
-}
-//static
+
void NACLAntiSpamRegistry::purgeAllQueues()
{
- if(bGlobalQueue)
- NACLAntiSpamRegistry::purgeGlobalEntries();
+ // Note: These resets are upon the unique_ptr, not the Queue itself!
+ if (mGlobalQueue)
+ mGlobalQueue.reset();
else
- for(int queue = 0; queue < QUEUE_MAX; ++queue)
- {
- if(queues[queue]) queues[queue]->purgeEntries();
- }
- LL_INFOS() << "AntiSpam Queues Purged" << LL_ENDL;
-}
-//static
-int NACLAntiSpamRegistry::checkGlobalEntry(LLUUID& name, U32 multiplier)
-{
- static LLCachedControl enabled(gSavedSettings,"AntiSpamEnabled",false);
- if(!enabled) return 0;
- it2=globalEntries.find(name.asString());
- if(it2 != globalEntries.end())
- {
- if(it2->second->getBlocked()) return 2;
- U32 eTime=it2->second->getEntryTime();
- U32 currentTime=time(0);
- if((currentTime-eTime) <= globalTime)
- {
- it2->second->updateEntryAmount();
- U32 eAmount=it2->second->getEntryAmount();
- if(eAmount > (globalAmount*multiplier))
- return 1;
- else
- return 0;
- }
- else
- {
- it2->second->clearEntry();
- it2->second->updateEntryAmount();
- it2->second->updateEntryTime();
- return 0;
- }
- }
- else
- {
- globalEntries[name.asString()]=new NACLAntiSpamQueueEntry();
- globalEntries[name.asString()]->updateEntryAmount();
- globalEntries[name.asString()]->updateEntryTime();
- return 0;
- }
-}
-//static
-void NACLAntiSpamRegistry::clearGlobalEntries()
-{
- for(it2 = globalEntries.begin(); it2 != globalEntries.end(); it2++)
- {
- it2->second->clearEntry();
- }
-}
-//static
-void NACLAntiSpamRegistry::purgeGlobalEntries()
-{
- for(it2 = globalEntries.begin(); it2 != globalEntries.end(); it2++)
- {
- delete it2->second;
- it2->second = 0;
- }
- globalEntries.clear();
+ mQueues.reset();
}
-//Handlers
-//static
+// Handlers
+// static
+void NACLAntiSpamRegistry::startup()
+{
+ auto onAntiSpamToggle = [](const LLControlVariable*, const LLSD& value) {
+ if (value.asBoolean()) instance();
+ else deleteSingleton();
+ };
+ auto control = gSavedSettings.getControl("AntiSpamEnabled");
+ control->getSignal()->connect(onAntiSpamToggle);
+ onAntiSpamToggle(control, control->get());
+}
+
+// static
bool NACLAntiSpamRegistry::handleNaclAntiSpamGlobalQueueChanged(const LLSD& newvalue)
{
- setGlobalQueue(newvalue.asBoolean());
+ if (instanceExists())
+ {
+ auto& inst = instance();
+ inst.purgeAllQueues();
+ inst.initializeQueues(newvalue.asBoolean(), gSavedSettings.getU32("_NACL_AntiSpamTime"), gSavedSettings.getU32("_NACL_AntiSpamAmount"));
+ }
return true;
}
//static
bool NACLAntiSpamRegistry::handleNaclAntiSpamTimeChanged(const LLSD& newvalue)
{
- setAllQueueTimes(newvalue.asInteger());
+ if (auto inst = getIfExists()) inst->setAllQueueTimes(newvalue.asInteger());
return true;
}
//static
bool NACLAntiSpamRegistry::handleNaclAntiSpamAmountChanged(const LLSD& newvalue)
{
- setAllQueueAmounts(newvalue.asInteger());
+ if (auto inst = getIfExists()) inst->setAllQueueAmounts(newvalue.asInteger());
return true;
-}
-
+}
\ No newline at end of file
diff --git a/indra/newview/NACLantispam.h b/indra/newview/NACLantispam.h
index 321bbde72..33c6d39d7 100644
--- a/indra/newview/NACLantispam.h
+++ b/indra/newview/NACLantispam.h
@@ -13,72 +13,22 @@
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*/
-#ifndef NACLANTISPAM_H
-#define NACLANTISPAM_H
+#pragma once
+#include
#include
#include "stdtypes.h"
+#include "lfidbearer.h"
#include "lluuid.h"
-class NACLAntiSpamQueueEntry
+
+class NACLAntiSpamQueue;
+class NACLAntiSpamRegistry final : public LLSingleton
{
- friend class NACLAntiSpamQueue;
- friend class NACLAntiSpamRegistry;
-protected:
- NACLAntiSpamQueueEntry();
- void clearEntry();
- U32 getEntryAmount() const;
- U32 getEntryTime() const;
- void updateEntryAmount();
- void updateEntryTime();
- bool getBlocked() const;
- void setBlocked();
-private:
- U32 entryAmount;
- U32 entryTime;
- bool blocked;
-};
-class NACLAntiSpamQueue
-{
- friend class NACLAntiSpamRegistry;
+ friend class LLSingleton;
+ NACLAntiSpamRegistry();
public:
- U32 getAmount() const;
- U32 getTime() const;
-protected:
- NACLAntiSpamQueue(U32 time, U32 amount);
- void setAmount(U32 amount);
- void setTime(U32 time);
- void clearEntries();
- void purgeEntries();
- void blockEntry(LLUUID& source);
- int checkEntry(LLUUID& source, U32 multiplier);
-private:
- boost::unordered_map entries;
- boost::unordered_map::iterator it;
- U32 queueAmount;
- U32 queueTime;
-};
-class NACLAntiSpamRegistry
-{
-public:
- NACLAntiSpamRegistry(U32 time=2, U32 amount=10);
- static void registerQueues(U32 time=2, U32 amount=10);
-// static void registerQueue(U32 name, U32 time, U32 amount);
- static void setRegisteredQueueTime(U32 name, U32 time);
- static void setRegisteredQueueAmount(U32 name,U32 amount);
- static void setAllQueueTimes(U32 amount);
- static void setAllQueueAmounts(U32 time);
- static bool checkQueue(U32 name, LLUUID& source, U32 multiplier=1);
- static bool handleNaclAntiSpamGlobalQueueChanged(const LLSD& newvalue);
- static bool handleNaclAntiSpamTimeChanged(const LLSD& newvalue);
- static bool handleNaclAntiSpamAmountChanged(const LLSD& newvalue);
- static void clearRegisteredQueue(U32 name);
- static void purgeRegisteredQueue(U32 name);
- static void clearAllQueues();
- static void purgeAllQueues();
- static void setGlobalQueue(bool value);
- static void setGlobalAmount(U32 amount);
- static void setGlobalTime(U32 time);
- static void blockOnQueue(U32 name,LLUUID& owner_id);
- enum {
+ static void startup();
+
+ enum Type : U8 {
QUEUE_CHAT,
QUEUE_INVENTORY,
QUEUE_IM,
@@ -89,22 +39,19 @@ public:
QUEUE_TELEPORT,
QUEUE_MAX
};
+ bool checkQueue(const Type& name, const LLUUID& source, const LFIDBearer::Type& type = LFIDBearer::AVATAR, const U32& multiplier = 1);
+ void blockOnQueue(const Type& name, const LLUUID& owner_id);
+ void idle();
+ void resetQueues();
private:
- static const char* getQueueName(U32 queue_id);
- static NACLAntiSpamQueue* queues[QUEUE_MAX];
- static boost::unordered_map globalEntries;
- static boost::unordered_map::iterator it2;
- static U32 globalTime;
- static U32 globalAmount;
- static bool bGlobalQueue;
-
- static int checkGlobalEntry(LLUUID& source, U32 multiplier);
- static void clearGlobalEntries();
- static void purgeGlobalEntries();
- static void blockGlobalEntry(LLUUID& source);
+ void setAllQueueTimes(U32 amount);
+ void setAllQueueAmounts(U32 time);
+ void purgeAllQueues();
+ void initializeQueues(bool global, const U32& time, const U32& amount);
+ static bool handleNaclAntiSpamGlobalQueueChanged(const LLSD& newvalue);
+ static bool handleNaclAntiSpamTimeChanged(const LLSD& newvalue);
+ static bool handleNaclAntiSpamAmountChanged(const LLSD& newvalue);
+ std::unique_ptr> mQueues;
+ std::unique_ptr mGlobalQueue;
+ std::array mConnections;
};
-
-extern const std::string COLLISION_SOUNDS[];
-extern const int COLLISION_SOUNDS_SIZE;
-
-#endif //NACLANTISPAM_H
diff --git a/indra/newview/alfloaterregiontracker.cpp b/indra/newview/alfloaterregiontracker.cpp
index cc58da211..f95967367 100644
--- a/indra/newview/alfloaterregiontracker.cpp
+++ b/indra/newview/alfloaterregiontracker.cpp
@@ -117,6 +117,9 @@ void ALFloaterRegionTracker::refresh()
{
saved_selected_values.push_back(item->getValue().asString());
}
+ S32 saved_scroll_pos = mRegionScrollList->getScrollPos();
+ auto sort_column_name = mRegionScrollList->getSortColumnName();
+ auto sort_asending = mRegionScrollList->getSortAscending();
mRegionScrollList->deleteAllItems();
const std::string& cur_region_name = gAgent.getRegion()->getName();
@@ -172,8 +175,11 @@ void ALFloaterRegionTracker::refresh()
mRegionScrollList->addRow(row);
}
}
+
+ mRegionScrollList->sortByColumn(sort_column_name, sort_asending);
if (!saved_selected_values.empty())
mRegionScrollList->selectMultiple(saved_selected_values);
+ mRegionScrollList->setScrollPos(saved_scroll_pos);
}
BOOL ALFloaterRegionTracker::tick()
diff --git a/indra/newview/app_settings/default_grids.xml b/indra/newview/app_settings/default_grids.xml
index f258b23d3..78c82ebdb 100644
--- a/indra/newview/app_settings/default_grids.xml
+++ b/indra/newview/app_settings/default_grids.xml
@@ -2,7 +2,7 @@
- default_grids_version22
+ default_grids_version23
@@ -11,7 +11,7 @@
gridnameSecond Life
platformSecondLife
loginurihttps://login.agni.lindenlab.com/cgi-bin/login.cgi
- loginpagehttp://viewer-login.agni.lindenlab.com/
+ loginpagehttps://viewer-splash.secondlife.com/
helperurihttps://secondlife.com/helpers/
websitehttp://secondlife.com/
supporthttp://secondlife.com/support/
@@ -26,7 +26,7 @@
gridnicksecondlife_beta
gridnameSecond Life BETA
helperurihttp://aditi-secondlife.webdev.lindenlab.com/helpers/
- loginpagehttp://viewer-login.agni.lindenlab.com/
+ loginpagehttps://viewer-splash.secondlife.com/
loginurihttps://login.aditi.lindenlab.com/cgi-bin/login.cgi
passwordhttp://secondlife.com/account/request.php
platformSecondLife
diff --git a/indra/newview/app_settings/lsl_functions_sl.xml b/indra/newview/app_settings/lsl_functions_sl.xml
index 7799b74a4..7bb14209c 100644
--- a/indra/newview/app_settings/lsl_functions_sl.xml
+++ b/indra/newview/app_settings/lsl_functions_sl.xml
@@ -1052,5 +1052,28 @@
llGetMaxScaleFactor
+
+ llAgentInExperience
+
+ llGetExperienceDetails
+
+ llRequestExperiencePermissions
+
+ llReadKeyValue
+
+ llCreateKeyValue
+
+ llUpdateKeyValue
+
+ llDeleteKeyValue
+
+ llDataSizeKeyValue
+
+ llKeysKeyValue
+
+ llKeyCountKeyValue
+
+ llGetExperienceErrorMessage
+
diff --git a/indra/newview/app_settings/mime_types.xml b/indra/newview/app_settings/mime_types.xml
index 0f78823b3..9182f5742 100644
--- a/indra/newview/app_settings/mime_types.xml
+++ b/indra/newview/app_settings/mime_types.xml
@@ -133,6 +133,17 @@
media_plugin_libvlc
+
+
+
+ movie
+
+
+ media_plugin_example
+
+
IsCOA
1
+ LogChatColor
+
+ Comment
+ Color of chat messages loaded from your log
+ Persist
+ 1
+ Type
+ Color4
+ Value
+
+ 0.5
+ 0.5
+ 0.5
+ 1.0
+
+ IsCOA
+ 1
+
UISndAlert
Comment
@@ -2538,6 +2556,17 @@ This should be as low as possible, but too low may break functionality
1
+ CrashHostUrl
+
+ Comment
+ A URL pointing to a crash report handler; overrides cluster negotiation to locate crash handler.
+ Persist
+ 1
+ Type
+ String
+ Value
+
+
AFKTimeout
Comment
@@ -7482,6 +7511,54 @@ This should be as low as possible, but too low may break functionality
0
+ FloaterExperiencePickerRect
+
+ Comment
+ Rectangle for experience picker floater
+ Persist
+ 1
+ Type
+ Rect
+ Value
+
+ 300
+ 600
+ 800
+ 700
+
+
+ FloaterExperienceProfileRect
+
+ Comment
+ Rectangle for experience profile floaters
+ Persist
+ 1
+ Type
+ Rect
+ Value
+
+ 377
+ 650
+ 345
+ 128
+
+
+ FloaterExperiencesRect
+
+ Comment
+ Rectangle for experiences floater
+ Persist
+ 1
+ Type
+ Rect
+ Value
+
+ 300
+ 600
+ 700
+ 700
+
+
FloaterFindRect2
Comment
@@ -8239,7 +8316,7 @@ This should be as low as possible, but too low may break functionality
0
512
- 480
+ 500
0
@@ -11659,17 +11736,6 @@ This should be as low as possible, but too low may break functionality
Value
344
- RevokePermsOnStandUp
-
- Comment
- When enabled, revokes any permission granted to an object you don't own and from which your avatar is standing up
- Persist
- 1
- Type
- Boolean
- Value
- 0
-
PickerContextOpacity
Comment
@@ -14561,6 +14627,17 @@ This should be as low as possible, but too low may break functionality
Value
1
+ RevokePermsOnStandUp
+
+ Comment
+ When enabled, revokes any permission granted to an object you don't own and from which your avatar is standing up
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
RevokePermsOnStopAnimation
Comment
diff --git a/indra/newview/app_settings/settings_ascent.xml b/indra/newview/app_settings/settings_ascent.xml
index 65398b513..0c74e3164 100644
--- a/indra/newview/app_settings/settings_ascent.xml
+++ b/indra/newview/app_settings/settings_ascent.xml
@@ -91,6 +91,17 @@
Value
1
+ ProfileNameSystem
+
+ Comment
+ For a name displayed on its profile. 0 = Old Style, 1 = Display Names and Username, 2 = Displayname only, 3 = Old Style (Display Name)
+ Persist
+ 1
+ Type
+ S32
+ Value
+ 1
+
RadarNameSystem
Comment
@@ -135,6 +146,28 @@
Value
/resync
+ AlchemyChatCommandSetChatChannel
+
+ Comment
+ Command to set nearby chat channel
+ Persist
+ 1
+ Type
+ String
+ Value
+ /setchannel
+
+ AlchemyChatCommandSetHome
+
+ Comment
+ Command to set the avatar home
+ Persist
+ 1
+ Type
+ String
+ Value
+ /sethome
+
AlchemyConnectToNeighbors
Comment
@@ -201,6 +234,17 @@
Value
1
+ AlchemyNearbyChatChannel
+
+ Comment
+ Chat channel used for sending nearby chat from the viewer
+ Persist
+ 0
+ Type
+ S32
+ Value
+ 0
+
AlchemyRainbowEffects
Comment
@@ -521,6 +565,17 @@
Value
1
+ AlchemyDisableSimCamConstraint
+
+ Comment
+ Disable the push the simulator applies when camera collides with objects
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+
AscentFlyAlwaysEnabled
Comment
@@ -763,6 +818,17 @@
Value
key2name
+ AscentCmdLineKeyToNameNameSystem
+
+ Comment
+ For key to name command defined by AscentCmdLineKeyToName, the format to show the key's name in. 0 = Old Style, 1 = Display Names and Username, 2 = Displayname only, 3 = Old Style (Display Name)
+ Persist
+ 1
+ Type
+ S32
+ Value
+ 1
+
AscentCmdLineOfferTp
Comment
@@ -840,17 +906,6 @@
Value
/open
- SinguCompleteNameProfiles
-
- Comment
- Use the complete name "Display Name (legacy.name)" in profiles, instead of following the choice set by PhoenixNameSystem.
- Persist
- 1
- Type
- Boolean
- Value
- 0
-
SinguDefaultEaseIn
Comment
@@ -873,6 +928,17 @@
Value
0
+ SinguFollowDistance
+
+ Comment
+ Distance at which to follow objects and avatars at, from menus that offer follow (minimum 0.5)
+ Persist
+ 1
+ Type
+ F32
+ Value
+ 1.0
+
SinguLastKnownReleaseBuild
Comment
@@ -1109,6 +1175,17 @@ Changing this setting only affects new text.
Value
1
+ LiruUseMarkedColor
+
+ Comment
+ Whether or not to use the color of marks done on radar and minimap to color tags and chat from those individuals
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 1
+
OBJExportNotifyFailed
Comment
@@ -1567,6 +1644,19 @@ Changing this setting only affects new text.
IsCOA
1
+ ToolbarVisibleExperiences
+
+ Comment
+ Whether or not the button for the experiences floater is on the toolbar
+ Persist
+ 1
+ Type
+ Boolean
+ Value
+ 0
+ IsCOA
+ 1
+
ToolbarVisibleFastTimers
Comment
diff --git a/indra/newview/ascentprefschat.cpp b/indra/newview/ascentprefschat.cpp
index 084cc448f..25d196435 100644
--- a/indra/newview/ascentprefschat.cpp
+++ b/indra/newview/ascentprefschat.cpp
@@ -70,7 +70,10 @@ LLPrefsAscentChat::LLPrefsAscentChat()
}
childSetEnabled("reset_antispam", started);
- getChild("reset_antispam")->setCommitCallback(boost::bind(NACLAntiSpamRegistry::purgeAllQueues));
+ getChild("reset_antispam")->setCommitCallback([](LLUICtrl* ctrl, const LLSD& param) {
+ if (auto inst = NACLAntiSpamRegistry::getIfExists())
+ inst->resetQueues();
+ });
getChild("autoreplace")->setCommitCallback(boost::bind(LLFloaterAutoReplaceSettings::showInstance, LLSD()));
getChild("KeywordsOn")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1));
@@ -257,6 +260,7 @@ void LLPrefsAscentChat::refreshValues()
mFriendNames = gSavedSettings.getS32("FriendNameSystem");
mGroupMembersNames = gSavedSettings.getS32("GroupMembersNameSystem");
mLandManagementNames = gSavedSettings.getS32("LandManagementNameSystem");
+ mProfileNames = gSavedSettings.getS32("ProfileNameSystem");
mRadarNames = gSavedSettings.getS32("RadarNameSystem");
mSpeakerNames = gSavedSettings.getS32("SpeakerNameSystem");
@@ -454,6 +458,7 @@ void LLPrefsAscentChat::cancel()
gSavedSettings.setS32("FriendNameSystem", mFriendNames);
gSavedSettings.setS32("GroupMembersNameSystem", mGroupMembersNames);
gSavedSettings.setS32("LandManagementNameSystem", mLandManagementNames);
+ gSavedSettings.setS32("ProfileNameSystem", mProfileNames);
gSavedSettings.setS32("RadarNameSystem", mRadarNames);
gSavedSettings.setS32("SpeakerNameSystem", mSpeakerNames);
diff --git a/indra/newview/ascentprefschat.h b/indra/newview/ascentprefschat.h
index 2521b2039..0f0dc9baa 100644
--- a/indra/newview/ascentprefschat.h
+++ b/indra/newview/ascentprefschat.h
@@ -91,6 +91,7 @@ private:
S32 mFriendNames;
S32 mGroupMembersNames;
S32 mLandManagementNames;
+ S32 mProfileNames;
S32 mRadarNames;
S32 mSpeakerNames;
diff --git a/indra/newview/ascentprefsvan.cpp b/indra/newview/ascentprefsvan.cpp
index 0c6216647..0bb200636 100644
--- a/indra/newview/ascentprefsvan.cpp
+++ b/indra/newview/ascentprefsvan.cpp
@@ -130,7 +130,6 @@ void LLPrefsAscentVan::refreshValues()
mAnnounceStreamMetadata = gSavedSettings.getBOOL("AnnounceStreamMetadata");
mInactiveFloaterTransparency = gSavedSettings.getF32("InactiveFloaterTransparency");
mActiveFloaterTransparency = gSavedSettings.getF32("ActiveFloaterTransparency");
- mCompleteNameProfiles = gSavedSettings.getBOOL("SinguCompleteNameProfiles");
mScriptErrorsStealFocus = gSavedSettings.getBOOL("LiruScriptErrorsStealFocus");
mConnectToNeighbors = gSavedSettings.getBOOL("AlchemyConnectToNeighbors");
mRestartMinimized = gSavedSettings.getBOOL("LiruRegionRestartMinimized");
@@ -205,7 +204,6 @@ void LLPrefsAscentVan::cancel()
gSavedSettings.setBOOL("AnnounceStreamMetadata", mAnnounceStreamMetadata);
gSavedSettings.setF32("InactiveFloaterTransparency", mInactiveFloaterTransparency);
gSavedSettings.setF32("ActiveFloaterTransparency", mActiveFloaterTransparency);
- gSavedSettings.setBOOL("SinguCompleteNameProfiles", mCompleteNameProfiles);
gSavedSettings.setBOOL("LiruScriptErrorsStealFocus", mScriptErrorsStealFocus);
gSavedSettings.setBOOL("AlchemyConnectToNeighbors", mConnectToNeighbors);
gSavedSettings.setBOOL("LiruRegionRestartMinimized", mRestartMinimized);
diff --git a/indra/newview/ascentprefsvan.h b/indra/newview/ascentprefsvan.h
index d8e27338e..b29f5147d 100644
--- a/indra/newview/ascentprefsvan.h
+++ b/indra/newview/ascentprefsvan.h
@@ -63,7 +63,6 @@ private:
bool mAnnounceSnapshots;
bool mAnnounceStreamMetadata;
F32 mInactiveFloaterTransparency, mActiveFloaterTransparency;
- bool mCompleteNameProfiles;
bool mScriptErrorsStealFocus;
bool mConnectToNeighbors;
bool mRestartMinimized;
diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp
index c70eec31b..2f5da6a33 100644
--- a/indra/newview/chatbar_as_cmdline.cpp
+++ b/indra/newview/chatbar_as_cmdline.cpp
@@ -236,6 +236,7 @@ struct ProfCtrlListAccum : public LLControlGroup::ApplyFunctor
#endif //PROF_CTRL_CALLS
void spew_key_to_name(const LLUUID& targetKey, const LLAvatarName& av_name)
{
+ static const LLCachedControl ns(gSavedSettings, "AscentCmdLineKeyToNameNameSystem");
cmdline_printchat(llformat("%s: %s", targetKey.asString().c_str(), av_name.getNSName().c_str()));
}
bool cmd_line_chat(std::string data, EChatType type)
@@ -255,12 +256,14 @@ bool cmd_line_chat(std::string data, EChatType type)
static LLCachedControl sPosCommand(gSavedSettings, "AscentCmdLinePos");
static LLCachedControl sRezPlatCommand(gSavedSettings, "AscentCmdLineRezPlatform");
static LLCachedControl sHomeCommand(gSavedSettings, "AscentCmdLineTeleportHome");
+ static LLCachedControl sSetHomeCommand(gSavedSettings, "AlchemyChatCommandSetHome", "/sethome");
static LLCachedControl sCalcCommand(gSavedSettings, "AscentCmdLineCalc");
static LLCachedControl sMapToCommand(gSavedSettings, "AscentCmdLineMapTo");
static LLCachedControl sClearCommand(gSavedSettings, "AscentCmdLineClearChat");
static LLCachedControl sRegionMsgCommand(gSavedSettings, "SinguCmdLineRegionSay");
static LLCachedControl sTeleportToCam(gSavedSettings, "AscentCmdTeleportToCam");
static LLCachedControl sHoverHeight(gSavedSettings, "AlchemyChatCommandHoverHeight", "/hover");
+ static LLCachedControl sSetNearbyChatChannelCmd(gSavedSettings, "AlchemyChatCommandSetChatChannel", "/setchannel");
static LLCachedControl sResyncAnimCommand(gSavedSettings, "AlchemyChatCommandResyncAnim", "/resync");
static LLCachedControl sKeyToName(gSavedSettings, "AscentCmdLineKeyToName");
static LLCachedControl sOfferTp(gSavedSettings, "AscentCmdLineOfferTp");
@@ -364,6 +367,11 @@ bool cmd_line_chat(std::string data, EChatType type)
gAgent.teleportHome();
return false;
}
+ else if (cmd == utf8str_tolower(sSetHomeCommand)) // sethome
+ {
+ gAgent.setStartPosition(START_LOCATION_ID_HOME);
+ return false;
+ }
else if (cmd == utf8str_tolower(sCalcCommand))//Cryogenic Blitz
{
if (data.length() > cmd.length() + 1)
@@ -447,6 +455,15 @@ bool cmd_line_chat(std::string data, EChatType type)
}
return false;
}
+ else if (cmd == utf8str_tolower(sSetNearbyChatChannelCmd)) // Set nearby chat channel
+ {
+ S32 chan;
+ if (input >> chan)
+ {
+ gSavedSettings.setS32("AlchemyNearbyChatChannel", chan);
+ return false;
+ }
+ }
else if (cmd == utf8str_tolower(sTeleportToCam))
{
gAgent.teleportViaLocation(gAgentCamera.getCameraPositionGlobal());
diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt
index 618c8970e..af5aa6a12 100644
--- a/indra/newview/gpu_table.txt
+++ b/indra/newview/gpu_table.txt
@@ -438,6 +438,12 @@ NVIDIA GTX 770 .*NVIDIA .*GTX *77.* 3 1
NVIDIA GTX 780 .*NVIDIA .*GTX *78.* 3 1
NVIDIA GTX 970 .*NVIDIA .*GTX *97.* 3 1
NVIDIA GTX 980 .*NVIDIA .*GTX *98.* 3 1
+NVIDIA GTX 1050 .*NVIDIA.*GTX 105.* 3 1
+NVIDIA GTX 1060 .*NVIDIA.*GTX 106.* 3 1
+NVIDIA GTX 1070 .*NVIDIA.*GTX 107.* 3 1
+NVIDIA GTX 1080 .*NVIDIA.*GTX 108.* 3 1
+NVIDIA GTX 1650 .*NVIDIA.*GTX 165.* 3 1
+NVIDIA GTX 1660 .*NVIDIA.*GTX 166.* 3 1
NVIDIA GTX TITAN .*NVIDIA .*GTX *TITAN.* 3 1
NVIDIA GeForce/Quadro RTX .*(GeForce|Quadro) .*RTX.* 3 1
NVIDIA RTX TITAN .*NVIDIA .*RTX.*TITAN.* 3 1
diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi
index b2d52808a..04c810ed7 100644
--- a/indra/newview/installers/windows/installer_template.nsi
+++ b/indra/newview/installers/windows/installer_template.nsi
@@ -1,6 +1,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Second Life setup.nsi
-;; Copyright 2004-2015, Linden Research, Inc.
+;; secondlife setup.nsi
+;; Copyright 2004-2011, Linden Research, Inc.
+;; Copyright 2013-2015 Alchemy Viewer Project
;;
;; This library is free software; you can redistribute it and/or
;; modify it under the terms of the GNU Lesser General Public
@@ -94,18 +95,6 @@
;File Handling
SetOverwrite on
-;--------------------------------
-;Version Information
-
- VIProductVersion "${VERSION_LONG}"
- VIAddVersionKey "ProductName" "Singularity Viewer Installer"
- VIAddVersionKey "Comments" "A viewer for the meta-verse!"
- VIAddVersionKey "CompanyName" "${VENDORSTR}"
- VIAddVersionKey "LegalCopyright" "Copyright © 2010-2019, ${VENDORSTR}"
- VIAddVersionKey "FileDescription" "${APPNAME} Installer"
- VIAddVersionKey "ProductVersion" "${VERSION_LONG}"
- VIAddVersionKey "FileVersion" "${VERSION_LONG}"
-
;--------------------------------
;Interface Settings
@@ -198,6 +187,18 @@
!include "%%SOURCE%%\installers\windows\lang_tr.nsi"
!include "%%SOURCE%%\installers\windows\lang_zh.nsi"
+;--------------------------------
+;Version Information
+
+ VIProductVersion "${VERSION_LONG}"
+ VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "Singularity Viewer Installer"
+ VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "A viewer for the meta-verse!"
+ VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "${VENDORSTR}"
+ VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright © 2010-2020, ${VENDORSTR}"
+ VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "${APPNAME} Installer"
+ VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "${VERSION_LONG}"
+ VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${VERSION_LONG}"
+
;--------------------------------
;Reserve Files
@@ -206,7 +207,7 @@
;because this will make your installer start faster.
!insertmacro MUI_RESERVEFILE_LANGDLL
- ReserveFile "${NSISDIR}\Plugins\x86-unicode\nsisdl.dll"
+ ReserveFile "${NSISDIR}\Plugins\x86-unicode\NSISdl.dll"
ReserveFile "${NSISDIR}\Plugins\x86-unicode\nsDialogs.dll"
ReserveFile "${NSISDIR}\Plugins\x86-unicode\StartMenu.dll"
ReserveFile "${NSISDIR}\Plugins\x86-unicode\StdUtils.dll"
@@ -408,7 +409,41 @@ Section "Viewer"
;This placeholder is replaced by the complete list of all the files in the installer, by viewer_manifest.py
%%INSTALL_FILES%%
-
+
+ ;Create temp dir and set out dir to it
+ CreateDirectory "$TEMP\AlchemyInst"
+ SetOutPath "$TEMP\AlchemyInst"
+
+ ;Download LibVLC
+!ifdef WIN64_BIN_BUILD
+ NSISdl::download "http://download.videolan.org/pub/videolan/vlc/3.0.8/win64/vlc-3.0.8-win64.7z" "$TEMP\AlchemyInst\libvlc.7z"
+!else
+ NSISdl::download "http://download.videolan.org/pub/videolan/vlc/3.0.8/win32/vlc-3.0.8-win32.7z" "$TEMP\AlchemyInst\libvlc.7z"
+!endif
+ Nsis7z::ExtractWithDetails "$TEMP\AlchemyInst\libvlc.7z" "Unpacking media plugins %s..."
+ Rename "$TEMP\AlchemyInst\vlc-3.0.8\libvlc.dll" "$INSTDIR\llplugin\libvlc.dll"
+ Rename "$TEMP\AlchemyInst\vlc-3.0.8\libvlccore.dll" "$INSTDIR\llplugin\libvlccore.dll"
+ Rename "$TEMP\AlchemyInst\vlc-3.0.8\plugins" "$INSTDIR\llplugin\plugins"
+
+ ;Download and install VC redist
+!ifdef WIN64_BIN_BUILD
+ NSISdl::download "https://aka.ms/vs/16/release/vc_redist.x64.exe" "$TEMP\AlchemyInst\vc_redist_16.x64.exe"
+ ExecWait "$TEMP\AlchemyInst\vc_redist_16.x64.exe /install /passive /norestart"
+
+ NSISdl::download "https://aka.ms/highdpimfc2013x64enu" "$TEMP\AlchemyInst\vc_redist_12.x64.exe"
+ ExecWait "$TEMP\AlchemyInst\vc_redist_12.x64.exe /install /passive /norestart"
+!else
+ NSISdl::download "https://aka.ms/vs/16/release/vc_redist.x86.exe" "$TEMP\AlchemyInst\vc_redist_16.x86.exe"
+ ExecWait "$TEMP\AlchemyInst\vc_redist_16.x86.exe /install /passive /norestart"
+
+ NSISdl::download "https://aka.ms/highdpimfc2013x86enu" "$TEMP\AlchemyInst\vc_redist_12.x86.exe"
+ ExecWait "$TEMP\AlchemyInst\vc_redist_12.x86.exe /install /passive /norestart"
+!endif
+
+ ;Remove temp dir and reset out to inst dir
+ RMDir /r "$TEMP\AlchemyInst\"
+ SetOutPath "$INSTDIR"
+
;Pass the installer's language to the client to use as a default
StrCpy $SHORTCUT_LANG_PARAM "--set InstallLanguage $(LanguageCode)"
@@ -425,6 +460,7 @@ Section "Viewer"
WriteINIStr "$SMPROGRAMS\$STARTMENUFOLDER\SL Create Account.url" "InternetShortcut" "URL" "http://join.secondlife.com/"
WriteINIStr "$SMPROGRAMS\$STARTMENUFOLDER\SL Your Account.url" "InternetShortcut" "URL" "http://www.secondlife.com/account/"
WriteINIStr "$SMPROGRAMS\$STARTMENUFOLDER\SL Scripting Language Help.url" "InternetShortcut" "URL" "http://wiki.secondlife.com/wiki/LSL_Portal"
+
!insertmacro MUI_STARTMENU_WRITE_END
;Other shortcuts
diff --git a/indra/newview/jcfloaterareasearch.cpp b/indra/newview/jcfloaterareasearch.cpp
index 4c5ad09e1..ae970ebc3 100644
--- a/indra/newview/jcfloaterareasearch.cpp
+++ b/indra/newview/jcfloaterareasearch.cpp
@@ -80,13 +80,21 @@ BOOL JCFloaterAreaSearch::postBuild()
mResultList = getChild("result_list");
mResultList->setDoubleClickCallback(boost::bind(&JCFloaterAreaSearch::onDoubleClick,this));
mResultList->sortByColumn("Name", TRUE);
+ auto tp = getChild("TP");
+ auto look = getChild("Look");
+ mResultList->setCommitOnSelectionChange(true);
+ mResultList->setCommitCallback([=](LLUICtrl* ctrl, const LLSD& param){
+ bool enabled = mResultList->getNumSelected() == 1;
+ tp->setEnabled(enabled);
+ look->setEnabled(enabled);
+ });
mCounterText = getChild("counter");
getChild("Refresh")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::onRefresh,this));
getChild("Stop")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::onStop,this));
- getChild("TP")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::teleportToSelected, this));
- getChild("Look")->setClickedCallback(boost::bind(&JCFloaterAreaSearch::lookAtSelected, this));
+ tp->setClickedCallback(boost::bind(&JCFloaterAreaSearch::teleportToSelected, this));
+ look->setClickedCallback(boost::bind(&JCFloaterAreaSearch::lookAtSelected, this));
getChild("Name query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_NAME));
getChild("Description query chunk")->setCommitCallback(boost::bind(&JCFloaterAreaSearch::onCommitLine,this,_1,_2,LIST_OBJECT_DESC));
@@ -291,6 +299,7 @@ void JCFloaterAreaSearch::processObjectPropertiesFamily(LLMessageSystem* msg, vo
// We cache unknown objects (to avoid having to request them later)
// and requested objects.
msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, data->owner_id);
+ if (auto obj = gObjectList.findObject(object_id)) obj->mOwnerID = data->owner_id; // Singu Note: Try to get Owner whenever possible
msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, data->group_id);
msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, data->name);
msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, data->desc);
diff --git a/indra/newview/jcfloaterareasearch.h b/indra/newview/jcfloaterareasearch.h
index 32c40c253..033dc22ad 100644
--- a/indra/newview/jcfloaterareasearch.h
+++ b/indra/newview/jcfloaterareasearch.h
@@ -56,6 +56,20 @@ public:
void results();
static void processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data);
+ struct ObjectData
+ {
+ LLUUID id;
+ std::string name;
+ std::string desc;
+ LLUUID owner_id;
+ LLUUID group_id;
+ };
+
+ const ObjectData* getObjectData(const LLUUID& id) const
+ {
+ const auto& it = mCachedObjects.find(id);
+ return it != mCachedObjects.end() ? &it->second : nullptr;
+ }
private:
@@ -84,14 +98,6 @@ private:
LLViewerRegion* mLastRegion;
bool mStopped;
- struct ObjectData
- {
- LLUUID id;
- std::string name;
- std::string desc;
- LLUUID owner_id;
- LLUUID group_id;
- };
uuid_set_t mPendingObjects;
boost::unordered_map mCachedObjects;
diff --git a/indra/newview/lffloaterinvpanel.cpp b/indra/newview/lffloaterinvpanel.cpp
index be530d1e2..b687100a0 100644
--- a/indra/newview/lffloaterinvpanel.cpp
+++ b/indra/newview/lffloaterinvpanel.cpp
@@ -32,48 +32,38 @@ LFFloaterInvPanel::LFFloaterInvPanel(const LLSD& cat, const std::string& name, L
: LLInstanceTracker(cat)
{
// Setup the floater first
- mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, cat, LLRect(), model ? model : &gInventory, true);
- const auto& title = name.empty() ? gInventory.getCategory(mPanel->getRootFolderID())->getName() : name;
-
- // Figure out a unique name for our rect control
- const auto rect_control = llformat("FloaterInv%sRect", boost::algorithm::erase_all_copy(title, " ").data());
-
- bool existed = gSavedSettings.controlExists(rect_control);
- if (existed) // Set our initial rect to the stored control
- setRectControl(rect_control);
+ auto mPanel = new LLInventoryPanel("inv_panel", LLInventoryPanel::DEFAULT_SORT_ORDER, cat, LLRect(), model ? model : &gInventory, true);
// Load from XUI
- mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, boost::ref(mPanel), _2));
+ mCommitCallbackRegistrar.add("InvPanel.Search", boost::bind(&LLInventoryPanel::setFilterSubString, mPanel, _2));
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_inv_panel.xml");
// Now set the title
+ const auto& title = name.empty() ? gInventory.getCategory(mPanel->getRootFolderID())->getName() : name;
setTitle(title);
- // If we haven't existed before, create and set our rect control now
- if (!existed)
+ // Figure out a unique name for our rect control
+ const auto rect_control = llformat("FloaterInv%sRect", boost::algorithm::erase_all_copy(title, " ").data());
+ if (!gSavedSettings.controlExists(rect_control)) // If we haven't existed before, create it
{
S32 left, top;
gFloaterView->getNewFloaterPosition(&left, &top);
LLRect rect = getRect();
rect.translate(left - rect.mLeft, top - rect.mTop);
- setRect(rect);
gSavedSettings.declareRect(rect_control, rect, "Rectangle for " + title + " window");
- setRectControl(rect_control);
}
+ setRectControl(rect_control);
+ applyRectControl(); // Set our initial rect to the stored (or just created) control
// Now take care of the children
LLPanel* panel = getChild("placeholder_panel");
mPanel->setRect(panel->getRect());
+ mPanel->setOrigin(0, 0);
mPanel->postBuild();
mPanel->setFollows(FOLLOWS_ALL);
mPanel->setEnabled(true);
- addChild(mPanel);
- removeChild(panel);
-}
-
-LFFloaterInvPanel::~LFFloaterInvPanel()
-{
- delete mPanel;
+ mPanel->removeBorder();
+ panel->addChild(mPanel);
}
// static
@@ -100,15 +90,18 @@ void LFFloaterInvPanel::closeAll()
}
}
-// virtual
BOOL LFFloaterInvPanel::handleKeyHere(KEY key, MASK mask)
{
- if (!mPanel->hasFocus() && mask == MASK_NONE && (key == KEY_RETURN || key == KEY_DOWN))
+ if (mask == MASK_NONE && (key == KEY_RETURN || key == KEY_DOWN))
{
- mPanel->setFocus(true);
- if (LLFolderView* root = mPanel->getRootFolder())
- root->scrollToShowSelection();
- return true;
+ auto& mPanel = *getChild("inv_panel");
+ if (!mPanel.hasFocus())
+ {
+ mPanel.setFocus(true);
+ if (LLFolderView* root = mPanel.getRootFolder())
+ root->scrollToShowSelection();
+ return true;
+ }
}
return LLFloater::handleKeyHere(key, mask);
diff --git a/indra/newview/lffloaterinvpanel.h b/indra/newview/lffloaterinvpanel.h
index 724fed98d..c65763cdd 100644
--- a/indra/newview/lffloaterinvpanel.h
+++ b/indra/newview/lffloaterinvpanel.h
@@ -18,18 +18,16 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA */
-#ifndef LFFLOATERINVPANEL_H
-#define LFFLOATERINVPANEL_H
+#pragma once
#include "llfloater.h"
#include "llinstancetracker.h"
#include "llsdutil.h"
-class LFFloaterInvPanel : public LLFloater, public LLInstanceTracker
+class LFFloaterInvPanel final : public LLFloater, public LLInstanceTracker
{
LFFloaterInvPanel(const LLSD& cat, const std::string& name = LLStringUtil::null, class LLInventoryModel* model = nullptr);
- ~LFFloaterInvPanel();
public:
static void show(const LLSD& cat, const std::string& name = LLStringUtil::null, LLInventoryModel* model = nullptr); // Show the floater for cat (create with other params if necessary)
@@ -42,10 +40,5 @@ public:
}
static void closeAll(); // Called when not allowed to have inventory open
- /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
-
-private:
- class LLInventoryPanel* mPanel;
+ BOOL handleKeyHere(KEY key, MASK mask) override;
};
-
-#endif //LFFLOATERINVPANEL_H
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index f5ea77a9d..ed765ff27 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -38,11 +38,14 @@
#include "llanimationstates.h"
#include "llcallingcard.h"
#include "llcapabilitylistener.h"
+#include "llcororesponder.h"
#include "llconsole.h"
#include "llenvmanager.h"
#include "llfirstuse.h"
#include "llfloatercamera.h"
#include "llfloatertools.h"
+#include "llfloaterpostcard.h"
+#include "llfloaterpreference.h"
#include "llgroupactions.h"
#include "llgroupmgr.h"
#include "llhomelocationresponder.h"
@@ -184,10 +187,10 @@ public:
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId);
virtual ~LLTeleportRequestViaLandmark();
- virtual bool canRestartTeleport();
+ bool canRestartTeleport() override;
- virtual void startTeleport();
- virtual void restartTeleport();
+ void startTeleport() override;
+ void restartTeleport() override;
protected:
inline const LLUUID &getLandmarkId() const {return mLandmarkId;};
@@ -196,15 +199,15 @@ private:
LLUUID mLandmarkId;
};
-class LLTeleportRequestViaLure : public LLTeleportRequestViaLandmark
+class LLTeleportRequestViaLure final : public LLTeleportRequestViaLandmark
{
public:
LLTeleportRequestViaLure(const LLUUID &pLureId, BOOL pIsLureGodLike);
virtual ~LLTeleportRequestViaLure();
- virtual bool canRestartTeleport();
+ bool canRestartTeleport() override;
- virtual void startTeleport();
+ void startTeleport() override;
protected:
inline BOOL isLureGodLike() const {return mIsLureGodLike;};
@@ -219,10 +222,10 @@ public:
LLTeleportRequestViaLocation(const LLVector3d &pPosGlobal);
virtual ~LLTeleportRequestViaLocation();
- virtual bool canRestartTeleport();
+ bool canRestartTeleport() override;
- virtual void startTeleport();
- virtual void restartTeleport();
+ void startTeleport() override;
+ void restartTeleport() override;
protected:
inline const LLVector3d &getPosGlobal() const {return mPosGlobal;};
@@ -232,16 +235,16 @@ private:
};
-class LLTeleportRequestViaLocationLookAt : public LLTeleportRequestViaLocation
+class LLTeleportRequestViaLocationLookAt final : public LLTeleportRequestViaLocation
{
public:
LLTeleportRequestViaLocationLookAt(const LLVector3d &pPosGlobal);
virtual ~LLTeleportRequestViaLocationLookAt();
- virtual bool canRestartTeleport();
+ bool canRestartTeleport() override;
- virtual void startTeleport();
- virtual void restartTeleport();
+ void startTeleport() override;
+ void restartTeleport() override;
protected:
@@ -258,12 +261,12 @@ const F32 LLAgent::TYPING_TIMEOUT_SECS = 5.f;
std::map LLAgent::sTeleportErrorMessages;
std::map LLAgent::sTeleportProgressMessages;
-class LLAgentFriendObserver : public LLFriendObserver
+class LLAgentFriendObserver final : public LLFriendObserver
{
public:
- LLAgentFriendObserver() {}
- virtual ~LLAgentFriendObserver() {}
- virtual void changed(U32 mask);
+ LLAgentFriendObserver() = default;
+ virtual ~LLAgentFriendObserver() = default;
+ void changed(U32 mask) override;
};
void LLAgentFriendObserver::changed(U32 mask)
@@ -293,7 +296,7 @@ bool LLAgent::isActionAllowed(const LLSD& sdname)
{
bool allow_agent_voice = false;
LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
- if (channel != NULL)
+ if (channel != nullptr)
{
if (channel->getSessionName().empty() && channel->getSessionID().isNull())
{
@@ -381,6 +384,7 @@ LLAgent::LLAgent() :
mAgentAccess(new LLAgentAccess(gSavedSettings)),
mGodLevelChangeSignal(),
+ mIsCrossingRegion(false),
mCanEditParcel(false),
mTeleportSourceSLURL(new LLSLURL),
mTeleportRequest(),
@@ -907,7 +911,7 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
if (mRegionp)
{
// NaCl - Antispam Registry
- NACLAntiSpamRegistry::purgeAllQueues();
+ if (auto antispam = NACLAntiSpamRegistry::getIfExists()) antispam->resetQueues();
// NaCl End
// We've changed regions, we're now going to change our agent coordinate frame.
@@ -1612,12 +1616,14 @@ void LLAgent::startAutoPilotGlobal(
mAutoPilotFlyOnStop = FALSE;
}
- if (distance > 30.0 && mAutoPilotAllowFlying)
+ bool follow = mAutoPilotBehaviorName == "Follow";
+
+ if (!follow && distance > 30.0 && mAutoPilotAllowFlying)
{
setFlying(TRUE);
}
- if ( distance > 1.f &&
+ if (!follow && distance > 1.f &&
mAutoPilotAllowFlying &&
heightDelta > (sqrtf(mAutoPilotStopDistance) + 1.f))
{
@@ -1689,7 +1695,7 @@ void LLAgent::startFollowPilot(const LLUUID &leader_id, BOOL allow_flying, F32 s
}
startAutoPilotGlobal(object->getPositionGlobal(),
- std::string(), // behavior_name
+ "Follow", // behavior_name
NULL, // target_rotation
NULL, // finish_callback
NULL, // callback_data
@@ -1706,6 +1712,9 @@ void LLAgent::stopAutoPilot(BOOL user_cancel)
{
if (mAutoPilot)
{
+ if (!user_cancel && mAutoPilotBehaviorName == "Follow")
+ return; // Follow means actually follow
+
mAutoPilot = FALSE;
if (mAutoPilotUseRotation && !user_cancel)
{
@@ -1724,7 +1733,16 @@ void LLAgent::stopAutoPilot(BOOL user_cancel)
mAutoPilotFinishedCallback(!user_cancel && dist_vec_squared(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < (mAutoPilotStopDistance * mAutoPilotStopDistance), mAutoPilotCallbackData);
mAutoPilotFinishedCallback = NULL;
}
+
+ // Sit response during follow pilot, now complete, resume follow
+ if (!user_cancel && mAutoPilotBehaviorName == "Sit" && mLeaderID.notNull())
+ {
+ startFollowPilot(mLeaderID, true, gSavedSettings.getF32("SinguFollowDistance"));
+ return;
+ }
+
mLeaderID = LLUUID::null;
+ mAutoPilotNoProgressFrameCount = 0;
setControlFlags(AGENT_CONTROL_STOP);
@@ -1734,6 +1752,8 @@ void LLAgent::stopAutoPilot(BOOL user_cancel)
LLNotificationsUtil::add("CancelledSit");
else if (mAutoPilotBehaviorName == "Attach")
LLNotificationsUtil::add("CancelledAttach");
+ else if (mAutoPilotBehaviorName == "Follow")
+ LLNotificationsUtil::add("CancelledFollow");
else
LLNotificationsUtil::add("Cancelled");
}
@@ -1741,28 +1761,104 @@ void LLAgent::stopAutoPilot(BOOL user_cancel)
}
+bool LLAgent::getAutoPilotNoProgress() const
+{
+ return mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped;
+}
+
// Returns necessary agent pitch and yaw changes, radians.
//-----------------------------------------------------------------------------
// autoPilot()
//-----------------------------------------------------------------------------
void LLAgent::autoPilot(F32 *delta_yaw)
{
- if (mAutoPilot)
+ if (mAutoPilot && isAgentAvatarValid())
{
- if (!mLeaderID.isNull())
+ U8 follow = mAutoPilotBehaviorName == "Follow";
+ if (follow)
{
- LLViewerObject* object = gObjectList.findObject(mLeaderID);
- if (!object)
+ llassert(mLeaderID.notNull());
+ const auto old_pos = mAutoPilotTargetGlobal;
+ if (auto object = gObjectList.findObject(mLeaderID))
{
- stopAutoPilot();
- return;
- }
- mAutoPilotTargetGlobal = object->getPositionGlobal();
- }
-
- if (!isAgentAvatarValid()) return;
+ mAutoPilotTargetGlobal = object->getPositionGlobal();
+ if (const auto& av = object->asAvatar()) // Fly if avatar target is flying
+ {
+ setFlying(av->mInAir);
+ if (av->isSitting() && (!rlv_handler_t::isEnabled() || !gRlvHandler.hasBehaviour(RLV_BHVR_SIT)))
+ {
+ if (auto seat = av->getParent())
+ {
+ if (gAgentAvatarp->getParent() == seat)
+ {
+ mAutoPilotNoProgressFrameCount = 0; // We may have incremented this before making it here, reset it
+ return; // We're seated with them, nothing more to do
+ }
+ else if (!getAutoPilotNoProgress())
+ {
+ void handle_object_sit(LLViewerObject*, const LLVector3&);
+ handle_object_sit(static_cast(seat), LLVector3::zero);
+ follow = 2; // Indicate ground sitting is okay if we can't make it
+ }
+ else return; // If the server just wouldn't let us sit there, we won't be moving, exit here
+ }
+ else // Ground sit, but only if near enough
+ {
+ if (dist_vec(av->getPositionAgent(), getPositionAgent()) <= mAutoPilotStopDistance) // We're close enough, sit.
+ {
+ if (!gAgentAvatarp->isSittingAvatarOnGround())
+ setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+ mAutoPilotNoProgressFrameCount = 0; // Ground Sit may have incremented this, reset it now
+ return; // We're already sitting on the ground, we have nothing to do
+ }
+ else // We're not close enough yet
+ {
+ if (/*!gAgentAvatarp->isSitting() && */ // RLV takes care of sitting check for us inside standUp
+ getAutoPilotNoProgress()) // Only stand up if we haven't exhausted our no progress frames
+ standUp(); // Unsit if need be, so we can move
+ follow = 2; // Indicate we want to groundsit
+ }
+ }
+ }
+ else
+ {
+ if (dist_vec(av->getPositionAgent(), getPositionAgent()) <= mAutoPilotStopDistance)
+ {
+ follow = 3; // We're close enough, indicate no walking
+ }
- if (gAgentAvatarp->mInAir && mAutoPilotAllowFlying)
+ if (gAgentAvatarp->isSitting()) // Leader isn't sitting, standUp if needed
+ {
+ standUp();
+ mAutoPilotNoProgressFrameCount = 0; // Ground Sit may have incremented this, reset it
+ }
+ }
+ }
+ }
+ else // We might still have a valid avatar pos
+ {
+ const LLVector3d& get_av_pos(const LLUUID & id);
+ auto pos = get_av_pos(mLeaderID);
+ if (pos.isExactlyZero()) // Default constructed or invalid from server
+ {
+ // Wait for them for more follow pilot
+ return;
+ }
+ standUp(); // Leader not rendered, we mustn't be sitting
+ mAutoPilotNoProgressFrameCount = 0; // Ground Sit may have incremented this, reset it
+ mAutoPilotTargetGlobal = pos;
+ setFlying(true); // Should we fly here? Altitude is often invalid...
+
+ if (dist_vec(mAutoPilotTargetGlobal, getPositionGlobal()) <= mAutoPilotStopDistance)
+ {
+ follow = 3; // We're close enough, indicate no walking
+ }
+ }
+ if (old_pos != mAutoPilotTargetGlobal) // Reset if position changes
+ mAutoPilotNoProgressFrameCount = 0;
+ }
+
+ if (follow % 2 == 0 && gAgentAvatarp->mInAir && mAutoPilotAllowFlying)
{
setFlying(TRUE);
}
@@ -1774,12 +1870,15 @@ void LLAgent::autoPilot(F32 *delta_yaw)
F32 target_dist = direction.magVec();
- if (target_dist >= mAutoPilotTargetDist)
+ if (follow % 2 == 0 && target_dist >= mAutoPilotTargetDist)
{
mAutoPilotNoProgressFrameCount++;
- if (mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped)
+ if (getAutoPilotNoProgress())
{
- stopAutoPilot();
+ if (follow) // Well, we tried to reach them, let's just ground sit for now.
+ setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+ else
+ stopAutoPilot();
return;
}
}
@@ -1821,6 +1920,7 @@ void LLAgent::autoPilot(F32 *delta_yaw)
}
*delta_yaw = yaw;
+ if (follow == 3) return; // We're close enough, all we need to do is turn
// Compute when to start slowing down
F32 slow_distance;
@@ -2492,15 +2592,11 @@ void LLAgent::onAnimStop(const LLUUID& id)
}
else if (id == ANIM_AGENT_AWAY)
{
-// clearAFK();
// [RLVa:KB] - Checked: 2010-05-03 (RLVa-1.2.0g) | Added: RLVa-1.1.0g
-#ifdef RLV_EXTENSION_CMD_ALLOWIDLE
if (!gRlvHandler.hasBehaviour(RLV_BHVR_ALLOWIDLE))
clearAFK();
-#else
- clearAFK();
-#endif // RLV_EXTENSION_CMD_ALLOWIDLE
// [/RLVa:KB]
+// clearAFK();
}
else if (id == ANIM_AGENT_STANDUP)
{
@@ -2571,7 +2667,7 @@ bool LLAgent::canAccessMaturityInRegion( U64 region_handle ) const
return true;
}
-bool LLAgent::canAccessMaturityAtGlobal( LLVector3d pos_global ) const
+bool LLAgent::canAccessMaturityAtGlobal(const LLVector3d& pos_global ) const
{
U64 region_handle = to_region_handle_global( pos_global.mdV[0], pos_global.mdV[1] );
return canAccessMaturityInRegion( region_handle );
@@ -2618,7 +2714,7 @@ int LLAgent::convertTextToMaturity(char text)
return LLAgentAccess::convertTextToMaturity(text);
}
-class LLMaturityPreferencesResponder : public LLHTTPClient::ResponderWithResult
+class LLMaturityPreferencesResponder final : public LLHTTPClient::ResponderWithResult
{
LOG_CLASS(LLMaturityPreferencesResponder);
public:
@@ -2626,12 +2722,10 @@ public:
virtual ~LLMaturityPreferencesResponder();
protected:
- virtual void httpSuccess();
- virtual void httpFailure();
-
- /*virtual*/ char const* getName(void) const { return "LLMaturityPreferencesResponder"; }
-protected:
+ void httpSuccess() override;
+ void httpFailure() override;
+ char const* getName() const override { return "LLMaturityPreferencesResponder"; }
private:
U8 parseMaturityFromServerResponse(const LLSD &pContent) const;
@@ -2832,7 +2926,7 @@ void LLAgent::sendMaturityPreferenceToServer(U8 pPreferredMaturity)
boost::intrusive_ptr responderPtr = boost::intrusive_ptr(new LLMaturityPreferencesResponder(this, pPreferredMaturity, mLastKnownResponseMaturity));
// If we don't have a region, report it as an error
- if (getRegion() == NULL)
+ if (getRegion() == nullptr)
{
responderPtr->failureResult(0U, "region is not defined", LLSD());
}
@@ -3109,24 +3203,27 @@ void LLAgent::sendAnimationRequests(const uuid_vec_t &anim_ids, EAnimRequest req
msg->addUUIDFast(_PREHASH_AgentID, getID());
msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
- for (U32 i = 0; i < anim_ids.size(); i++)
+ for (auto anim_id : anim_ids)
{
- if (anim_ids[i].isNull())
+ if (anim_id.isNull())
{
continue;
}
msg->nextBlockFast(_PREHASH_AnimationList);
- msg->addUUIDFast(_PREHASH_AnimID, (anim_ids[i]) );
+ msg->addUUIDFast(_PREHASH_AnimID, anim_id);
msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
num_valid_anims++;
}
+ if (!num_valid_anims)
+ {
+ msg->clearMessage();
+ return;
+ }
msg->nextBlockFast(_PREHASH_PhysicalAvatarEventList);
- msg->addBinaryDataFast(_PREHASH_TypeData, NULL, 0);
- if (num_valid_anims)
- {
- sendReliableMessage();
- }
+ msg->addBinaryDataFast(_PREHASH_TypeData, nullptr, 0);
+
+ sendReliableMessage();
}
void LLAgent::sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request)
@@ -3147,7 +3244,7 @@ void LLAgent::sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request)
msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
msg->nextBlockFast(_PREHASH_PhysicalAvatarEventList);
- msg->addBinaryDataFast(_PREHASH_TypeData, NULL, 0);
+ msg->addBinaryDataFast(_PREHASH_TypeData, nullptr, 0);
sendReliableMessage();
}
@@ -3171,7 +3268,7 @@ void LLAgent::sendAnimationStateReset()
msg->addBOOLFast(_PREHASH_StartAnim, FALSE);
msg->nextBlockFast(_PREHASH_PhysicalAvatarEventList);
- msg->addBinaryDataFast(_PREHASH_TypeData, NULL, 0);
+ msg->addBinaryDataFast(_PREHASH_TypeData, nullptr, 0);
sendReliableMessage();
}
@@ -3410,8 +3507,8 @@ void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **)
// Remove the group if it already exists remove it and add the new data to pick up changes.
LLGroupData gd;
gd.mID = group_id;
- std::vector::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), gd);
- if (found_it != gAgent.mGroups.end())
+ auto found_it = std::find(gAgent.mGroups.cbegin(), gAgent.mGroups.cend(), gd);
+ if (found_it != gAgent.mGroups.cend())
{
gAgent.mGroups.erase(found_it);
if (gAgent.getGroupID() == group_id)
@@ -3437,12 +3534,12 @@ void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **)
}
}
-class LLAgentDropGroupViewerNode : public LLHTTPNode
+class LLAgentDropGroupViewerNode final : public LLHTTPNode
{
- virtual void post(
+ void post(
LLHTTPNode::ResponsePtr response,
const LLSD& context,
- const LLSD& input) const
+ const LLSD& input) const override
{
if (
@@ -3469,11 +3566,8 @@ class LLAgentDropGroupViewerNode : public LLHTTPNode
//there is only one set of data in the AgentData block
LLSD agent_data = body["AgentData"][0];
- LLUUID agent_id;
- LLUUID group_id;
-
- agent_id = agent_data["AgentID"].asUUID();
- group_id = agent_data["GroupID"].asUUID();
+ LLUUID agent_id = agent_data["AgentID"].asUUID();
+ LLUUID group_id = agent_data["GroupID"].asUUID();
if (agent_id != gAgentID)
{
@@ -3488,8 +3582,8 @@ class LLAgentDropGroupViewerNode : public LLHTTPNode
// and add the new data to pick up changes.
LLGroupData gd;
gd.mID = group_id;
- std::vector::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), gd);
- if (found_it != gAgent.mGroups.end())
+ auto found_it = std::find(gAgent.mGroups.cbegin(), gAgent.mGroups.cend(), gd);
+ if (found_it != gAgent.mGroups.cend())
{
gAgent.mGroups.erase(found_it);
if (gAgent.getGroupID() == group_id)
@@ -3560,8 +3654,8 @@ void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **)
{
need_floater_update = true;
// Remove the group if it already exists remove it and add the new data to pick up changes.
- std::vector::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), group);
- if (found_it != gAgent.mGroups.end())
+ auto found_it = std::find(gAgent.mGroups.cbegin(), gAgent.mGroups.cend(), group);
+ if (found_it != gAgent.mGroups.cend())
{
gAgent.mGroups.erase(found_it);
}
@@ -3575,12 +3669,12 @@ void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **)
}
-class LLAgentGroupDataUpdateViewerNode : public LLHTTPNode
+class LLAgentGroupDataUpdateViewerNode final : public LLHTTPNode
{
- virtual void post(
+ void post(
LLHTTPNode::ResponsePtr response,
const LLSD& context,
- const LLSD& input) const
+ const LLSD& input) const override
{
LLSD body = input["body"];
if(body.has("body"))
@@ -3620,8 +3714,8 @@ class LLAgentGroupDataUpdateViewerNode : public LLHTTPNode
{
need_floater_update = true;
// Remove the group if it already exists remove it and add the new data to pick up changes.
- std::vector::iterator found_it = std::find(gAgent.mGroups.begin(), gAgent.mGroups.end(), group);
- if (found_it != gAgent.mGroups.end())
+ auto found_it = std::find(gAgent.mGroups.cbegin(), gAgent.mGroups.cend(), group);
+ if (found_it != gAgent.mGroups.cend())
{
gAgent.mGroups.erase(found_it);
}
@@ -3822,7 +3916,7 @@ void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void *
return;
}
- if (gAgentAvatarp->isEditingAppearance())
+ if (isAgentAvatarValid() && gAgentAvatarp->isEditingAppearance())
{
// ignore baked textures when in customize mode
return;
@@ -3953,6 +4047,7 @@ void LLAgent::clearVisualParams(void *data)
// protected
bool LLAgent::teleportCore(bool is_local)
{
+ LL_INFOS("Teleport") << "In teleport core!" << LL_ENDL;
if ((TELEPORT_NONE != mTeleportState) && (mTeleportState != TELEPORT_PENDING))
{
LL_WARNS() << "Attempt to teleport when already teleporting." << LL_ENDL;
@@ -3970,7 +4065,7 @@ bool LLAgent::teleportCore(bool is_local)
// Stop all animation before actual teleporting
if (isAgentAvatarValid())
{
- for ( LLVOAvatar::AnimIterator anim_it= gAgentAvatarp->mPlayingAnimations.begin();
+ for ( auto anim_it= gAgentAvatarp->mPlayingAnimations.begin();
anim_it != gAgentAvatarp->mPlayingAnimations.end();
++anim_it)
{
@@ -3989,8 +4084,7 @@ bool LLAgent::teleportCore(bool is_local)
LLFloaterWorldMap::hide();
// hide land floater too - it'll be out of date
- if (LLFloaterLand::findInstance())
- LLFloaterLand::hideInstance();
+ if (LLFloaterLand::findInstance()) LLFloaterLand::hideInstance();
LLViewerParcelMgr::getInstance()->deselectLand();
LLViewerMediaFocus::getInstance()->clearFocus();
@@ -4431,6 +4525,7 @@ void LLAgent::setTeleportState(ETeleportState state)
{
case TELEPORT_NONE:
mbTeleportKeepsLookAt = false;
+ mIsCrossingRegion = false; // Attachments getting lost on TP; finished TP
break;
case TELEPORT_MOVING:
@@ -4763,7 +4858,7 @@ void LLAgent::sendAgentSetAppearance()
// This means the baked texture IDs on the server will be untouched.
// Once all textures are baked, another AvatarAppearance message will be sent to update the TEs
msg->nextBlockFast(_PREHASH_ObjectData);
- gMessageSystem->addBinaryDataFast(_PREHASH_TextureEntry, NULL, 0);
+ gMessageSystem->addBinaryDataFast(_PREHASH_TextureEntry, nullptr, 0);
}
@@ -4786,9 +4881,7 @@ void LLAgent::sendAgentSetAppearance()
}
LL_INFOS() << "Avatar XML num VisualParams transmitted = " << transmitted_params << LL_ENDL;
- if(transmitted_params < 218) {
- LLNotificationsUtil::add("SGIncompleteAppearance");
- }
+ if (transmitted_params < 218) LLNotificationsUtil::add("SGIncompleteAppearance");
sendReliableMessage();
}
@@ -4803,8 +4896,96 @@ void LLAgent::sendAgentDataUpdateRequest()
void LLAgent::sendAgentUserInfoRequest()
{
- if(getID().isNull())
+ std::string cap;
+
+ if (getID().isNull())
return; // not logged in
+
+ if (mRegionp)
+ cap = mRegionp->getCapability("UserInfo");
+
+ if (!cap.empty())
+ {
+ LLHTTPClient::get(cap, new LLCoroResponder(
+ boost::bind(&LLAgent::requestAgentUserInfoCoro, this, _1)));
+ }
+ else
+ {
+ sendAgentUserInfoRequestMessage();
+ }
+}
+
+void LLAgent::requestAgentUserInfoCoro(const LLCoroResponder& responder)
+{
+ const auto& result = responder.getContent();
+ const auto& status = responder.getStatus();
+
+ if (!responder.isGoodStatus(status))
+ {
+ LL_WARNS("UserInfo") << "Failed to get user information: " << result["message"] << "Status " << status << " Reason: " << responder.getReason() << LL_ENDL;
+ return;
+ }
+
+ bool im_via_email;
+ bool is_verified_email;
+ std::string email;
+ std::string dir_visibility;
+
+ im_via_email = result["im_via_email"].asBoolean();
+ is_verified_email = result["is_verified"].asBoolean();
+ email = result["email"].asString();
+ dir_visibility = result["directory_visibility"].asString();
+
+ // TODO: This should probably be changed. I'm not entirely comfortable
+ // having LLAgent interact directly with the UI in this way.
+ LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email, is_verified_email);
+ LLFloaterPostcard::updateUserInfo(email);
+}
+
+void LLAgent::sendAgentUpdateUserInfo(bool im_via_email, const std::string& directory_visibility)
+{
+ std::string cap;
+
+ if (getID().isNull())
+ return; // not logged in
+
+ if (mRegionp)
+ cap = mRegionp->getCapability("UserInfo");
+
+ if (!cap.empty())
+ {
+ LLSD body(LLSDMap
+ ("dir_visibility", LLSD::String(directory_visibility))
+ ("im_via_email", LLSD::Boolean(im_via_email)));
+ LLHTTPClient::post(cap, body, new LLCoroResponder(
+ boost::bind(&LLAgent::updateAgentUserInfoCoro, this, _1)));
+ }
+ else
+ {
+ sendAgentUpdateUserInfoMessage(im_via_email, directory_visibility);
+ }
+}
+
+
+void LLAgent::updateAgentUserInfoCoro(const LLCoroResponder& responder)
+{
+ const auto& result = responder.getContent();
+ const auto& status = responder.getStatus();
+
+ if (!responder.isGoodStatus(status))
+ {
+ LL_WARNS("UserInfo") << "Failed to set user information." << LL_ENDL;
+ }
+ else if (!result["success"].asBoolean())
+ {
+ LL_WARNS("UserInfo") << "Failed to set user information: " << result["message"] << LL_ENDL;
+ }
+}
+
+// deprecated:
+// May be removed when UserInfo cap propagates to all simhosts in grid
+void LLAgent::sendAgentUserInfoRequestMessage()
+{
gMessageSystem->newMessageFast(_PREHASH_UserInfoRequest);
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
@@ -4812,6 +4993,21 @@ void LLAgent::sendAgentUserInfoRequest()
sendReliableMessage();
}
+void LLAgent::sendAgentUpdateUserInfoMessage(bool im_via_email, const std::string& directory_visibility)
+{
+ gMessageSystem->newMessageFast(_PREHASH_UpdateUserInfo);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_UserData);
+ gMessageSystem->addBOOLFast(_PREHASH_IMViaEMail, im_via_email);
+ gMessageSystem->addString("DirectoryVisibility", directory_visibility);
+ gAgent.sendReliableMessage();
+
+}
+// end deprecated
+//------
+
void LLAgent::observeFriends()
{
if(!mFriendObserver)
@@ -4879,18 +5075,6 @@ const void LLAgent::getTeleportSourceSLURL(LLSLURL& slurl) const
slurl = *mTeleportSourceSLURL;
}
-void LLAgent::sendAgentUpdateUserInfo(bool im_via_email, const std::string& directory_visibility )
-{
- gMessageSystem->newMessageFast(_PREHASH_UpdateUserInfo);
- gMessageSystem->nextBlockFast(_PREHASH_AgentData);
- gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
- gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
- gMessageSystem->nextBlockFast(_PREHASH_UserData);
- gMessageSystem->addBOOLFast(_PREHASH_IMViaEMail, im_via_email);
- gMessageSystem->addString("DirectoryVisibility", directory_visibility);
- gAgent.sendReliableMessage();
-}
-
void LLAgent::dumpGroupInfo()
{
LL_INFOS() << "group " << mGroupName << LL_ENDL;
@@ -4973,6 +5157,7 @@ void LLAgent::onFoundLureDestination(LLSimInfo *siminfo)
msg.append(llformat(" (%s)", maturity.c_str()));
}
LLChat chat(msg);
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
LLFloaterChat::addChat(chat);
}
else
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 17042fd45..6fd97b679 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -33,8 +33,6 @@
#ifndef LL_LLAGENT_H
#define LL_LLAGENT_H
-#include
-
#include "indra_constants.h"
#include "llevent.h" // LLObservable base class
#include "llagentconstants.h"
@@ -46,7 +44,6 @@
#include "llinventorymodel.h"
#include "v3dmath.h"
-#include
#include
#include
@@ -70,6 +67,7 @@ class LLAgentAccess;
class LLSLURL;
class LLSimInfo;
class LLTeleportRequest;
+struct LLCoroResponder;
typedef boost::shared_ptr LLTeleportRequestPtr;
@@ -100,7 +98,7 @@ struct LLGroupData
//------------------------------------------------------------------------
// LLAgent
//------------------------------------------------------------------------
-class LLAgent : public LLOldEvents::LLObservable
+class LLAgent final : public LLOldEvents::LLObservable
{
LOG_CLASS(LLAgent);
@@ -248,7 +246,7 @@ public:
void changeParcels(); // called by LLViewerParcelMgr when we cross a parcel boundary
// Register a boost callback to be called when the agent changes parcels
- typedef boost::function parcel_changed_callback_t;
+ typedef std::function parcel_changed_callback_t;
boost::signals2::connection addParcelChangedCallback(parcel_changed_callback_t);
private:
@@ -593,13 +591,14 @@ public:
public:
BOOL getAutoPilot() const { return mAutoPilot; }
LLVector3d getAutoPilotTargetGlobal() const { return mAutoPilotTargetGlobal; }
- LLUUID getAutoPilotLeaderID() const { return mLeaderID; }
+ const LLUUID& getAutoPilotLeaderID() const { return mLeaderID; }
F32 getAutoPilotStopDistance() const { return mAutoPilotStopDistance; }
F32 getAutoPilotTargetDist() const { return mAutoPilotTargetDist; }
BOOL getAutoPilotUseRotation() const { return mAutoPilotUseRotation; }
LLVector3 getAutoPilotTargetFacing() const { return mAutoPilotTargetFacing; }
F32 getAutoPilotRotationThreshold() const { return mAutoPilotRotationThreshold; }
- std::string getAutoPilotBehaviorName() const { return mAutoPilotBehaviorName; }
+ const std::string& getAutoPilotBehaviorName() const { return mAutoPilotBehaviorName; }
+ bool getAutoPilotNoProgress() const;
void startAutoPilotGlobal(const LLVector3d &pos_global,
const std::string& behavior_name = std::string(),
@@ -739,6 +738,13 @@ private:
** **
*******************************************************************************/
+ // Attachments getting lost on TP
+public:
+ void setIsCrossingRegion(bool is_crossing) { mIsCrossingRegion = is_crossing; }
+ bool isCrossingRegion() const { return mIsCrossingRegion; }
+private:
+ bool mIsCrossingRegion;
+
// Build
public:
bool canEditParcel() const { return mCanEditParcel; }
@@ -778,7 +784,7 @@ public:
void requestEnterGodMode();
void requestLeaveGodMode();
- typedef boost::function god_level_change_callback_t;
+ typedef std::function god_level_change_callback_t;
typedef boost::signals2::signal god_level_change_signal_t;
typedef boost::signals2::connection god_level_change_slot_t;
@@ -800,7 +806,7 @@ public:
bool canAccessMature() const;
bool canAccessAdult() const;
bool canAccessMaturityInRegion( U64 region_handle ) const;
- bool canAccessMaturityAtGlobal( LLVector3d pos_global ) const;
+ bool canAccessMaturityAtGlobal( const LLVector3d& pos_global ) const;
bool prefersPG() const;
bool prefersMature() const;
bool prefersAdult() const;
@@ -957,9 +963,17 @@ public:
void sendAgentSetAppearance();
void sendAgentDataUpdateRequest();
void sendAgentUserInfoRequest();
- // IM to Email and Online visibility
+
+// IM to Email and Online visibility
void sendAgentUpdateUserInfo(bool im_to_email, const std::string& directory_visibility);
+private:
+ void requestAgentUserInfoCoro(const LLCoroResponder& responder);
+ void updateAgentUserInfoCoro(const LLCoroResponder& responder);
+ // DEPRECATED: may be removed when User Info cap propagates
+ void sendAgentUserInfoRequestMessage();
+ void sendAgentUpdateUserInfoMessage(bool im_via_email, const std::string& directory_visibility);
+
//--------------------------------------------------------------------
// Receive
//--------------------------------------------------------------------
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index bb3317005..727a59f4b 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -289,11 +289,6 @@ LLAgentCamera::~LLAgentCamera()
//-----------------------------------------------------------------------------
void LLAgentCamera::resetView(BOOL reset_camera, BOOL change_camera)
{
- if (gAgent.getAutoPilot())
- {
- gAgent.stopAutoPilot(TRUE);
- }
-
LLSelectMgr::getInstance()->unhighlightAll();
// By popular request, keep land selection while walking around. JC
@@ -2517,13 +2512,13 @@ void LLAgentCamera::changeCameraToCustomizeAvatar()
gAgent.sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_START);
gAgent.setCustomAnim(TRUE);
gAgentAvatarp->startMotion(ANIM_AGENT_CUSTOMIZE);
- }
- LLMotion* turn_motion = gAgentAvatarp->findMotion(ANIM_AGENT_CUSTOMIZE);
+ LLMotion* turn_motion = gAgentAvatarp->findMotion(ANIM_AGENT_CUSTOMIZE);
- if (turn_motion)
- {
- // delay camera animation long enough to play through turn animation
- setAnimationDuration(turn_motion->getDuration() + CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP);
+ if (turn_motion)
+ {
+ // delay camera animation long enough to play through turn animation
+ setAnimationDuration(turn_motion->getDuration() + CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP);
+ }
}
}
//
diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp
index c284f33e8..453b68080 100644
--- a/indra/newview/llaisapi.cpp
+++ b/indra/newview/llaisapi.cpp
@@ -35,13 +35,12 @@
#include "llviewerregion.h"
#include "llinventoryobserver.h"
#include "llviewercontrol.h"
-#include
///----------------------------------------------------------------------------
/// Classes for AISv3 support.
///----------------------------------------------------------------------------
-class AISCommand : public LLHTTPClient::ResponderWithResult
+class AISCommand final : public LLHTTPClient::ResponderWithCompleted
{
public:
typedef boost::function command_func_type;
@@ -60,35 +59,28 @@ public:
(mCommandFunc = func)();
}
- char const* getName(void) const
+ char const* getName(void) const override
{
return mName;
}
void markComplete()
- {
- mRetryPolicy->onSuccess();
- }
-
-protected:
- /* virtual */
- void httpSuccess()
{
// Command func holds a reference to self, need to release it
// after a success or final failure.
mCommandFunc = no_op;
- AISAPI::InvokeAISCommandCoro(this, getURL(), mTargetId, getContent(), mCompletionFunc, (AISAPI::COMMAND_TYPE)mType);
+ mRetryPolicy->onSuccess();
}
- /*virtual*/
- void httpFailure()
+ void malformedResponse() { mStatus = HTTP_INTERNAL_ERROR_OTHER; mReason = llformat("Malformed response contents (original code: %d)", mStatus); }
+
+ bool onFailure()
{
- LL_WARNS("Inventory") << dumpResponse() << LL_ENDL;
- S32 status = getStatus();
- const AIHTTPReceivedHeaders& headers = getResponseHeaders();
- mRetryPolicy->onFailure(status, headers);
+ bool retry = mStatus != HTTP_INTERNAL_ERROR_OTHER && mStatus != 410; // We handle these and stop
+ LL_WARNS("Inventory") << "Inventory error: " << dumpResponse() << LL_ENDL;
+ if (retry) mRetryPolicy->onFailure(mStatus, getResponseHeaders());
F32 seconds_to_wait;
- if (mRetryPolicy->shouldRetry(seconds_to_wait))
+ if (retry && mRetryPolicy->shouldRetry(seconds_to_wait))
{
doAfterInterval(mCommandFunc,seconds_to_wait);
}
@@ -99,6 +91,13 @@ protected:
// *TODO: Notify user? This seems bad.
mCommandFunc = no_op;
}
+ return retry;
+ }
+
+protected:
+ void httpCompleted() override
+ {
+ AISAPI::InvokeAISCommandCoro(this, getURL(), mTargetId, getContent(), mCompletionFunc, (AISAPI::COMMAND_TYPE)mType);
}
command_func_type mCommandFunc;
@@ -313,31 +312,97 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t
boost::intrusive_ptr< AISCommand > responder = new AISCommand(UPDATEITEM, "UpdateItem",itemId, callback);
responder->run(boost::bind(&LLHTTPClient::patch, url, updates, responder/*,*/ DEBUG_CURLIO_PARAM(debug_off), keep_alive, (AIStateMachine*)NULL, 0));
}
-void AISAPI::InvokeAISCommandCoro(LLHTTPClient::ResponderWithResult* responder,
- std::string url,
+void AISAPI::InvokeAISCommandCoro(AISCommand* responder,
+ std::string url,
LLUUID targetId, LLSD result, completion_t callback, COMMAND_TYPE type)
{
+ LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL;
+
+ auto status = responder->getStatus();
+
+ if (!responder->isGoodStatus(status) || !result.isMap())
{
if (!result.isMap())
{
- responder->failureResult(400, "Malformed response contents", result);
- return;
+ responder->malformedResponse();
}
- ((AISCommand*)responder)->markComplete();
+ else if (status == 410) //GONE
+ {
+ // Item does not exist or was already deleted from server.
+ // parent folder is out of sync
+ if (type == REMOVECATEGORY)
+ {
+ LLViewerInventoryCategory *cat = gInventory.getCategory(targetId);
+ if (cat)
+ {
+ LL_WARNS("Inventory") << "Purge failed for '" << cat->getName()
+ << "' local version:" << cat->getVersion()
+ << " since folder no longer exists at server. Descendent count: server == " << cat->getDescendentCount()
+ << ", viewer == " << cat->getViewerDescendentCount()
+ << LL_ENDL;
+ gInventory.fetchDescendentsOf(cat->getParentUUID());
+ // Note: don't delete folder here - contained items will be deparented (or deleted)
+ // and since we are clearly out of sync we can't be sure we won't get rid of something we need.
+ // For example folder could have been moved or renamed with items intact, let it fetch first.
+ }
+ }
+ else if (type == REMOVEITEM)
+ {
+ LLViewerInventoryItem *item = gInventory.getItem(targetId);
+ if (item)
+ {
+ LL_WARNS("Inventory") << "Purge failed for '" << item->getName()
+ << "' since item no longer exists at server." << LL_ENDL;
+ gInventory.fetchDescendentsOf(item->getParentUUID());
+ // since item not on the server and exists at viewer, so it needs an update at the least,
+ // so delete it, in worst case item will be refetched with new params.
+ gInventory.onObjectDeletedFromServer(targetId);
+ }
+ }
+ }
+ // Keep these statuses accounted for in the responder too
+ if (responder->onFailure()) // If we're retrying, exit early.
+ return;
}
+ else responder->markComplete();
gInventory.onAISUpdateReceived("AISCommand", result);
if (callback && callback != nullptr)
{
- LLUUID id(LLUUID::null);
-
- if (result.has("category_id") && (type == COPYLIBRARYCATEGORY))
+// [SL:KB] - Patch: Appearance-SyncAttach | Checked: Catznip-3.7
+ uuid_list_t ids;
+ switch (type)
{
- id = result["category_id"];
+ case COPYLIBRARYCATEGORY:
+ if (result.has("category_id"))
+ {
+ ids.insert(result["category_id"]);
+ }
+ break;
+ case COPYINVENTORY:
+ {
+ AISUpdate::parseUUIDArray(result, "_created_items", ids);
+ AISUpdate::parseUUIDArray(result, "_created_categories", ids);
+ }
+ break;
+ default:
+ break;
}
- callback(id);
+ // If we were feeling daring we'd call LLInventoryCallback::fire for every item but it would take additional work to investigate whether all LLInventoryCallback derived classes
+ // were designed to handle multiple fire calls (with legacy link creation only one would ever fire per link creation) so we'll be cautious and only call for the first one for now
+ // (note that the LL code as written below will always call fire once with the NULL UUID for anything but CopyLibraryCategoryCommand so even the above is an improvement)
+ callback( (!ids.empty()) ? *ids.begin() : LLUUID::null);
+// [/SL:KB]
+// LLUUID id(LLUUID::null);
+//
+// if (result.has("category_id") && (type == COPYLIBRARYCATEGORY))
+// {
+// id = result["category_id"];
+// }
+//
+// callback(id);
}
}
@@ -374,18 +439,17 @@ void AISUpdate::parseMeta(const LLSD& update)
// parse _categories_removed -> mObjectsDeletedIds
uuid_list_t cat_ids;
parseUUIDArray(update,"_categories_removed",cat_ids);
- for (uuid_list_t::const_iterator it = cat_ids.begin();
- it != cat_ids.end(); ++it)
+ for (auto cat_id : cat_ids)
{
- LLViewerInventoryCategory *cat = gInventory.getCategory(*it);
+ LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
if(cat)
{
mCatDescendentDeltas[cat->getParentUUID()]--;
- mObjectsDeletedIds.insert(*it);
+ mObjectsDeletedIds.insert(cat_id);
}
else
{
- LL_WARNS("Inventory") << "removed category not found " << *it << LL_ENDL;
+ LL_WARNS("Inventory") << "removed category not found " << cat_id << LL_ENDL;
}
}
@@ -393,36 +457,34 @@ void AISUpdate::parseMeta(const LLSD& update)
uuid_list_t item_ids;
parseUUIDArray(update,"_category_items_removed",item_ids);
parseUUIDArray(update,"_removed_items",item_ids);
- for (uuid_list_t::const_iterator it = item_ids.begin();
- it != item_ids.end(); ++it)
+ for (auto item_id : item_ids)
{
- LLViewerInventoryItem *item = gInventory.getItem(*it);
+ LLViewerInventoryItem *item = gInventory.getItem(item_id);
if(item)
{
mCatDescendentDeltas[item->getParentUUID()]--;
- mObjectsDeletedIds.insert(*it);
+ mObjectsDeletedIds.insert(item_id);
}
else
{
- LL_WARNS("Inventory") << "removed item not found " << *it << LL_ENDL;
+ LL_WARNS("Inventory") << "removed item not found " << item_id << LL_ENDL;
}
}
// parse _broken_links_removed -> mObjectsDeletedIds
uuid_list_t broken_link_ids;
parseUUIDArray(update,"_broken_links_removed",broken_link_ids);
- for (uuid_list_t::const_iterator it = broken_link_ids.begin();
- it != broken_link_ids.end(); ++it)
+ for (auto broken_link_id : broken_link_ids)
{
- LLViewerInventoryItem *item = gInventory.getItem(*it);
+ LLViewerInventoryItem *item = gInventory.getItem(broken_link_id);
if(item)
{
mCatDescendentDeltas[item->getParentUUID()]--;
- mObjectsDeletedIds.insert(*it);
+ mObjectsDeletedIds.insert(broken_link_id);
}
else
{
- LL_WARNS("Inventory") << "broken link not found " << *it << LL_ENDL;
+ LL_WARNS("Inventory") << "broken link not found " << broken_link_id << LL_ENDL;
}
}
@@ -764,7 +826,7 @@ void AISUpdate::parseEmbeddedCategories(const LLSD& categories)
void AISUpdate::doUpdate()
{
- // Do version/descendent accounting.
+ // Do version/descendant accounting.
for (std::map::const_iterator catit = mCatDescendentDeltas.begin();
catit != mCatDescendentDeltas.end(); ++catit)
{
@@ -785,7 +847,7 @@ void AISUpdate::doUpdate()
continue;
}
- // If we have a known descendent count, set that now.
+ // If we have a known descendant count, set that now.
LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
if (cat)
{
@@ -822,7 +884,7 @@ void AISUpdate::doUpdate()
LLUUID category_id(update_it->first);
LLPointer new_category = update_it->second;
// Since this is a copy of the category *before* the accounting update, above,
- // we need to transfer back the updated version/descendent count.
+ // we need to transfer back the updated version/descendant count.
LLViewerInventoryCategory* curr_cat = gInventory.getCategory(new_category->getUUID());
if (!curr_cat)
{
@@ -866,21 +928,19 @@ void AISUpdate::doUpdate()
}
// DELETE OBJECTS
- for (uuid_list_t::const_iterator del_it = mObjectsDeletedIds.begin();
- del_it != mObjectsDeletedIds.end(); ++del_it)
+ for (auto deleted_id : mObjectsDeletedIds)
{
- LL_DEBUGS("Inventory") << "deleted item " << *del_it << LL_ENDL;
- gInventory.onObjectDeletedFromServer(*del_it, false, false, false);
+ LL_DEBUGS("Inventory") << "deleted item " << deleted_id << LL_ENDL;
+ gInventory.onObjectDeletedFromServer(deleted_id, false, false, false);
}
// TODO - how can we use this version info? Need to be sure all
// changes are going through AIS first, or at least through
// something with a reliable responder.
- for (uuid_int_map_t::iterator ucv_it = mCatVersionsUpdated.begin();
- ucv_it != mCatVersionsUpdated.end(); ++ucv_it)
+ for (auto& ucv_it : mCatVersionsUpdated)
{
- const LLUUID id = ucv_it->first;
- S32 version = ucv_it->second;
+ const LLUUID id = ucv_it.first;
+ S32 version = ucv_it.second;
LLViewerInventoryCategory *cat = gInventory.getCategory(id);
LL_DEBUGS("Inventory") << "cat version update " << cat->getName() << " to version " << cat->getVersion() << LL_ENDL;
if (cat->getVersion() != version)
@@ -896,7 +956,16 @@ void AISUpdate::doUpdate()
// inventory COF is maintained on the viewer through calls to
// LLInventoryModel::accountForUpdate when a changing operation
// is performed. This occasionally gets out of sync however.
- cat->setVersion(version);
+ if (version != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ cat->setVersion(version);
+ }
+ else
+ {
+ // We do not account for update if version is UNKNOWN, so we shouldn't rise version
+ // either or viewer will get stuck on descendants count -1, try to refetch folder instead
+ cat->fetch();
+ }
}
}
diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h
index 7c8293a39..b37edbc78 100644
--- a/indra/newview/llaisapi.h
+++ b/indra/newview/llaisapi.h
@@ -72,7 +72,7 @@ private:
static std::string getInvCap();
static std::string getLibCap();
- static void InvokeAISCommandCoro( LLHTTPClient::ResponderWithResult* responder,
+ static void InvokeAISCommandCoro(class AISCommand* responder,
std::string url, LLUUID targetId, LLSD body,
completion_t callback, COMMAND_TYPE type);
};
@@ -84,7 +84,10 @@ public:
void parseUpdate(const LLSD& update);
void parseMeta(const LLSD& update);
void parseContent(const LLSD& update);
- void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids);
+// [SL:KB] - Patch: Appearance-SyncAttach | Checked: Catznip-3.7
+ static void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids);
+// [/SL:KB]
+// void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids);
void parseLink(const LLSD& link_map);
void parseItem(const LLSD& link_map);
void parseCategory(const LLSD& link_map);
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index dc7665727..6976f0050 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -2841,6 +2841,12 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
{
if(!category) return;
+ // Attachments getting lost on TP:
+ // We'll be sending the outfit change request to our current region,
+ // so we'll learn them if they've been sending bad kills.
+ // We don't take kindly to that sorta behaviour round these parts.
+ gAgent.setIsCrossingRegion(false);
+
selfClearPhases();
selfStartPhase("wear_inventory_category");
@@ -4178,7 +4184,7 @@ LLUUID LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, L
// 2) Stuff with requests via makeLink and makeCopy
// 3) Call dispatch()
// 4) Let the LLPointer go out of scope.
-class LLCreateLegacyOutfit : public LLBoostFuncInventoryCallbackFireOnce
+class LLCreateLegacyOutfit final : public LLBoostFuncInventoryCallbackFireOnce
{
public:
LLCreateLegacyOutfit(const LLUUID& folder_id, inventory_func_type fire_func, nullary_func_type destroy_func = no_op) :
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 56c2c6550..77382fd07 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -47,6 +47,7 @@
#include "llagent.h"
#include "llagentcamera.h"
#include "llagentwearables.h"
+#include "llimprocessing.h"
#include "llwindow.h"
#include "llviewerstats.h"
#include "llmarketplacefunctions.h"
@@ -95,6 +96,7 @@
// Linden library includes
#include "llavatarnamecache.h"
#include "lldiriterator.h"
+#include "llexperiencecache.h"
#include "llimagej2c.h"
#include "llmemory.h"
#include "llprimitive.h"
@@ -189,6 +191,7 @@
#include "llviewerthrottle.h"
#include "llparcel.h"
#include "llviewerassetstats.h"
+#include "NACLantispam.h"
#include "llmainlooprepeater.h"
@@ -428,27 +431,6 @@ static void ui_audio_callback(const LLUUID& uuid)
}
}
-void request_initial_instant_messages()
-{
- static BOOL requested = FALSE;
- if (!requested
- && gMessageSystem
- && LLMuteList::getInstance()->isLoaded()
- && isAgentAvatarValid())
- {
- // Auto-accepted inventory items may require the avatar object
- // to build a correct name. Likewise, inventory offers from
- // muted avatars require the mute list to properly mute.
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_RetrieveInstantMessages);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- gAgent.sendReliableMessage();
- requested = TRUE;
- }
-}
-
// Use these strictly for things that are constructed at startup,
// or for things that are performance critical. JC
static void settings_to_globals()
@@ -726,6 +708,8 @@ void LLAppViewer::initCrashReporting()
annotations.emplace("sentry[contexts][app][app_version]", LLVersionInfo::getVersion());
annotations.emplace("sentry[contexts][app][app_build]", LLVersionInfo::getChannelAndVersion());
+ annotations.emplace("sentry[release]", LLVersionInfo::getChannelAndVersion());
+
annotations.emplace("sentry[tags][second_instance]", fmt::to_string(isSecondInstance()));
//annotations.emplace("sentry[tags][bitness]", fmt::to_string(ADDRESS_SIZE));
annotations.emplace("sentry[tags][bitness]",
@@ -738,7 +722,6 @@ void LLAppViewer::initCrashReporting()
// Optional arguments to pass to the handler
std::vector arguments;
- arguments.push_back("--no-upload-gzip");
arguments.push_back("--no-rate-limit");
arguments.push_back("--monitor-self");
@@ -2286,7 +2269,9 @@ bool LLAppViewer::initConfiguration()
LL_INFOS() << "Loading settings file list" << settings_file_list << LL_ENDL;
if (0 == settings_control.loadFromFile(settings_file_list))
{
- LL_ERRS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL;
+ OSMessageBox("Cannot load default configuration file " + settings_file_list + " The installation may be corrupted.",
+ LLStringUtil::null,OSMB_OK);
+ return false;
}
mSettingsLocationList = settings_control.getLLSD("Locations");
@@ -4045,7 +4030,7 @@ void LLAppViewer::idle()
// here.
{
LAZY_FT("request_initial_instant_messages");
- request_initial_instant_messages();
+ LLIMProcessing::requestOfflineMessages();
}
///////////////////////////////////
@@ -4176,6 +4161,7 @@ void LLAppViewer::idle()
// floating throughout the various object lists.
//
idleNameCache();
+ if (gAgent.getRegion()) LLExperienceCache::instance().idleCoro();
gFrameStats.start(LLFrameStats::IDLE_NETWORK);
stop_glerror();
@@ -4204,6 +4190,7 @@ void LLAppViewer::idle()
gIdleCallbacks.callFunctions();
gInventory.idleNotifyObservers();
+ if (auto antispam = NACLAntiSpamRegistry::getIfExists()) antispam->idle();
}
// Metrics logging (LLViewerAssetStats, etc.)
@@ -4840,6 +4827,12 @@ void LLAppViewer::disconnectViewer()
}
saveNameCache();
+ if (LLExperienceCache::instanceExists())
+ {
+ // TODO: LLExperienceCache::cleanup() logic should be moved to
+ // cleanupSingleton().
+ LLExperienceCache::instance().cleanup();
+ }
// close inventory interface, close all windows
LLPanelMainInventory::cleanup();
diff --git a/indra/newview/llassetuploadqueue.cpp b/indra/newview/llassetuploadqueue.cpp
index 88695b2d1..67c7fa55c 100644
--- a/indra/newview/llassetuploadqueue.cpp
+++ b/indra/newview/llassetuploadqueue.cpp
@@ -171,6 +171,7 @@ void LLAssetUploadQueue::request(LLAssetUploadQueueSupplier** supplier)
body["item_id"] = data.mItemId;
body["is_script_running"] = data.mIsRunning;
body["target"] = data.mIsTargetMono? "mono" : "lsl2";
+ body["experience"] = data.mExperienceId;
std::string url = "";
LLViewerObject* object = gObjectList.findObject(data.mTaskId);
@@ -194,7 +195,8 @@ void LLAssetUploadQueue::queue(const std::string& filename,
const LLUUID& queue_id,
U8* script_data,
U32 data_size,
- std::string script_name)
+ std::string script_name,
+ const LLUUID& experience_id)
{
UploadData data;
data.mTaskId = task_id;
@@ -206,6 +208,7 @@ void LLAssetUploadQueue::queue(const std::string& filename,
data.mData = script_data;
data.mDataSize = data_size;
data.mScriptName = script_name;
+ data.mExperienceId = experience_id;
mQueue.push_back(data);
diff --git a/indra/newview/llassetuploadqueue.h b/indra/newview/llassetuploadqueue.h
index 930dc72a7..bc6c11cda 100644
--- a/indra/newview/llassetuploadqueue.h
+++ b/indra/newview/llassetuploadqueue.h
@@ -56,7 +56,8 @@ public:
const LLUUID& queue_id,
U8* data,
U32 data_size,
- std::string script_name);
+ std::string script_name,
+ const LLUUID& experience_id);
bool isEmpty() const {return mQueue.empty();}
@@ -75,6 +76,7 @@ private:
U8* mData;
U32 mDataSize;
std::string mScriptName;
+ LLUUID mExperienceId;
};
// Ownership of mSupplier passed to currently waiting responder
diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp
index 5395d42df..2be3da1d4 100644
--- a/indra/newview/llattachmentsmgr.cpp
+++ b/indra/newview/llattachmentsmgr.cpp
@@ -481,13 +481,6 @@ void LLAttachmentsMgr::onAttachmentArrived(const LLUUID& inv_item_id)
bool expected = mAttachmentRequests.getTime(inv_item_id, timer);
LLInventoryItem *item = gInventory.getItem(inv_item_id);
- if (item && boost::algorithm::contains(item->getName(), " Bridge v") && gSavedSettings.getBOOL("SGDetachBridge"))
- {
- LL_INFOS() << "Bridge detected! detaching" << LL_ENDL;
- LLVOAvatarSelf::detachAttachmentIntoInventory(item->getUUID());
- return;
- }
-
if (!expected)
{
LL_WARNS() << "ATT Attachment was unexpected or arrived after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds: "
@@ -505,6 +498,13 @@ void LLAttachmentsMgr::onAttachmentArrived(const LLUUID& inv_item_id)
mCOFLinkBatchTimer.reset();
}
mRecentlyArrivedAttachments.insert(inv_item_id);
+
+ static const LLCachedControl detach_bridge("SGDetachBridge");
+ if (detach_bridge && item && boost::algorithm::contains(item->getName(), " Bridge v"))
+ {
+ LL_INFOS() << "Bridge detected! detaching" << LL_ENDL;
+ LLVOAvatarSelf::detachAttachmentIntoInventory(item->getUUID());
+ }
}
void LLAttachmentsMgr::onDetachRequested(const LLUUID& inv_item_id)
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index f7eb591ac..3685ce21b 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -374,8 +374,8 @@ void LLAvatarActions::showProfile(const LLUUID& id, bool web)
// static
void LLAvatarActions::showProfiles(const uuid_vec_t& ids, bool web)
{
- for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
- showProfile(*it, web);
+ for (const auto& id : ids)
+ showProfile(id, web);
}
//static
@@ -1197,7 +1197,7 @@ void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::stri
calling_card_folder_id);
LLSD args;
- args["TO_NAME"] = target_name;
+ args["TO_NAME"] = getSLURL(target_id);
LLSD payload;
payload["from_id"] = target_id;
@@ -1245,27 +1245,31 @@ bool LLAvatarActions::isAgentMappable(const LLUUID& agent_id)
);
}
-// static
-void LLAvatarActions::copyUUIDs(const uuid_vec_t& ids)
+void copy_from_ids(const uuid_vec_t& ids, std::function func)
{
std::string ids_string;
const std::string& separator = LLTrans::getString("words_separator");
- for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ for (const auto& id : ids)
{
- const LLUUID& id = *it;
if (id.isNull())
continue;
if (!ids_string.empty())
ids_string.append(separator);
- ids_string.append(id.asString());
+ ids_string.append(func(id));
}
if (!ids_string.empty())
gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(ids_string));
}
+// static
+void LLAvatarActions::copyUUIDs(const uuid_vec_t& ids)
+{
+ copy_from_ids(ids, [](const LLUUID& id) { return id.asString(); });
+}
+
std::string LLAvatarActions::getSLURL(const LLUUID& id)
{
return llformat("secondlife:///app/agent/%s/about", id.asString().c_str());
diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
index 32323b8f5..3d8dde517 100644
--- a/indra/newview/llchatbar.cpp
+++ b/indra/newview/llchatbar.cpp
@@ -72,6 +72,8 @@
#include "chatbar_as_cmdline.h"
// [RLVa:KB]
+#include "rlvcommon.h"
+#include "rlvactions.h"
#include "rlvhandler.h"
// [/RLVa:KB]
@@ -100,25 +102,24 @@ class LLChatBarGestureObserver : public LLGestureManagerObserver
{
public:
LLChatBarGestureObserver(LLChatBar* chat_barp) : mChatBar(chat_barp){}
- virtual ~LLChatBarGestureObserver() {}
- virtual void changed() { mChatBar->refreshGestures(); }
+ virtual ~LLChatBarGestureObserver() = default;
+ void changed() override { mChatBar->refreshGestures(); }
private:
LLChatBar* mChatBar;
};
-
//
// Functions
//
LLChatBar::LLChatBar()
: LLPanel(),
- mInputEditor(NULL),
+ mInputEditor(nullptr),
mGestureLabelTimer(),
mLastSpecialChatChannel(0),
mIsBuilt(FALSE),
- mGestureCombo(NULL),
- mObserver(NULL)
+ mGestureCombo(nullptr),
+ mObserver(nullptr)
{
setIsChrome(TRUE);
@@ -132,10 +133,14 @@ LLChatBar::~LLChatBar()
{
LLGestureMgr::instance().removeObserver(mObserver);
delete mObserver;
- mObserver = NULL;
+ mObserver = nullptr;
// LLView destructor cleans up children
}
+//-----------------------------------------------------------------------
+// Overrides
+//-----------------------------------------------------------------------
+
BOOL LLChatBar::postBuild()
{
if (LLUICtrl* history_ctrl = findChild("History"))
@@ -189,7 +194,7 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask )
else if (mask == MASK_SHIFT)
{
// whisper
- sendChat( CHAT_TYPE_WHISPER );
+ sendChat(CHAT_TYPE_WHISPER);
handled = TRUE;
}
else if (mask == MASK_NONE)
@@ -210,6 +215,11 @@ BOOL LLChatBar::handleKeyHere( KEY key, MASK mask )
return handled;
}
+void LLChatBar::onFocusLost()
+{
+ //stopChat();
+}
+
void LLChatBar::refresh()
{
// HACK: Leave the name of the gesture in place for a few seconds.
@@ -242,17 +252,16 @@ void LLChatBar::refreshGestures()
//store current selection so we can maintain it
std::string cur_gesture = mGestureCombo->getValue().asString();
mGestureCombo->selectFirstItem();
- std::string label = mGestureCombo->getValue().asString();;
+
// clear
mGestureCombo->clearRows();
// collect list of unique gestures
std::map unique;
- LLGestureMgr::item_map_t::const_iterator it;
const LLGestureMgr::item_map_t& active_gestures = LLGestureMgr::instance().getActiveGestures();
- for (it = active_gestures.begin(); it != active_gestures.end(); ++it)
+ for (const auto& active_gesture : active_gestures)
{
- LLMultiGesture* gesture = (*it).second;
+ LLMultiGesture* gesture = active_gesture.second;
if (gesture)
{
if (!gesture->mTrigger.empty())
@@ -262,11 +271,9 @@ void LLChatBar::refreshGestures()
}
}
- // add unique gestures
- std::map ::iterator it2;
- for (it2 = unique.begin(); it2 != unique.end(); ++it2)
+ for (auto& it2 : unique)
{
- mGestureCombo->addSimpleElement((*it2).first);
+ mGestureCombo->addSimpleElement(it2.first);
}
mGestureCombo->sortByName();
@@ -316,12 +323,12 @@ void LLChatBar::setIgnoreArrowKeys(BOOL b)
}
}
-BOOL LLChatBar::inputEditorHasFocus()
+BOOL LLChatBar::inputEditorHasFocus() const
{
return mInputEditor && mInputEditor->hasFocus();
}
-std::string LLChatBar::getCurrentChat()
+std::string LLChatBar::getCurrentChat() const
{
return mInputEditor ? mInputEditor->getText() : LLStringUtil::null;
}
@@ -390,7 +397,7 @@ LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel)
pos++;
}
- mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10);
+ mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), nullptr, 10);
//
if(mesg[1] == '-')
mLastSpecialChatChannel = -mLastSpecialChatChannel;
@@ -570,9 +577,12 @@ void LLChatBar::onInputEditorKeystroke()
S32 length = raw_text.length();
- //if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences
-// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.0.0d)
- if ( (length > 0) && (raw_text[0] != '/') && (!gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT)) )
+// if( (length > 0)
+// && (raw_text[0] != '/') // forward slash is used for escape (eg. emote) sequences
+// && (raw_text[0] != ':') // colon is used in for MUD poses
+// )
+// [RLVa:KB] - Checked: 2010-03-26 (RLVa-1.2.0b) | Modified: RLVa-1.0.0d
+ if ( (length > 0) && (raw_text[0] != '/') && (raw_text[0] != ':') && (!RlvActions::hasBehaviour(RLV_BHVR_REDIRCHAT)) )
// [/RLVa:KB]
{
gAgent.startTyping();
@@ -582,21 +592,6 @@ void LLChatBar::onInputEditorKeystroke()
gAgent.stopTyping();
}
- /* Doesn't work -- can't tell the difference between a backspace
- that killed the selection vs. backspace at the end of line.
- if (length > 1
- && text[0] == '/'
- && key == KEY_BACKSPACE)
- {
- // the selection will already be deleted, but we need to trim
- // off the character before
- std::string new_text = raw_text.substr(0, length-1);
- mInputEditor->setText( new_text );
- mInputEditor->setCursorToEnd();
- length = length - 1;
- }
- */
-
KEY key = gKeyboard->currentKey();
// Ignore "special" keys, like backspace, arrows, etc.
@@ -622,11 +617,6 @@ void LLChatBar::onInputEditorKeystroke()
mInputEditor->setSelection(length, outlength);
}
}
-
- //LL_INFOS() << "GESTUREDEBUG " << trigger
- // << " len " << length
- // << " outlen " << out_str.getLength()
- // << LL_ENDL;
}
}
@@ -668,11 +658,11 @@ void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type,
void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
{
// Look for "/20 foo" channel chats.
- S32 channel = 0;
+ S32 channel = gSavedSettings.getS32("AlchemyNearbyChatChannel");
LLWString out_text = stripChannelNumber(wtext, &channel);
std::string utf8_out_text = wstring_to_utf8str(out_text);
-
std::string utf8_text = wstring_to_utf8str(wtext);
+
utf8_text = utf8str_trim(utf8_text);
if (!utf8_text.empty())
{
@@ -680,17 +670,12 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL
}
// [RLVa:KB] - Checked: 2010-03-27 (RLVa-1.2.0b) | Modified: RLVa-1.2.0b
- if ( (0 == channel) && (rlv_handler_t::isEnabled()) )
+ // RELEASE-RLVa: [SL-2.0.0] This entire class appears to be dead/non-functional?
+ if ( (0 == channel) && (RlvActions::isRlvEnabled()) )
{
// Adjust the (public) chat "volume" on chat and gestures (also takes care of playing the proper animation)
- if ( ((CHAT_TYPE_SHOUT == type) || (CHAT_TYPE_NORMAL == type)) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATNORMAL)) )
- type = CHAT_TYPE_WHISPER;
- else if ( (CHAT_TYPE_SHOUT == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATSHOUT)) )
- type = CHAT_TYPE_NORMAL;
- else if ( (CHAT_TYPE_WHISPER == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATWHISPER)) )
- type = CHAT_TYPE_NORMAL;
-
- animate &= !gRlvHandler.hasBehaviour( (!RlvUtil::isEmote(utf8_text)) ? RLV_BHVR_REDIRCHAT : RLV_BHVR_REDIREMOTE );
+ type = RlvActions::checkChatVolume(type);
+ animate &= !RlvActions::hasBehaviour( (!RlvUtil::isEmote(utf8_text)) ? RLV_BHVR_REDIRCHAT : RLV_BHVR_REDIREMOTE );
}
// [/RLVa:KB]
@@ -731,23 +716,19 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL
send_chat_from_viewer(utf8_out_text, type, channel);
}
-// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.0.0d) | Modified: RLVa-0.2.2a
+//void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel)
+// [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0b) | Modified: RLVa-0.2.2a
void send_chat_from_viewer(std::string utf8_out_text, EChatType type, S32 channel)
// [/RLVa:KB]
{
// [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0b) | Modified: RLVa-1.2.0a
// Only process chat messages (ie not CHAT_TYPE_START, CHAT_TYPE_STOP, etc)
- if ( (rlv_handler_t::isEnabled()) && ( (CHAT_TYPE_WHISPER == type) || (CHAT_TYPE_NORMAL == type) || (CHAT_TYPE_SHOUT == type) ) )
+ if ( (RlvActions::isRlvEnabled()) && ( (CHAT_TYPE_WHISPER == type) || (CHAT_TYPE_NORMAL == type) || (CHAT_TYPE_SHOUT == type) ) )
{
if (0 == channel)
{
- // (We already did this before, but LLChatHandler::handle() calls this directly)
- if ( ((CHAT_TYPE_SHOUT == type) || (CHAT_TYPE_NORMAL == type)) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATNORMAL)) )
- type = CHAT_TYPE_WHISPER;
- else if ( (CHAT_TYPE_SHOUT == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATSHOUT)) )
- type = CHAT_TYPE_NORMAL;
- else if ( (CHAT_TYPE_WHISPER == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATWHISPER)) )
- type = CHAT_TYPE_NORMAL;
+ // Clamp the volume of the chat if needed
+ type = RlvActions::checkChatVolume(type);
// Redirect chat if needed
if ( ( (gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT) || (gRlvHandler.hasBehaviour(RLV_BHVR_REDIREMOTE)) ) &&
@@ -763,7 +744,7 @@ void send_chat_from_viewer(std::string utf8_out_text, EChatType type, S32 channe
else
{
// Don't allow chat on a non-public channel if sendchannel restricted (unless the channel is an exception)
- if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDCHANNEL)) && (!gRlvHandler.isException(RLV_BHVR_SENDCHANNEL, channel)) )
+ if (!RlvActions::canSendChannel(channel))
return;
// Don't allow chat on debug channel if @sendchat, @redirchat or @rediremote restricted (shows as public chat on viewers)
@@ -836,33 +817,29 @@ void send_chat_from_viewer(std::string utf8_out_text, EChatType type, S32 channe
void really_send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel)
{
LLMessageSystem* msg = gMessageSystem;
- //
- if(channel >= 0)
+ if (channel >= 0)
{
- //
- msg->newMessageFast(_PREHASH_ChatFromViewer);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_ChatData);
- msg->addStringFast(_PREHASH_Message, utf8_out_text);
- msg->addU8Fast(_PREHASH_Type, type);
- msg->addS32("Channel", channel);
- //
+ msg->newMessageFast(_PREHASH_ChatFromViewer);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ChatData);
+ msg->addStringFast(_PREHASH_Message, utf8_out_text);
+ msg->addU8Fast(_PREHASH_Type, type);
+ msg->addS32("Channel", channel);
}
else
{
- msg->newMessage("ScriptDialogReply");
- msg->nextBlock("AgentData");
- msg->addUUID("AgentID", gAgent.getID());
- msg->addUUID("SessionID", gAgent.getSessionID());
- msg->nextBlock("Data");
- msg->addUUID("ObjectID", gAgent.getID());
+ msg->newMessageFast(_PREHASH_ScriptDialogReply);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_ObjectID, gAgent.getID());
msg->addS32("ChatChannel", channel);
- msg->addS32("ButtonIndex", 0);
- msg->addString("ButtonLabel", utf8_out_text);
+ msg->addS32Fast(_PREHASH_ButtonIndex, 0);
+ msg->addStringFast(_PREHASH_ButtonLabel, utf8_out_text);
}
- //
gAgent.sendReliableMessage();
LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT);
@@ -895,7 +872,7 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl)
}
}
mGestureLabelTimer.start();
- if (mGestureCombo != NULL)
+ if (mGestureCombo != nullptr)
{
// free focus back to chat bar
mGestureCombo->setFocus(FALSE);
@@ -907,24 +884,39 @@ void toggleChatHistory()
LLFloaterChat::toggleInstance(LLSD());
}
+//
+// LLChatCommandHandler
+//
-class LLChatHandler : public LLCommandHandler
+class LLChatCommandHandler final : public LLCommandHandler
{
public:
// not allowed from outside the app
- LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
+ LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
// Your code here
bool handle(const LLSD& tokens, const LLSD& query_map,
- LLMediaCtrl* web)
+ LLMediaCtrl* web) override
{
- if (tokens.size() < 2) return false;
- S32 channel = tokens[0].asInteger();
- std::string mesg = tokens[1].asString();
- send_chat_from_viewer(mesg, CHAT_TYPE_NORMAL, channel);
- return true;
+ bool retval = false;
+ // Need at least 2 tokens to have a valid message.
+ if (tokens.size() < 2)
+ {
+ retval = false;
+ }
+ else
+ {
+ S32 channel = tokens[0].asInteger();
+ {
+ retval = true;
+ // Send unescaped message, see EXT-6353.
+ std::string unescaped_mesg (LLURI::unescape(tokens[1].asString()));
+ send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel);
+ }
+ }
+ return retval;
}
};
// Creating the object registers with the dispatcher.
-LLChatHandler gChatHandler;
+LLChatCommandHandler gChatHandler;
diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h
index 71440d8bb..656da5132 100644
--- a/indra/newview/llchatbar.h
+++ b/indra/newview/llchatbar.h
@@ -33,10 +33,9 @@
#ifndef LL_LLCHATBAR_H
#define LL_LLCHATBAR_H
-#include "llpanel.h"
#include "llframetimer.h"
#include "llchat.h"
-#include "lllayoutstack.h"
+#include "llpanel.h"
class LLLineEditor;
class LLMessageSystem;
@@ -46,17 +45,19 @@ class LLFrameTimer;
class LLChatBarGestureObserver;
class LLComboBox;
-class LLChatBar : public LLPanel
+
+class LLChatBar final
+: public LLPanel
{
public:
// constructor for inline chat-bars (e.g. hosted in chat history window)
LLChatBar();
- ~LLChatBar();
- virtual BOOL postBuild();
- virtual BOOL handleKeyHere(KEY key, MASK mask);
+ BOOL postBuild() override;
+ BOOL handleKeyHere(KEY key, MASK mask) override;
+ void onFocusLost() override;
- void refresh();
+ void refresh() override;
void refreshGestures();
// Move cursor into chat input field.
@@ -65,8 +66,8 @@ public:
// Ignore arrow keys for chat bar
void setIgnoreArrowKeys(BOOL b);
- BOOL inputEditorHasFocus();
- std::string getCurrentChat();
+ BOOL inputEditorHasFocus() const;
+ std::string getCurrentChat() const;
// since chat bar logic is reused for chat history
// gesture combo box might not be a direct child
@@ -94,10 +95,11 @@ public:
static void stopChat();
protected:
+ ~LLChatBar();
+
void sendChat(EChatType type);
void updateChat();
-protected:
LLLineEditor* mInputEditor;
LLFrameTimer mGestureLabelTimer;
diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp
index 02d2e22de..17a869e87 100644
--- a/indra/newview/llcommandlineparser.cpp
+++ b/indra/newview/llcommandlineparser.cpp
@@ -293,19 +293,19 @@ bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp)
po::basic_parsed_options opts = clp.run();
po::store(opts, gVariableMap);
}
- catch(po::error& e)
+ catch (const po::error& e)
{
LL_WARNS() << "Caught Error:" << e.what() << LL_ENDL;
mErrorMsg = e.what();
return false;
}
- catch(LLCLPError& e)
+ catch (const LLCLPError& e)
{
LL_WARNS() << "Caught Error:" << e.what() << LL_ENDL;
mErrorMsg = e.what();
return false;
}
- catch(LLCLPLastOption&)
+ catch (const LLCLPLastOption&)
{
// This exception means a token was read after an option
// that must be the last option was reached (see url and slurl options)
@@ -354,18 +354,58 @@ bool LLCommandLineParser::parseCommandLine(int argc, char **argv)
return parseAndStoreResults(clp);
}
+// TODO:
+// - Break out this funky parsing logic into separate method
+// - Unit-test it with tests like LLStringUtil::getTokens() (the command-line
+// overload that supports quoted tokens)
+// - Unless this logic offers significant semantic benefits, replace it with
+// LLStringUtil::getTokens(). This would fix a known bug: you cannot --set a
+// string-valued variable to the empty string, because empty strings are
+// eliminated below.
+
bool LLCommandLineParser::parseCommandLineString(const std::string& str)
{
+ std::string cmd_line_string("");
+ if (!str.empty())
+ {
+ bool add_last_c = true;
+ S32 last_c_pos = str.size() - 1; //don't get out of bounds on pos+1, last char will be processed separately
+ for (S32 pos = 0; pos < last_c_pos; ++pos)
+ {
+ cmd_line_string.append(&str[pos], 1);
+ if (str[pos] == '\\')
+ {
+ cmd_line_string.append("\\", 1);
+ if (str[pos + 1] == '\\')
+ {
+ ++pos;
+ add_last_c = (pos != last_c_pos);
+ }
+ }
+ }
+ if (add_last_c)
+ {
+ cmd_line_string.append(&str[last_c_pos], 1);
+ if (str[last_c_pos] == '\\')
+ {
+ cmd_line_string.append("\\", 1);
+ }
+ }
+ }
+
// Split the string content into tokens
- boost::escaped_list_separator sep("\\", "\r\n ", "\"'");
- boost::tokenizer< boost::escaped_list_separator > tok(str, sep);
+ const char* escape_chars = "\\";
+ const char* separator_chars = "\r\n ";
+ const char* quote_chars = "\"'";
+ boost::escaped_list_separator sep(escape_chars, separator_chars, quote_chars);
+ boost::tokenizer< boost::escaped_list_separator > tok(cmd_line_string, sep);
std::vector tokens;
// std::copy(tok.begin(), tok.end(), std::back_inserter(tokens));
for(boost::tokenizer< boost::escaped_list_separator >::iterator i = tok.begin();
i != tok.end();
++i)
{
- if(0 != i->size())
+ if(!i->empty())
{
tokens.push_back(*i);
}
@@ -386,20 +426,28 @@ bool LLCommandLineParser::parseCommandLineFile(const std::basic_istream < char >
void LLCommandLineParser::notify()
{
- po::notify(gVariableMap);
+ try
+ {
+ po::notify(gVariableMap);
+ }
+ catch (const LLCLPError& e)
+ {
+ LL_WARNS() << "Caught Error: " << e.what() << LL_ENDL;
+ mErrorMsg = e.what();
+ }
}
void LLCommandLineParser::printOptions() const
{
- for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end(); ++i)
+ for (auto& i : gVariableMap)
{
- std::string name = i->first;
- token_vector_t values = i->second.as();
+ std::string name = i.first;
+ token_vector_t values = i.second.as();
std::ostringstream oss;
oss << name << ": ";
- for(token_vector_t::iterator t_itr = values.begin(); t_itr != values.end(); ++t_itr)
+ for (auto& value : values)
{
- oss << t_itr->c_str() << " ";
+ oss << value.c_str() << " ";
}
LL_INFOS() << oss.str() << LL_ENDL;
}
diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp
index fce835651..0e4a0168f 100644
--- a/indra/newview/llcompilequeue.cpp
+++ b/indra/newview/llcompilequeue.cpp
@@ -65,6 +65,7 @@
#include "lltrans.h"
#include "llselectmgr.h"
+#include "llexperiencecache.h"
// *TODO: This should be separated into the script queue, and the floater views of that queue.
// There should only be one floater class that can view any queue type
@@ -76,12 +77,12 @@
struct LLScriptQueueData
{
LLUUID mQueueID;
- std::string mScriptName;
+ LLPointer mItemp;
LLUUID mTaskId;
- LLUUID mItemId;
- LLScriptQueueData(const LLUUID& q_id, const std::string& name, const LLUUID& task_id, const LLUUID& item_id) :
- mQueueID(q_id), mScriptName(name), mTaskId(task_id), mItemId(item_id) {}
-
+ LLUUID mExperienceId;
+ LLHost mHost;
+ LLScriptQueueData(const LLUUID& q_id, const LLUUID& task_id, LLInventoryItem* item, const LLHost& host) :
+ mQueueID(q_id), mTaskId(task_id), mItemp(item), mHost(host) {}
};
///----------------------------------------------------------------------------
@@ -220,7 +221,7 @@ BOOL LLFloaterScriptQueue::nextObject()
LL_INFOS() << "LLFloaterScriptQueue::nextObject() - " << count
<< " objects left to process." << LL_ENDL;
mCurrentObjectID.setNull();
- if(count > 0)
+ if (count > 0)
{
successful_start = popNext();
}
@@ -228,7 +229,7 @@ BOOL LLFloaterScriptQueue::nextObject()
<< (successful_start ? "successful" : "unsuccessful")
<< LL_ENDL;
} while((mObjectIDs.size() > 0) && !successful_start);
- if(isDone() && !mDone)
+ if (isDone() && !mDone)
{
mDone = true;
getChild("queue output")->addSimpleElement(getString("Done"), ADD_BOTTOM);
@@ -245,14 +246,14 @@ BOOL LLFloaterScriptQueue::popNext()
// the inventory.
BOOL rv = FALSE;
S32 count = mObjectIDs.size();
- if(mCurrentObjectID.isNull() && (count > 0))
+ if (mCurrentObjectID.isNull() && (count > 0))
{
mCurrentObjectID = mObjectIDs.at(0);
LL_INFOS() << "LLFloaterScriptQueue::popNext() - mCurrentID: "
<< mCurrentObjectID << LL_ENDL;
mObjectIDs.erase(mObjectIDs.begin());
LLViewerObject* obj = gObjectList.findObject(mCurrentObjectID);
- if(obj)
+ if (obj)
{
LL_INFOS() << "LLFloaterScriptQueue::popNext() requesting inv for "
<< mCurrentObjectID << LL_ENDL;
@@ -270,35 +271,21 @@ BOOL LLFloaterScriptQueue::startQueue()
return nextObject();
}
-#if 0 // Singu TODO: Experiences
-class CompileQueueExperienceResponder : public LLHTTPClient::ResponderWithResult
+class CompileQueueExperienceResponder final : public LLHTTPClient::ResponderWithResult
{
+ const LLUUID mParent;
public:
- CompileQueueExperienceResponder(const LLUUID& parent):mParent(parent)
- {
- }
+ CompileQueueExperienceResponder(const LLUUID& parent): mParent(parent) {}
- LLUUID mParent;
-
- /*virtual*/ void httpSuccess()
- {
- sendResult(getContent());
- }
- /*virtual*/ void httpFailure()
- {
- sendResult(LLSD());
- }
+ void httpSuccess() override { sendResult(getContent()); }
+ void httpFailure() override { sendResult(LLSD()); }
void sendResult(const LLSD& content)
{
- LLFloaterCompileQueue* queue = (LLFloaterCompileQueue*) LLFloaterScriptQueue::findInstance(mParent);
- if(!queue)
- return;
-
- queue->experienceIdsReceived(content["experience_ids"]);
+ if (auto queue = static_cast(LLFloaterCompileQueue::findInstance(mParent)))
+ queue->experienceIdsReceived(content["experience_ids"]);
}
- /*virtual*/ char const* getName() const { return "RequiredRubbish"; }
+ char const* getName() const override { return "CompileQueueExperienceResponder"; }
};
-#endif
@@ -318,7 +305,7 @@ public:
virtual LLAssetUploadQueue* get() const
{
LLFloaterCompileQueue* queue = (LLFloaterCompileQueue*) LLFloaterScriptQueue::findInstance(mQueueId);
- if(NULL == queue)
+ if (NULL == queue)
{
return NULL;
}
@@ -328,7 +315,7 @@ public:
virtual void log(std::string message) const
{
LLFloaterCompileQueue* queue = (LLFloaterCompileQueue*) LLFloaterScriptQueue::findInstance(mQueueId);
- if(NULL == queue)
+ if (NULL == queue)
{
return;
}
@@ -363,6 +350,21 @@ LLFloaterCompileQueue::~LLFloaterCompileQueue()
{
}
+void LLFloaterCompileQueue::experienceIdsReceived(const LLSD& content)
+{
+ for(LLSD::array_const_iterator it = content.beginArray(); it != content.endArray(); ++it)
+ {
+ mExperienceIds.insert(it->asUUID());
+ }
+ nextObject();
+}
+
+BOOL LLFloaterCompileQueue::hasExperience(const LLUUID& id) const
+{
+ return mExperienceIds.find(id) != mExperienceIds.end();
+}
+
+
void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object,
LLInventoryObject::object_list_t* inv)
{
@@ -376,7 +378,7 @@ void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object,
LLInventoryObject::object_list_t::const_iterator end = inv->end();
for ( ; it != end; ++it)
{
- if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ if ((*it)->getType() == LLAssetType::AT_LSL_TEXT)
{
LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
// Check permissions before allowing the user to retrieve data.
@@ -397,31 +399,68 @@ void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object,
}
else
{
- // request all of the assets.
- uuid_item_map::iterator iter;
- for(iter = asset_item_map.begin(); iter != asset_item_map.end(); iter++)
+ LLViewerRegion* region = viewer_object->getRegion();
+ std::string url = std::string();
+ if (region)
{
- LLInventoryItem *itemp = iter->second;
- LLScriptQueueData* datap = new LLScriptQueueData(getID(),
- itemp->getName(),
- viewer_object->getID(),
- itemp->getUUID());
+ url = region->getCapability("GetMetadata");
+ }
- //LL_INFOS() << "ITEM NAME 2: " << names.get(i) << LL_ENDL;
- gAssetStorage->getInvItemAsset(viewer_object->getRegion()->getHost(),
- gAgent.getID(),
- gAgent.getSessionID(),
- itemp->getPermissions().getOwner(),
- viewer_object->getID(),
- itemp->getUUID(),
- itemp->getAssetUUID(),
- itemp->getType(),
- LLFloaterCompileQueue::scriptArrived,
- (void*)datap);
+ const auto& host = region->getHost();
+ const auto& obj_id = viewer_object->getID();
+ // request all of the assets.
+ for(const auto& pair : asset_item_map)
+ {
+ LLInventoryItem *itemp = pair.second;
+ LLScriptQueueData* datap = new LLScriptQueueData(getID(),
+ obj_id,
+ itemp,
+ host);
+
+ LLExperienceCache::instance().fetchAssociatedExperience(itemp->getParentUUID(), itemp->getUUID(),
+ boost::bind(LLFloaterCompileQueue::requestAsset, datap, _1));
}
}
}
+void LLFloaterCompileQueue::requestAsset(LLScriptQueueData* datap, const LLSD& experience)
+{
+ LLFloaterCompileQueue* queue = static_cast(findInstance(datap->mQueueID));
+ if (!queue)
+ {
+ delete datap;
+ return;
+ }
+
+ if (experience.has(LLExperienceCache::EXPERIENCE_ID))
+ {
+ datap->mExperienceId = experience[LLExperienceCache::EXPERIENCE_ID].asUUID();
+ if (!queue->hasExperience(datap->mExperienceId))
+ {
+ std::string buffer = LLTrans::getString("CompileNoExperiencePerm", LLSD::emptyMap()
+ .with("SCRIPT", datap->mItemp->getName())
+ .with("EXPERIENCE", experience[LLExperienceCache::NAME].asString()));
+
+ queue->getChild("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
+ queue->removeItemByItemID(datap->mItemp->getUUID());
+ delete datap;
+ return;
+ }
+ }
+
+ //LL_INFOS() << "ITEM NAME 2: " << names.get(i) << LL_ENDL;
+ gAssetStorage->getInvItemAsset(datap->mHost,
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ datap->mItemp->getPermissions().getOwner(),
+ datap->mTaskId,
+ datap->mItemp->getUUID(),
+ datap->mItemp->getAssetUUID(),
+ datap->mItemp->getType(),
+ LLFloaterCompileQueue::scriptArrived,
+ (void*)datap);
+}
+
// This is the callback for when each script arrives
// static
@@ -438,9 +477,9 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
LLFloaterCompileQueue* queue = static_cast (LLFloaterScriptQueue::findInstance(data->mQueueID));
std::string buffer;
- if(queue && (0 == status))
+ if (queue && (0 == status))
{
- //LL_INFOS() << "ITEM NAME 3: " << data->mScriptName << LL_ENDL;
+ //LL_INFOS() << "ITEM NAME 3: " << data->mItemp->getName() << LL_ENDL;
// Dump this into a file on the local disk so we can compile it.
std::string filename;
@@ -454,7 +493,7 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
if (object)
{
std::string url = object->getRegion()->getCapability("UpdateScriptTask");
- if(!url.empty())
+ if (!url.empty())
{
// Read script source in to buffer.
U32 script_size = file.getSize();
@@ -462,17 +501,17 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
file.read(script_data, script_size);
queue->mUploadQueue->queue(filename, data->mTaskId,
- data->mItemId, is_running, queue->mMono, queue->getID(),
- script_data, script_size, data->mScriptName);
+ data->mItemp->getUUID(), is_running, queue->mMono, queue->getID(),
+ script_data, script_size, data->mItemp->getName(), data->mExperienceId);
}
else
{
std::string text = LLTrans::getString("CompileQueueProblemUploading");
LLChat chat(text);
LLFloaterChat::addChat(chat);
- buffer = text + LLTrans::getString(":") + ' ' + data->mScriptName;
+ buffer = text + LLTrans::getString(":") + ' ' + data->mItemp->getName();
LL_WARNS() << "Problem uploading script asset." << LL_ENDL;
- if(queue) queue->removeItemByItemID(data->mItemId);
+ if(queue) queue->removeItemByItemID(data->mItemp->getUUID());
}
}
}
@@ -480,29 +519,29 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
{
LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
- if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status )
+ if ( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status )
{
LLChat chat(LLTrans::getString("CompileQueueScriptNotFound"));
LLFloaterChat::addChat(chat);
- buffer = LLTrans::getString("CompileQueueProblemDownloading") + LLTrans::getString(":") + ' ' + data->mScriptName;
+ buffer = LLTrans::getString("CompileQueueProblemDownloading") + LLTrans::getString(":") + ' ' + data->mItemp->getName();
}
else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
{
LLChat chat(LLTrans::getString("CompileQueueInsufficientPermDownload"));
LLFloaterChat::addChat(chat);
- buffer = LLTrans::getString("CompileQueueInsufficientPermFor") + LLTrans::getString(":") + ' ' + data->mScriptName;
+ buffer = LLTrans::getString("CompileQueueInsufficientPermFor") + LLTrans::getString(":") + ' ' + data->mItemp->getName();
}
else
{
- buffer = LLTrans::getString("CompileQueueUnknownFailure") + ' ' + data->mScriptName;
+ buffer = LLTrans::getString("CompileQueueUnknownFailure") + ' ' + data->mItemp->getName();
}
LL_WARNS() << "Problem downloading script asset." << LL_ENDL;
- if(queue) queue->removeItemByItemID(data->mItemId);
+ if(queue) queue->removeItemByItemID(data->mItemp->getUUID());
}
- if(queue && (buffer.size() > 0))
+ if (queue && (buffer.size() > 0))
{
queue->getChild("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
}
@@ -528,7 +567,7 @@ void LLFloaterCompileQueue::saveItemByItemID(const LLUUID& asset_id)
{
LL_INFOS() << "LLFloaterCompileQueue::saveItemByAssetID()" << LL_ENDL;
LLViewerObject* viewer_object = gObjectList.findObject(mCurrentObjectID);
- if(viewer_object)
+ if (viewer_object)
{
S32 count = mCurrentScripts.size();
for(S32 i = 0; i < count; ++i)
@@ -582,7 +621,7 @@ void LLFloaterResetQueue::handleInventory(LLViewerObject* viewer_obj,
LLInventoryObject::object_list_t::const_iterator end = inv->end();
for ( ; it != end; ++it)
{
- if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ if ((*it)->getType() == LLAssetType::AT_LSL_TEXT)
{
LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
@@ -641,7 +680,7 @@ void LLFloaterRunQueue::handleInventory(LLViewerObject* viewer_obj,
LLInventoryObject::object_list_t::const_iterator end = inv->end();
for ( ; it != end; ++it)
{
- if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ if ((*it)->getType() == LLAssetType::AT_LSL_TEXT)
{
LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
@@ -716,17 +755,16 @@ void LLFloaterCompileQueue::removeItemByItemID(const LLUUID& asset_id)
BOOL LLFloaterCompileQueue::startQueue()
{
- /* Singu TODO: Experiences
LLViewerRegion* region = gAgent.getRegion();
if (region)
{
- std::string lookup_url=region->getCapability("GetCreatorExperiences");
- if(!lookup_url.empty())
+ std::string lookup_url = region->getCapability("GetCreatorExperiences");
+ if (!lookup_url.empty())
{
- LLHTTPClient::get(lookup_url, new CompileQueueExperienceResponder(getKey().asUUID()));
+ LLHTTPClient::get(lookup_url, new CompileQueueExperienceResponder(mID));
return TRUE;
}
- }*/
+ }
return nextObject();
}
@@ -741,7 +779,7 @@ void LLFloaterNotRunQueue::handleInventory(LLViewerObject* viewer_obj,
LLInventoryObject::object_list_t::const_iterator end = inv->end();
for ( ; it != end; ++it)
{
- if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ if ((*it)->getType() == LLAssetType::AT_LSL_TEXT)
{
LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h
index 25212de11..fba5d295a 100644
--- a/indra/newview/llcompilequeue.h
+++ b/indra/newview/llcompilequeue.h
@@ -103,7 +103,7 @@ protected:
// Get this instance's ID.
const LLUUID& getID() const { return mID; }
-
+
protected:
// UI
LLScrollListCtrl* mMessages;
@@ -149,8 +149,8 @@ public:
LLAssetUploadQueue* getUploadQueue() { return mUploadQueue; }
- void experienceIdsReceived( const LLSD& content );
- BOOL hasExperience(const LLUUID& id)const;
+ void experienceIdsReceived(const LLSD& content);
+ BOOL hasExperience(const LLUUID& id) const;
protected:
LLFloaterCompileQueue(const std::string& name, const LLRect& rect);
@@ -160,6 +160,9 @@ protected:
virtual void handleInventory(LLViewerObject* viewer_obj,
LLInventoryObject::object_list_t* inv);
+ static void requestAsset(struct LLScriptQueueData* datap, const LLSD& experience);
+
+
// This is the callback for when each script arrives
static void scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
LLAssetType::EType type,
diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp
index 010a83916..31d027926 100644
--- a/indra/newview/llcontrolavatar.cpp
+++ b/indra/newview/llcontrolavatar.cpp
@@ -47,8 +47,8 @@ LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewer
LLVOAvatar(id, pcode, regionp),
mPlaying(false),
mGlobalScale(1.0f),
+ mRootVolp(NULL),
mMarkedForDeath(false),
- mRootVolp(NULL),
mScaleConstraintFixup(1.0),
mRegionChanged(false)
{
@@ -60,6 +60,8 @@ LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewer
// virtual
LLControlAvatar::~LLControlAvatar()
{
+ // Should already have been unlinked before destruction
+ llassert(!mRootVolp);
}
// virtual
@@ -82,18 +84,12 @@ void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_
{
F32 max_legal_offset = MAX_LEGAL_OFFSET;
- if (gSavedSettings.getControl("AnimatedObjectsMaxLegalOffset"))
- {
- max_legal_offset = gSavedSettings.getF32("AnimatedObjectsMaxLegalOffset");
- }
- max_legal_offset = llmax(max_legal_offset,0.f);
+ static LLCachedControl animated_object_max_legal_offset(gSavedSettings, "AnimatedObjectsMaxLegalOffset");
+ max_legal_offset = llmax(animated_object_max_legal_offset(),0.f);
F32 max_legal_size = MAX_LEGAL_SIZE;
- if (gSavedSettings.getControl("AnimatedObjectsMaxLegalSize"))
- {
- max_legal_size = gSavedSettings.getF32("AnimatedObjectsMaxLegalSize");
- }
- max_legal_size = llmax(max_legal_size, 1.f);
+ static LLCachedControl animated_object_max_legal_size(gSavedSettings, "AnimatedObjectsMaxLegalSize");
+ max_legal_size = llmax(animated_object_max_legal_size(), 1.f);
new_pos_fixup = LLVector3();
new_scale_fixup = 1.0f;
@@ -113,7 +109,7 @@ void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_
unshift_extents[0] = extents[0] - mPositionConstraintFixup;
unshift_extents[1] = extents[1] - mPositionConstraintFixup;
LLVector3 box_dims = extents[1]-extents[0];
- //F32 box_size = llmax(box_dims[0],box_dims[1],box_dims[2]);
+ F32 box_size = llmax(box_dims[0],box_dims[1],box_dims[2]);
if (!mRootVolp->isAttachment())
{
@@ -124,23 +120,23 @@ void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_
F32 target_dist = (offset_dist - max_legal_offset);
new_pos_fixup = (target_dist/offset_dist)*pos_box_offset;
}
- if (new_pos_fixup != mPositionConstraintFixup)
- {
- LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup "
- << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL;
- LL_DEBUGS("ConstraintFix") << "vol_pos " << vol_pos << LL_ENDL;
- LL_DEBUGS("ConstraintFix") << "extents " << extents[0] << " " << extents[1] << LL_ENDL;
- LL_DEBUGS("ConstraintFix") << "unshift_extents " << unshift_extents[0] << " " << unshift_extents[1] << LL_ENDL;
-
- }
+ //if (new_pos_fixup != mPositionConstraintFixup)
+ //{
+ // LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup "
+ // << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL;
+ // LL_DEBUGS("ConstraintFix") << "vol_pos " << vol_pos << LL_ENDL;
+ // LL_DEBUGS("ConstraintFix") << "extents " << extents[0] << " " << extents[1] << LL_ENDL;
+ // LL_DEBUGS("ConstraintFix") << "unshift_extents " << unshift_extents[0] << " " << unshift_extents[1] << LL_ENDL;
+ //
+ //}
}
- /*if (box_size/mScaleConstraintFixup > max_legal_size)
+ if (box_size/mScaleConstraintFixup > max_legal_size)
{
new_scale_fixup = mScaleConstraintFixup*max_legal_size/box_size;
- LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, box_size " << box_size << " fixup "
- << mScaleConstraintFixup << " max legal " << max_legal_size
- << " -> new scale " << new_scale_fixup << LL_ENDL;
- }*/
+ //LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, box_size " << box_size << " fixup "
+ // << mScaleConstraintFixup << " max legal " << max_legal_size
+ // << " -> new scale " << new_scale_fixup << LL_ENDL;
+ }
}
}
@@ -163,6 +159,8 @@ void LLControlAvatar::matchVolumeTransform()
mPositionConstraintFixup = new_pos_fixup;
mScaleConstraintFixup = new_scale_fixup;
+ static LLCachedControl global_scale(gSavedSettings, "AnimatedObjectsGlobalScale", 1.f);
+
if (mRootVolp->isAttachment())
{
LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor();
@@ -174,13 +172,12 @@ void LLControlAvatar::matchVolumeTransform()
LLVector3 joint_pos = attach->getWorldPosition();
LLQuaternion joint_rot = attach->getWorldRotation();
LLVector3 obj_pos = mRootVolp->mDrawable->getPosition();
- LLQuaternion obj_rot = mRootVolp->mDrawable->getRotation();
+ const LLQuaternion& obj_rot = mRootVolp->mDrawable->getRotation();
obj_pos.rotVec(joint_rot);
mRoot->setWorldPosition(obj_pos + joint_pos);
mRoot->setWorldRotation(obj_rot * joint_rot);
setRotation(mRoot->getRotation());
- F32 global_scale = gSavedSettings.getF32("AnimatedObjectsGlobalScale");
setGlobalScale(global_scale * mScaleConstraintFixup);
}
else
@@ -200,17 +197,7 @@ void LLControlAvatar::matchVolumeTransform()
// complexity info and such line up better. Should defer
// this until avatars also get fixed.
- LLQuaternion obj_rot;
- if (mRootVolp->mDrawable)
- {
- obj_rot = mRootVolp->mDrawable->getRotation();
- }
- else
- {
- obj_rot = mRootVolp->getRotation();
- }
-
- LLMatrix3 bind_mat;
+ const LLQuaternion& obj_rot = mRootVolp->mDrawable ? mRootVolp->mDrawable->getRotation() : mRootVolp->getRotation();
LLQuaternion bind_rot;
#define MATCH_BIND_SHAPE
@@ -219,7 +206,7 @@ void LLControlAvatar::matchVolumeTransform()
const LLMeshSkinInfo* skin_info = mRootVolp->getSkinInfo();
if (skin_info)
{
- LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
+ //LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
bind_rot = LLSkinningUtil::getUnscaledQuaternion(skin_info->mBindShapeMatrix);
}
#endif
@@ -228,7 +215,6 @@ void LLControlAvatar::matchVolumeTransform()
setPositionAgent(vol_pos);
mRoot->setPosition(vol_pos + mPositionConstraintFixup);
- F32 global_scale = gSavedSettings.getF32("AnimatedObjectsGlobalScale");
setGlobalScale(global_scale * mScaleConstraintFixup);
}
}
@@ -236,7 +222,7 @@ void LLControlAvatar::matchVolumeTransform()
void LLControlAvatar::setGlobalScale(F32 scale)
{
- if (scale <= 0.0)
+ if (scale <= 0.0f)
{
LL_WARNS() << "invalid global scale " << scale << LL_ENDL;
return;
@@ -255,10 +241,8 @@ void LLControlAvatar::recursiveScaleJoint(LLJoint* joint, F32 factor)
{
joint->setScale(factor * joint->getScale());
- for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
- iter != joint->mChildren.end(); ++iter)
+ for (auto child : joint->mChildren)
{
- LLJoint* child = *iter;
recursiveScaleJoint(child, factor);
}
}
@@ -278,10 +262,9 @@ void LLControlAvatar::updateVolumeGeom()
mRootVolp->mDrawable->setState(LLDrawable::USE_BACKLIGHT);
LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren();
- for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
- iter != child_list.end(); ++iter)
+ for (const auto& iter : child_list)
{
- LLViewerObject* childp = *iter;
+ LLViewerObject* childp = iter;
if (childp && childp->mDrawable.notNull())
{
childp->mDrawable->setState(LLDrawable::USE_BACKLIGHT);
@@ -339,6 +322,21 @@ LLControlAvatar *LLControlAvatar::createControlAvatar(LLVOVolume *obj)
void LLControlAvatar::markForDeath()
{
mMarkedForDeath = true;
+ mRootVolp = NULL;
+}
+
+void LLControlAvatar::markDead()
+{
+ // NOTE: this can happen when the control avatar and root volume are on different regions and we're
+ // being called from the LLViewerRegion destructor due the region being dropped
+ // (due to being used as a vehicle and the move not yet being processed?)
+ if (mRootVolp)
+ {
+ mRootVolp->unlinkControlAvatar();
+ mRootVolp = nullptr;
+ }
+
+ LLVOAvatar::markDead();
}
void LLControlAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
@@ -362,7 +360,8 @@ BOOL LLControlAvatar::updateCharacter(LLAgent &agent)
//virtual
void LLControlAvatar::updateDebugText()
{
- /*if (gSavedSettings.getBOOL("DebugAnimatedObjects"))
+ /*static LLCachedControl debug_animated_objects(gSavedSettings, "DebugAnimatedObjects");
+ if (debug_animated_objects)
{
S32 total_linkset_count = 0;
if (mRootVolp)
@@ -385,63 +384,64 @@ void LLControlAvatar::updateDebugText()
S32 cam_dist_count = 0;
F32 lod_radius = mRootVolp->mLODRadius;
- for (std::vector::iterator it = volumes.begin();
- it != volumes.end(); ++it)
+ for (auto volp : volumes)
{
- LLVOVolume *volp = *it;
- S32 verts = 0;
- total_tris += volp->getTriangleCount(&verts);
- total_verts += verts;
- est_tris += volp->getEstTrianglesMax();
- est_streaming_tris += volp->getEstTrianglesStreamingCost();
- streaming_cost += volp->getStreamingCost();
- lod_string += llformat("%d",volp->getLOD());
- if (volp && volp->mDrawable)
+ if (volp)
{
- bool is_animated_flag = volp->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
- if (is_animated_flag)
+ S32 verts = 0;
+ total_tris += volp->getTriangleCount(&verts);
+ total_verts += verts;
+ est_tris += volp->getEstTrianglesMax();
+ est_streaming_tris += volp->getEstTrianglesStreamingCost();
+ streaming_cost += volp->getStreamingCost();
+ lod_string += llformat("%d", volp->getLOD());
+ if (volp->mDrawable)
{
- animated_object_flag_string += "1";
+ bool is_animated_flag = volp->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
+ if (is_animated_flag)
+ {
+ animated_object_flag_string += "1";
+ }
+ else
+ {
+ animated_object_flag_string += "0";
+ }
+ if (volp->mDrawable->isActive())
+ {
+ active_string += "A";
+ }
+ else
+ {
+ active_string += "S";
+ }
+ if (volp->isRiggedMesh())
+ {
+ // Rigged/animatable mesh
+ type_string += "R";
+ lod_radius = volp->mLODRadius;
+ }
+ else if (volp->isMesh())
+ {
+ // Static mesh
+ type_string += "M";
+ }
+ else
+ {
+ // Any other prim
+ type_string += "P";
+ }
+ if (cam_dist_count < 4)
+ {
+ cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) + "/" +
+ LLStringOps::getReadableNumber(volp->mLODAdjustedDistance) + " ";
+ cam_dist_count++;
+ }
}
else
{
- animated_object_flag_string += "0";
+ active_string += "-";
+ type_string += "-";
}
- if (volp->mDrawable->isActive())
- {
- active_string += "A";
- }
- else
- {
- active_string += "S";
- }
- if (volp->isRiggedMesh())
- {
- // Rigged/animatable mesh
- type_string += "R";
- lod_radius = volp->mLODRadius;
- }
- else if (volp->isMesh())
- {
- // Static mesh
- type_string += "M";
- }
- else
- {
- // Any other prim
- type_string += "P";
- }
- if (cam_dist_count < 4)
- {
- cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) + "/" +
- LLStringOps::getReadableNumber(volp->mLODAdjustedDistance) + " ";
- cam_dist_count++;
- }
- }
- else
- {
- active_string += "-";
- type_string += "-";
}
}
addDebugText(llformat("CAV obj %d anim %d active %s impost %d upprd %d strcst %f",
@@ -491,11 +491,10 @@ void LLControlAvatar::getAnimatedVolumes(std::vector& volumes)
volumes.push_back(mRootVolp);
LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren();
- for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin();
- iter != child_list.end(); ++iter)
+ for (const auto& iter : child_list)
{
- LLViewerObject* childp = *iter;
- LLVOVolume *child_volp = dynamic_cast(childp);
+ LLViewerObject* childp = iter;
+ LLVOVolume *child_volp = childp ? childp->asVolume() : nullptr;
if (child_volp && child_volp->isAnimatedObject())
{
volumes.push_back(child_volp);
@@ -519,16 +518,17 @@ void LLControlAvatar::updateAnimations()
// Rebuild mSignaledAnimations from the associated volumes.
std::map anims;
- for (std::vector::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it)
+ for (auto vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it)
{
LLVOVolume *volp = *vol_it;
//LL_INFOS("AnimatedObjects") << "updating anim for vol " << volp->getID() << " root " << mRootVolp->getID() << LL_ENDL;
- signaled_animation_map_t& signaled_animations = LLObjectSignaledAnimationMap::instance().getMap()[volp->getID()];
- for (std::map::iterator anim_it = signaled_animations.begin();
- anim_it != signaled_animations.end();
+ auto& signaled_anim_map = LLObjectSignaledAnimationMap::instance().getMap();
+ signaled_animation_map_t& signaled_animations = signaled_anim_map[volp->getID()];
+ for (auto anim_it = signaled_animations.begin(), anim_it_end = signaled_animations.end();
+ anim_it != anim_it_end;
++anim_it)
{
- std::map::iterator found_anim_it = anims.find(anim_it->first);
+ auto found_anim_it = anims.find(anim_it->first);
if (found_anim_it != anims.end())
{
// Animation already present, use the larger sequence id
@@ -539,7 +539,9 @@ void LLControlAvatar::updateAnimations()
// Animation not already present, use this sequence id.
anims[anim_it->first] = anim_it->second;
}
+#if LL_DEBUG
LL_DEBUGS("AnimatedObjectsNotify") << "found anim for vol " << volp->getID() << " anim " << anim_it->first << " root " << mRootVolp->getID() << LL_ENDL;
+#endif
}
}
if (!mPlaying)
@@ -567,24 +569,45 @@ LLViewerObject* LLControlAvatar::lineSegmentIntersectRiggedAttachments(const LLV
LLVector4a* normal,
LLVector4a* tangent)
{
+ if (!mRootVolp)
+ {
+ return NULL;
+ }
+
LLViewerObject* hit = NULL;
if (lineSegmentBoundingBox(start, end))
{
LLVector4a local_end = end;
LLVector4a local_intersection;
-
- if (mRootVolp &&
- mRootVolp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, face_hit, &local_intersection, tex_coord, normal, tangent))
+ if (mRootVolp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, face_hit, &local_intersection, tex_coord, normal, tangent))
{
local_end = local_intersection;
if (intersection)
{
*intersection = local_intersection;
}
-
hit = mRootVolp;
}
+ else
+ {
+ std::vector volumes;
+ getAnimatedVolumes(volumes);
+
+ for (auto volp : volumes)
+ {
+ if (mRootVolp != volp && volp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, face_hit, &local_intersection, tex_coord, normal, tangent))
+ {
+ local_end = local_intersection;
+ if (intersection)
+ {
+ *intersection = local_intersection;
+ }
+ hit = volp;
+ break;
+ }
+ }
+ }
}
return hit;
@@ -618,7 +641,7 @@ bool LLControlAvatar::shouldRenderRigged() const
}
// virtual
-BOOL LLControlAvatar::isImpostor()
+BOOL LLControlAvatar::isImpostor() const
{
if (mRootVolp && mRootVolp->isAttachment())
{
@@ -638,8 +661,11 @@ void LLControlAvatar::onRegionChanged()
std::vector::iterator it = LLCharacter::sInstances.begin();
for ( ; it != LLCharacter::sInstances.end(); ++it)
{
- LLControlAvatar* cav = dynamic_cast(*it);
- if (!cav) continue;
- cav->mRegionChanged = true;
+ auto avatar = static_cast(*it);
+ if (!avatar->isDead() && avatar->isControlAvatar())
+ {
+ LLControlAvatar* cav = static_cast(avatar);
+ cav->mRegionChanged = true;
+ }
}
}
diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h
index 056593850..c01c5c0a9 100644
--- a/indra/newview/llcontrolavatar.h
+++ b/indra/newview/llcontrolavatar.h
@@ -30,16 +30,16 @@
#include "llvoavatar.h"
#include "llvovolume.h"
-class LLControlAvatar:
+class LLControlAvatar final:
public LLVOAvatar
{
LOG_CLASS(LLControlAvatar);
public:
LLControlAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
- virtual void initInstance(); // Called after construction to initialize the class.
+ void initInstance() override; // Called after construction to initialize the class.
virtual ~LLControlAvatar();
- virtual LLControlAvatar* asControlAvatar() { return this; }
+ LLControlAvatar* asControlAvatar() override { return this; }
void getNewConstraintFixups(LLVector3& new_pos_constraint, F32& new_scale_constraint) const;
void matchVolumeTransform();
@@ -52,14 +52,15 @@ public:
// Delayed kill so we don't make graphics pipeline unhappy calling
// markDead() inside other graphics pipeline operations.
void markForDeath();
+ void markDead() override;
- virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
- virtual BOOL updateCharacter(LLAgent &agent);
+ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) override;
+ BOOL updateCharacter(LLAgent &agent) override;
void getAnimatedVolumes(std::vector& volumes);
void updateAnimations();
- virtual LLViewerObject* lineSegmentIntersectRiggedAttachments(
+ LLViewerObject* lineSegmentIntersectRiggedAttachments(
const LLVector4a& start, const LLVector4a& end,
S32 face = -1, // which face to check, -1 = ALL_SIDES
BOOL pick_transparent = FALSE,
@@ -68,15 +69,15 @@ public:
LLVector4a* intersection = NULL, // return the intersection point
LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
LLVector4a* normal = NULL, // return the surface normal at the intersection point
- LLVector4a* tangent = NULL); // return the surface tangent at the intersection point
+ LLVector4a* tangent = NULL) override; // return the surface tangent at the intersection point
- virtual void updateDebugText();
+ void updateDebugText() override;
- virtual std::string getFullname() const;
+ std::string getFullname() const override;
- virtual bool shouldRenderRigged() const;
+ bool shouldRenderRigged() const override;
- virtual BOOL isImpostor();
+ BOOL isImpostor() const override;
bool mPlaying;
@@ -101,7 +102,7 @@ typedef std::map signaled_animation_map_t;
typedef std::map object_signaled_animation_map_t;
// Stores information about previously requested animations, by object id.
-class LLObjectSignaledAnimationMap: public LLSingleton
+class LLObjectSignaledAnimationMap final : public LLSingleton
{
public:
LLObjectSignaledAnimationMap() {}
diff --git a/indra/newview/llexperiencelog.cpp b/indra/newview/llexperiencelog.cpp
new file mode 100644
index 000000000..ea2e57535
--- /dev/null
+++ b/indra/newview/llexperiencelog.cpp
@@ -0,0 +1,289 @@
+/**
+ * @file llexperiencelog.cpp
+ * @brief llexperiencelog implementation
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llexperiencelog.h"
+
+#include "lldispatcher.h"
+#include "llsdserialize.h"
+#include "llviewergenericmessage.h"
+#include "llnotificationsutil.h"
+#include "lltrans.h"
+#include "llerror.h"
+#include "lldate.h"
+
+
+class LLExperienceLogDispatchHandler final : public LLDispatchHandler
+{
+public:
+ bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings) override
+ {
+ LLSD message;
+
+ sparam_t::const_iterator it = strings.begin();
+ if(it != strings.end()){
+ const std::string& llsdRaw = *it++;
+ std::istringstream llsdData(llsdRaw);
+ if (!LLSDSerialize::deserialize(message, llsdData, llsdRaw.length()))
+ {
+ LL_WARNS() << "LLExperienceLogDispatchHandler: Attempted to read parameter data into LLSD but failed:" << llsdRaw << LL_ENDL;
+ }
+ }
+ message["public_id"] = invoice;
+
+ // Object Name
+ if(it != strings.end())
+ {
+ message["ObjectName"] = *it++;
+ }
+
+ // parcel Name
+ if(it != strings.end())
+ {
+ message["ParcelName"] = *it++;
+ }
+ message["Count"] = 1;
+
+ LLExperienceLog::instance().handleExperienceMessage(message);
+ return true;
+ }
+};
+
+static LLExperienceLogDispatchHandler experience_log_dispatch_handler;
+
+void LLExperienceLog::handleExperienceMessage(LLSD& message)
+{
+ time_t now;
+ time(&now);
+ char daybuf[16];/* Flawfinder: ignore */
+ char time_of_day[16];/* Flawfinder: ignore */
+ strftime(daybuf, 16, "%Y-%m-%d", localtime(&now));
+ strftime(time_of_day, 16, " %H:%M:%S", localtime(&now));
+ message["Time"] = time_of_day;
+
+ std::string day = daybuf;
+
+ if(!mEvents.has(day))
+ {
+ mEvents[day] = LLSD::emptyArray();
+ }
+ LLSD& dayEvents = mEvents[day];
+ if(dayEvents.size() > 0)
+ {
+ LLSD& last = *(dayEvents.rbeginArray());
+ if( last["public_id"].asUUID() == message["public_id"].asUUID()
+ && last["ObjectName"].asString() == message["ObjectName"].asString()
+ && last["OwnerID"].asUUID() == message["OwnerID"].asUUID()
+ && last["ParcelName"].asString() == message["ParcelName"].asString()
+ && last["Permission"].asInteger() == message["Permission"].asInteger())
+ {
+ last["Count"] = last["Count"].asInteger() + 1;
+ last["Time"] = time_of_day;
+ mSignals(last);
+ return;
+ }
+ }
+ message["Time"] = time_of_day;
+ mEvents[day].append(message);
+ mSignals(message);
+}
+
+LLExperienceLog::LLExperienceLog()
+ : mMaxDays(7)
+ , mPageSize(25)
+ , mNotifyNewEvent(false)
+{
+}
+
+void LLExperienceLog::initialize()
+{
+ loadEvents();
+ if(!gGenericDispatcher.isHandlerPresent("ExperienceEvent"))
+ {
+ gGenericDispatcher.addHandler("ExperienceEvent", &experience_log_dispatch_handler);
+ }
+}
+
+std::string LLExperienceLog::getFilename()
+{
+ return gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "experience_events.xml");
+}
+
+
+std::string LLExperienceLog::getPermissionString( const LLSD& message, const std::string& base )
+{
+ std::ostringstream buf;
+ if(message.has("Permission"))
+ {
+ buf << base << message["Permission"].asInteger();
+ std::string entry;
+ if(LLTrans::findString(entry, buf.str()))
+ {
+ buf.str(entry);
+ }
+ }
+
+ if(buf.str().empty())
+ {
+ buf << base << "Unknown";
+
+ buf.str(LLTrans::getString(buf.str(), message));
+ }
+
+ return buf.str();
+}
+
+void LLExperienceLog::notify( LLSD& message )
+{
+ message["EventType"] = getPermissionString(message, "ExperiencePermission");
+ if(message.has("IsAttachment") && message["IsAttachment"].asBoolean())
+ {
+ LLNotificationsUtil::add("ExperienceEventAttachment", message);
+ }
+ else
+ {
+ LLNotificationsUtil::add("ExperienceEvent", message);
+ }
+ message.erase("EventType");
+}
+
+void LLExperienceLog::saveEvents()
+{
+ eraseExpired();
+ std::string filename = getFilename();
+ LLSD settings = LLSD::emptyMap().with("Events", mEvents);
+
+ settings["MaxDays"] = (int)mMaxDays;
+ settings["Notify"] = mNotifyNewEvent;
+ settings["PageSize"] = (int)mPageSize;
+
+ llofstream stream(filename.c_str());
+ LLSDSerialize::toPrettyXML(settings, stream);
+}
+
+
+void LLExperienceLog::loadEvents()
+{
+ LLSD settings = LLSD::emptyMap();
+
+ std::string filename = getFilename();
+ llifstream stream(filename.c_str());
+ LLSDSerialize::fromXMLDocument(settings, stream);
+
+ if(settings.has("MaxDays"))
+ {
+ setMaxDays((U32)settings["MaxDays"].asInteger());
+ }
+ if(settings.has("Notify"))
+ {
+ setNotifyNewEvent(settings["Notify"].asBoolean());
+ }
+ if(settings.has("PageSize"))
+ {
+ setPageSize((U32)settings["PageSize"].asInteger());
+ }
+ mEvents.clear();
+ if(mMaxDays > 0 && settings.has("Events"))
+ {
+ mEvents = settings["Events"];
+ }
+
+ eraseExpired();
+}
+
+LLExperienceLog::~LLExperienceLog()
+{
+ saveEvents();
+}
+
+void LLExperienceLog::eraseExpired()
+{
+ std::vector expired;
+ for (const auto& event_pair : mEvents.map())
+ {
+ const std::string& date = event_pair.first;
+ if (isExpired(date))
+ {
+ expired.push_back(date);
+ }
+ }
+
+ for (const auto& date : expired)
+ {
+ mEvents.erase(date);
+ }
+}
+
+bool LLExperienceLog::isExpired(const std::string& date) const
+{
+ if (date.empty())
+ return true;
+
+ S32 month, day, year = 0;
+ S32 matched = sscanf(date.c_str(), "%d-%d-%d", &year, &month, &day);
+ if (matched != 3) return false;
+ LLDate event_date;
+ event_date.fromYMDHMS(year, month, day);
+
+ return event_date.secondsSinceEpoch() <= (LLDate::now().secondsSinceEpoch() - F64(getMaxDays() * 86400U));
+}
+
+const LLSD& LLExperienceLog::getEvents() const
+{
+ return mEvents;
+}
+
+void LLExperienceLog::clear()
+{
+ mEvents.clear();
+}
+
+void LLExperienceLog::setMaxDays( U32 val )
+{
+ mMaxDays = val;
+}
+
+LLExperienceLog::callback_connection_t LLExperienceLog::addUpdateSignal( const callback_slot_t& cb )
+{
+ return mSignals.connect(cb);
+}
+
+void LLExperienceLog::setNotifyNewEvent( bool val )
+{
+ mNotifyNewEvent = val;
+ if(!val && mNotifyConnection.connected())
+ {
+ mNotifyConnection.disconnect();
+ }
+ else if( val && !mNotifyConnection.connected())
+ {
+ mNotifyConnection = addUpdateSignal(std::function(LLExperienceLog::notify));
+ }
+}
diff --git a/indra/newview/llexperiencelog.h b/indra/newview/llexperiencelog.h
new file mode 100644
index 000000000..27012898c
--- /dev/null
+++ b/indra/newview/llexperiencelog.h
@@ -0,0 +1,86 @@
+/**
+ * @file llexperiencelog.h
+ * @brief llexperiencelog and related class definitions
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+
+#ifndef LL_LLEXPERIENCELOG_H
+#define LL_LLEXPERIENCELOG_H
+
+#include "llsingleton.h"
+
+class LLExperienceLog final : public LLSingleton
+{
+ friend class LLSingleton;
+ LLExperienceLog();
+public:
+ typedef boost::signals2::signal
+ callback_signal_t;
+ typedef callback_signal_t::slot_type callback_slot_t;
+ typedef boost::signals2::connection callback_connection_t;
+ callback_connection_t addUpdateSignal(const callback_slot_t& cb);
+
+ void initialize();
+
+ U32 getMaxDays() const { return mMaxDays; }
+ void setMaxDays(U32 val);
+
+ bool getNotifyNewEvent() const { return mNotifyNewEvent; }
+ void setNotifyNewEvent(bool val);
+
+ U32 getPageSize() const { return mPageSize; }
+ void setPageSize(U32 val) { mPageSize = val; }
+
+ const LLSD& getEvents()const;
+ void clear();
+
+ virtual ~LLExperienceLog();
+
+ static void notify(LLSD& message);
+ static std::string getFilename();
+ static std::string getPermissionString(const LLSD& message, const std::string& base);
+ bool isExpired(const std::string& date) const;
+protected:
+ void handleExperienceMessage(LLSD& message);
+
+
+ void loadEvents();
+ void saveEvents();
+ void eraseExpired();
+
+ LLSD mEvents;
+ callback_signal_t mSignals;
+ callback_connection_t mNotifyConnection;
+ U32 mMaxDays;
+ U32 mPageSize;
+ bool mNotifyNewEvent;
+
+ friend class LLExperienceLogDispatchHandler;
+};
+
+
+
+
+#endif // LL_LLEXPERIENCELOG_H
diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp
index 3ed58db98..254d750cb 100644
--- a/indra/newview/llfavoritesbar.cpp
+++ b/indra/newview/llfavoritesbar.cpp
@@ -1130,7 +1130,7 @@ BOOL LLFavoritesBarCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
show_navbar_context_menu(this,x,y);
handled = true;
}*/
-
+
return handled;
}
void copy_slurl_to_clipboard_cb(std::string& slurl)
diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp
index 4a44b6a3a..cefa86f5c 100644
--- a/indra/newview/llfloaterauction.cpp
+++ b/indra/newview/llfloaterauction.cpp
@@ -3,38 +3,31 @@
* @author James Cook, Ian Wilkes
* @brief Implementation of the auction floater.
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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 "llfloaterauction.h"
-#include "lldir.h"
#include "llgl.h"
#include "llimagej2c.h"
#include "llimagetga.h"
@@ -46,17 +39,21 @@
#include "llagent.h"
#include "llcombobox.h"
+#include "llmimetypes.h"
#include "llnotifications.h"
-#include "llnotificationsutil.h"
+#include "llnotificationsutil.h"
#include "llviewertexturelist.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
-#include "lluictrlfactory.h"
#include "llviewerwindow.h"
#include "llviewerdisplay.h"
#include "llviewercontrol.h"
#include "llui.h"
+#include "lluictrlfactory.h"
#include "llrender.h"
+#include "llsdutil.h"
+#include "llsdutil_math.h"
+#include "lltrans.h"
///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
@@ -74,8 +71,8 @@ void auction_tga_upload_done(const LLUUID& asset_id,
LLFloaterAuction* LLFloaterAuction::sInstance = NULL;
// Default constructor
-LLFloaterAuction::LLFloaterAuction() :
- LLFloater(std::string("floater_auction")),
+LLFloaterAuction::LLFloaterAuction()
+ : LLFloater(std::string("floater_auction")),
mParcelID(-1)
{
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_auction.xml");
@@ -86,13 +83,13 @@ LLFloaterAuction::LLFloaterAuction() :
onCommitControlSetting(gSavedSettings), (void*)"AuctionShowFence");
childSetAction("snapshot_btn", onClickSnapshot, this);
- childSetAction("ok_btn", onClickOK, this);
+ childSetAction("ok_btn", onClickStartAuction, this);
}
// Destroys the object
LLFloaterAuction::~LLFloaterAuction()
{
- sInstance = NULL;
+ sInstance = nullptr;
}
// static
@@ -104,12 +101,23 @@ void LLFloaterAuction::show()
sInstance->center();
sInstance->setFocus(TRUE);
}
- sInstance->initialize();
sInstance->open(); /*Flawfinder: ignore*/
}
+BOOL LLFloaterAuction::postBuild()
+{
+ return TRUE;
+}
+
+void LLFloaterAuction::onOpen()
+{
+ initialize();
+}
+
void LLFloaterAuction::initialize()
{
+ mParcelUpdateCapUrl.clear();
+
mParcelp = LLViewerParcelMgr::getInstance()->getParcelSelection();
LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
LLParcel* parcelp = mParcelp->getParcel();
@@ -117,28 +125,30 @@ void LLFloaterAuction::initialize()
{
mParcelHost = region->getHost();
mParcelID = parcelp->getLocalID();
+ mParcelUpdateCapUrl = region->getCapability("ParcelPropertiesUpdate");
- childSetText("parcel_text", parcelp->getName());
- childEnable("snapshot_btn");
- childEnable("ok_btn");
+ getChild("parcel_text")->setValue(parcelp->getName());
+ getChildView("snapshot_btn")->setEnabled(TRUE);
+ getChildView("ok_btn")->setEnabled(true);
}
else
{
mParcelHost.invalidate();
if(parcelp && parcelp->getForSale())
{
- childSetText("parcel_text", getString("already for sale"));
+ getChild("parcel_text")->setValue(getString("already for sale"));
}
else
{
- childSetText("parcel_text", LLStringUtil::null);
+ getChild("parcel_text")->setValue(LLStringUtil::null);
}
mParcelID = -1;
- childSetEnabled("snapshot_btn", false);
- childSetEnabled("ok_btn", false);
+ getChildView("snapshot_btn")->setEnabled(false);
+ getChildView("ok_btn")->setEnabled(false);
}
+
mImageID.setNull();
- mImage = NULL;
+ mImage = nullptr;
}
void LLFloaterAuction::draw()
@@ -147,9 +157,10 @@ void LLFloaterAuction::draw()
if(!isMinimized() && mImage.notNull())
{
- LLRect rect;
- if (childGetRect("snapshot_icon", rect))
+ LLView* snapshot_icon = findChild("snapshot_icon");
+ if (snapshot_icon)
{
+ LLRect rect = snapshot_icon->getRect();
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
@@ -176,7 +187,7 @@ void LLFloaterAuction::onClickSnapshot(void* data)
LLPointer raw = new LLImageRaw;
- gForceRenderLandFence = self->childGetValue("fence_check").asBoolean();
+ gForceRenderLandFence = self->getChild("fence_check")->getValue().asBoolean();
BOOL success = gViewerWindow->rawSnapshot(raw,
gViewerWindow->getWindowWidth(),
gViewerWindow->getWindowHeight(),
@@ -218,13 +229,13 @@ void LLFloaterAuction::onClickSnapshot(void* data)
}
// static
-void LLFloaterAuction::onClickOK(void* data)
+void LLFloaterAuction::onClickStartAuction(void* data)
{
LLFloaterAuction* self = (LLFloaterAuction*)(data);
if(self->mImageID.notNull())
{
- LLSD parcel_name = self->childGetValue("parcel_text");
+ LLSD parcel_name = self->getChild("parcel_text")->getValue();
// create the asset
std::string* name = new std::string(parcel_name.asString());
@@ -257,11 +268,243 @@ void LLFloaterAuction::onClickOK(void* data)
msg->sendReliable(self->mParcelHost);
// clean up floater, and get out
- self->mImageID.setNull();
- self->mImage = NULL;
- self->mParcelID = -1;
- self->mParcelHost.invalidate();
- self->close();
+ self->cleanupAndClose();
+}
+
+
+void LLFloaterAuction::cleanupAndClose()
+{
+ mImageID.setNull();
+ mImage = nullptr;
+ mParcelID = -1;
+ mParcelHost.invalidate();
+ close();
+}
+
+
+
+// static glue
+void LLFloaterAuction::onClickResetParcel(void* data)
+{
+ LLFloaterAuction* self = (LLFloaterAuction*)(data);
+ if (self)
+ {
+ self->doResetParcel();
+ }
+}
+
+
+// Reset all the values for the parcel in preparation for a sale
+void LLFloaterAuction::doResetParcel()
+{
+ LLParcel* parcelp = mParcelp->getParcel();
+ LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
+
+ if (parcelp
+ && region
+ && !mParcelUpdateCapUrl.empty())
+ {
+ LLSD body;
+ std::string empty;
+
+ // request new properties update from simulator
+ U32 message_flags = 0x01;
+ body["flags"] = ll_sd_from_U32(message_flags);
+
+ // Set all the default parcel properties for auction
+ body["local_id"] = parcelp->getLocalID();
+
+ U32 parcel_flags = PF_ALLOW_LANDMARK |
+ PF_ALLOW_FLY |
+ PF_CREATE_GROUP_OBJECTS |
+ PF_ALLOW_ALL_OBJECT_ENTRY |
+ PF_ALLOW_GROUP_OBJECT_ENTRY |
+ PF_ALLOW_GROUP_SCRIPTS |
+ PF_RESTRICT_PUSHOBJECT |
+ PF_SOUND_LOCAL |
+ PF_ALLOW_VOICE_CHAT |
+ PF_USE_ESTATE_VOICE_CHAN;
+
+ body["parcel_flags"] = ll_sd_from_U32(parcel_flags);
+
+ // Build a parcel name like "Ahern (128,128) PG 4032m"
+ std::ostringstream parcel_name;
+ LLVector3 center_point( parcelp->getCenterpoint() );
+ center_point.snap(0); // Get rid of fractions
+ parcel_name << region->getName()
+ << " ("
+ << (S32) center_point.mV[VX]
+ << ","
+ << (S32) center_point.mV[VY]
+ << ") "
+ << region->getSimAccessString()
+ << " "
+ << parcelp->getArea()
+ << "m";
+
+ std::string new_name(parcel_name.str());
+ body["name"] = new_name;
+ getChild("parcel_text")->setValue(new_name); // Set name in dialog as well, since it won't get updated otherwise
+
+ body["sale_price"] = (S32) 0;
+ body["description"] = empty;
+ body["music_url"] = empty;
+ body["media_url"] = empty;
+ body["media_desc"] = empty;
+ body["media_type"] = LLMIMETypes::getDefaultMimeType();
+ body["media_width"] = (S32) 0;
+ body["media_height"] = (S32) 0;
+ body["auto_scale"] = (S32) 0;
+ body["media_loop"] = (S32) 0;
+ body["obscure_media"] = (S32) 0; // OBSOLETE - no longer used
+ body["obscure_music"] = (S32) 0; // OBSOLETE - no longer used
+ body["media_id"] = LLUUID::null;
+ body["group_id"] = MAINTENANCE_GROUP_ID; // Use maintenance group
+ body["pass_price"] = (S32) 10; // Defaults to $10
+ body["pass_hours"] = 0.0f;
+ body["category"] = (U8) LLParcel::C_NONE;
+ body["auth_buyer_id"] = LLUUID::null;
+ body["snapshot_id"] = LLUUID::null;
+ body["user_location"] = ll_sd_from_vector3( LLVector3::zero );
+ body["user_look_at"] = ll_sd_from_vector3( LLVector3::zero );
+ body["landing_type"] = (U8) LLParcel::L_DIRECT;
+
+ LL_INFOS() << "Sending parcel update to reset for auction via capability to: "
+ << mParcelUpdateCapUrl << LL_ENDL;
+ LLHTTPClient::post(mParcelUpdateCapUrl, body, new LLHTTPClient::ResponderIgnore());
+
+ // Send a message to clear the object return time
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelSetOtherCleanTime);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcelp->getLocalID());
+ msg->addS32Fast(_PREHASH_OtherCleanTime, 5); // 5 minute object auto-return
+
+ msg->sendReliable(region->getHost());
+
+ // Clear the access lists
+ clearParcelAccessList(parcelp, region, AL_ACCESS);
+ clearParcelAccessList(parcelp, region, AL_BAN);
+ clearParcelAccessList(parcelp, region, AL_ALLOW_EXPERIENCE);
+ clearParcelAccessList(parcelp, region, AL_BLOCK_EXPERIENCE);
+ }
+}
+
+
+
+void LLFloaterAuction::clearParcelAccessList(LLParcel* parcel, LLViewerRegion* region, U32 list)
+{
+ if (!region || !parcel) return;
+
+ LLUUID transactionUUID;
+ transactionUUID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addU32Fast(_PREHASH_Flags, list);
+ msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
+ msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
+ msg->addS32Fast(_PREHASH_SequenceID, 1); // sequence_id
+ msg->addS32Fast(_PREHASH_Sections, 0); // num_sections
+
+ // pack an empty block since there will be no data
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
+ msg->addS32Fast(_PREHASH_Time, 0 );
+ msg->addU32Fast(_PREHASH_Flags, 0 );
+
+ msg->sendReliable( region->getHost() );
+}
+
+
+
+// static - 'Sell to Anyone' clicked, throw up a confirmation dialog
+void LLFloaterAuction::onClickSellToAnyone(void* data)
+{
+ LLFloaterAuction* self = (LLFloaterAuction*)(data);
+ if (self)
+ {
+ LLParcel* parcelp = self->mParcelp->getParcel();
+
+ // Do a confirmation
+ S32 sale_price = parcelp->getArea(); // Selling for L$1 per meter
+ S32 area = parcelp->getArea();
+
+ LLSD args;
+ args["LAND_SIZE"] = llformat("%d", area);
+ args["SALE_PRICE"] = llformat("%d", sale_price);
+ args["NAME"] = LLTrans::getString("Anyone");
+
+ LLNotification::Params params("ConfirmLandSaleChange"); // Re-use existing dialog
+ params.substitutions(args)
+ .functor/*.function*/(boost::bind(&LLFloaterAuction::onSellToAnyoneConfirmed, self, _1, _2));
+
+ params.name("ConfirmLandSaleToAnyoneChange");
+
+ // ask away
+ LLNotifications::instance().add(params);
+ }
+}
+
+
+// Sell confirmation clicked
+bool LLFloaterAuction::onSellToAnyoneConfirmed(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0)
+ {
+ doSellToAnyone();
+ }
+
+ return false;
+}
+
+
+
+// Reset all the values for the parcel in preparation for a sale
+void LLFloaterAuction::doSellToAnyone()
+{
+ LLParcel* parcelp = mParcelp->getParcel();
+ LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
+
+ if (parcelp
+ && region
+ && !mParcelUpdateCapUrl.empty())
+ {
+ LLSD body;
+
+ // request new properties update from simulator
+ U32 message_flags = 0x01;
+ body["flags"] = ll_sd_from_U32(message_flags);
+
+ // Set all the default parcel properties for auction
+ body["local_id"] = parcelp->getLocalID();
+
+ // Set 'for sale' flag
+ U32 parcel_flags = parcelp->getParcelFlags() | PF_FOR_SALE;
+ // Ensure objects not included
+ parcel_flags &= ~PF_FOR_SALE_OBJECTS;
+ body["parcel_flags"] = ll_sd_from_U32(parcel_flags);
+
+ body["sale_price"] = parcelp->getArea(); // Sell for L$1 per square meter
+ body["auth_buyer_id"] = LLUUID::null; // To anyone
+
+ LL_INFOS() << "Sending parcel update to sell to anyone for L$1 via capability to: "
+ << mParcelUpdateCapUrl << LL_ENDL;
+
+ LLHTTPClient::post(mParcelUpdateCapUrl, body, new LLHTTPClient::ResponderIgnore());
+
+ // clean up floater, and get out
+ cleanupAndClose();
+ }
}
diff --git a/indra/newview/llfloaterauction.h b/indra/newview/llfloaterauction.h
index a9f1ea874..f431a84fc 100644
--- a/indra/newview/llfloaterauction.h
+++ b/indra/newview/llfloaterauction.h
@@ -3,31 +3,25 @@
* @author James Cook, Ian Wilkes
* @brief llfloaterauction class header file
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,7 +30,7 @@
#include "llfloater.h"
#include "lluuid.h"
-#include "llmemory.h"
+#include "llsafehandle.h"
#include "llviewertexture.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -45,34 +39,50 @@
// Class which holds the functionality to start auctions.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLParcelSelection;
+class LLParcel;
+class LLViewerRegion;
-class LLFloaterAuction : public LLFloater
+class LLFloaterAuction final : public LLFloater
{
public:
// LLFloater interface
- /*virtual*/ void onClose(bool app_quitting) { setVisible(FALSE); }
- /*virtual*/ void draw();
+ /*virtual*/ void onOpen() override;
+ /*virtual*/ void onClose(bool app_quitting) override { setVisible(FALSE); }
+ /*virtual*/ void draw() override;
// LLFloaterAuction interface
static void show();
-
private:
+
LLFloaterAuction();
~LLFloaterAuction();
+
void initialize();
static void onClickSnapshot(void* data);
- static void onClickOK(void* data);
+ static void onClickResetParcel(void* data);
+ static void onClickSellToAnyone(void* data); // Sell to anyone clicked
+ bool onSellToAnyoneConfirmed(const LLSD& notification, const LLSD& response); // Sell confirmation clicked
+ static void onClickStartAuction(void* data);
static LLFloaterAuction* sInstance;
+ /*virtual*/ BOOL postBuild() override;
+
+ void doResetParcel();
+ void doSellToAnyone();
+ void clearParcelAccessList(LLParcel* parcel, LLViewerRegion* region, U32 list);
+ void cleanupAndClose();
private:
+
LLTransactionID mTransactionID;
LLAssetID mImageID;
LLPointer mImage;
LLSafeHandle mParcelp;
S32 mParcelID;
LLHost mParcelHost;
+
+ std::string mParcelUpdateCapUrl; // "ParcelPropertiesUpdate" capability
};
diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp
index 0f90007f0..427997c02 100644
--- a/indra/newview/llfloateravatarlist.cpp
+++ b/indra/newview/llfloateravatarlist.cpp
@@ -134,8 +134,9 @@ namespace
}
} //namespace
+const LLColor4* mm_getMarkerColor(const LLUUID& id, bool mark_only = true);
LLAvatarListEntry::LLAvatarListEntry(const LLUUID& id, const std::string& name, const LLVector3d& position) :
- mID(id), mName(name), mPosition(position), mMarked(false), mFocused(false),
+ mID(id), mName(name), mPosition(position), mMarked(mm_getMarkerColor(id)), mFocused(false),
mStats(),
mActivityType(ACTIVITY_NEW), mActivityTimer(),
mIsInList(false), mAge(-1), mTime(time(NULL))
@@ -322,6 +323,17 @@ bool is_nearby(const LLUUID& id)
return std::find(avatars.begin(), avatars.end(), id) != avatars.end();
}
+const LLVector3d& get_av_pos(const LLUUID& id)
+{
+ if (const auto inst = LLFloaterAvatarList::getIfExists())
+ if (const auto av = inst->getAvatarEntry(id))
+ return av->getPosition();
+
+ LLWorld::pos_map_t avatars;
+ LLWorld::instance().getAvatars(&avatars);
+ return avatars[id];
+}
+
void track_av(const LLUUID& id)
{
if (auto inst = LLFloaterAvatarList::getIfExists())
@@ -336,83 +348,60 @@ void track_av(const LLUUID& id)
LLTracker::trackLocation(avatars[id], LLStringUtil::null, LLStringUtil::null);
}
-void teleport_to(const LLUUID& id)
-{
- if (auto entry = LLFloaterAvatarList::instanceExists() ? LLFloaterAvatarList::instance().getAvatarEntry(id) : nullptr)
- gAgent.teleportViaLocation(entry->getPosition());
- else
- {
- LLWorld::pos_map_t avatars;
- LLWorld::instance().getAvatars(&avatars);
- gAgent.teleportViaLocation(avatars[id]);
- }
-}
-
static void cmd_profile(const LLAvatarListEntry* entry);
-static void cmd_toggle_mark(LLAvatarListEntry* entry);
+static void cmd_toggle_mark(LLAvatarListEntry* entry)
+{
+ bool mark = !entry->isMarked();
+ void mm_setcolor(LLUUID key, LLColor4 col);
+ void mm_clearMark(const LLUUID & id);
+ mark ? mm_setcolor(entry->getID(), LLColor4::red) : mm_clearMark(entry->getID());
+ entry->setMarked(mark);
+}
static void cmd_ar(const LLAvatarListEntry* entry);
static void cmd_teleport(const LLAvatarListEntry* entry);
namespace
{
typedef LLMemberListener view_listener_t;
- class RadarTrack : public view_listener_t
+ class RadarTrack final : public view_listener_t
{
- bool handleEvent(LLPointer event, const LLSD& userdata)
+ bool handleEvent(LLPointer event, const LLSD& userdata) override
{
LLFloaterAvatarList::instance().onClickTrack();
return true;
}
};
- class RadarMark : public view_listener_t
+ class RadarFocus final : public view_listener_t
{
- bool handleEvent(LLPointer event, const LLSD& userdata)
- {
- LLFloaterAvatarList::instance().doCommand(cmd_toggle_mark);
- return true;
- }
- };
-
- class RadarFocus : public view_listener_t
- {
- bool handleEvent(LLPointer event, const LLSD& userdata)
+ bool handleEvent(LLPointer event, const LLSD& userdata) override
{
LLFloaterAvatarList::setFocusAvatar(LFIDBearer::getActiveSelectedID());
return true;
}
};
- class RadarFocusPrev : public view_listener_t
+ class RadarFocusPrev final : public view_listener_t
{
- bool handleEvent(LLPointer event, const LLSD& userdata)
+ bool handleEvent(LLPointer event, const LLSD& userdata) override
{
LLFloaterAvatarList::instance().focusOnPrev(userdata.asInteger());
return true;
}
};
- class RadarFocusNext : public view_listener_t
+ class RadarFocusNext final : public view_listener_t
{
- bool handleEvent(LLPointer event, const LLSD& userdata)
+ bool handleEvent(LLPointer event, const LLSD& userdata) override
{
LLFloaterAvatarList::instance().focusOnNext(userdata.asInteger());
return true;
}
};
- class RadarTeleportTo : public view_listener_t
+ class RadarAnnounceKeys final : public view_listener_t
{
- bool handleEvent(LLPointer event, const LLSD& userdata)
- {
- teleport_to(LFIDBearer::getActiveSelectedID());
- return true;
- }
- };
-
- class RadarAnnounceKeys : public view_listener_t
- {
- bool handleEvent(LLPointer event, const LLSD& userdata)
+ bool handleEvent(LLPointer event, const LLSD& userdata) override
{
LLFloaterAvatarList::instance().sendKeys();
return true;
@@ -425,11 +414,9 @@ void addMenu(view_listener_t* menu, const std::string& name);
void add_radar_listeners()
{
addMenu(new RadarTrack, "Radar.Track");
- addMenu(new RadarMark, "Radar.Mark");
addMenu(new RadarFocus, "Radar.Focus");
addMenu(new RadarFocusPrev, "Radar.FocusPrev");
addMenu(new RadarFocusNext, "Radar.FocusNext");
- addMenu(new RadarTeleportTo, "Radar.TeleportTo");
addMenu(new RadarAnnounceKeys, "Radar.AnnounceKeys");
}
@@ -790,7 +777,7 @@ void LLFloaterAvatarList::refreshAvatarList()
if (entry->isMarked())
{
mark.value = "X";
- mark.color = LLColor4::blue;
+ mark.color = *mm_getMarkerColor(av_id);
mark.font_style = "BOLD";
}
element.columns.add(mark);
@@ -1246,7 +1233,7 @@ void LLFloaterAvatarList::removeFocusFromAll()
// static
void LLFloaterAvatarList::setFocusAvatar(const LLUUID& id)
{
- if (!gAgentCamera.lookAtObject(id, false) && !lookAtAvatar(id)) return;
+ if (/*!gAgentCamera.lookAtObject(id, false) &&*/ !lookAtAvatar(id)) return;
if (auto inst = getIfExists())
inst->setFocusAvatarInternal(id);
}
@@ -1460,7 +1447,6 @@ void send_estate_message(const std::string request, const std::vectorgetName()); }
-static void cmd_toggle_mark(LLAvatarListEntry* entry) { entry->toggleMark(); }
static void cmd_ar(const LLAvatarListEntry* entry) { LLFloaterReporter::showFromObject(entry->getID()); }
static void cmd_profile(const LLAvatarListEntry* entry) { LLAvatarActions::showProfile(entry->getID()); }
static void cmd_teleport(const LLAvatarListEntry* entry) { gAgent.teleportViaLocation(entry->getPosition()); }
diff --git a/indra/newview/llfloateravatarlist.h b/indra/newview/llfloateravatarlist.h
index 480e52f52..5c8321987 100644
--- a/indra/newview/llfloateravatarlist.h
+++ b/indra/newview/llfloateravatarlist.h
@@ -124,7 +124,7 @@ enum ACTIVITY_TYPE
bool isInList() const { return mIsInList; }
- void toggleMark() { mMarked = !mMarked; }
+ void setMarked(bool marked) { mMarked = marked; }
struct uuidMatch
{
diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp
index e36a41975..a2c7a2d12 100644
--- a/indra/newview/llfloaterchat.cpp
+++ b/indra/newview/llfloaterchat.cpp
@@ -225,12 +225,13 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4&
style->setColor(color);
style->mItalic = is_irc;
style->mBold = chat.mChatType == CHAT_TYPE_SHOUT;
- edit->appendText(line, false, prepend_newline, style, false);
+ edit->appendText(line, false, prepend_newline, style, chat.mSourceType == CHAT_SOURCE_SYSTEM);
}
void LLFloaterChat::addChatHistory(const std::string& str, bool log_to_file)
{
LLChat chat(str);
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
addChatHistory(chat, log_to_file);
}
@@ -276,7 +277,7 @@ void LLFloaterChat::addChatHistory(LLChat& chat, bool log_to_file)
LLColor4 color = get_text_color(chat);
- if (!log_to_file) color = LLColor4::grey; //Recap from log file.
+ if (!log_to_file) color = gSavedSettings.getColor("LogChatColor"); //Recap from log file.
if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
{
@@ -344,6 +345,7 @@ void LLFloaterChat::onClickToggleShowMute(bool show_mute, LLTextEditor* history_
void LLFloaterChat::addChat(const std::string& str, BOOL from_im, BOOL local_agent)
{
LLChat chat(str);
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
addChat(chat, from_im, local_agent);
}
diff --git a/indra/newview/llfloaterexperiencepicker.cpp b/indra/newview/llfloaterexperiencepicker.cpp
new file mode 100644
index 000000000..7613001b3
--- /dev/null
+++ b/indra/newview/llfloaterexperiencepicker.cpp
@@ -0,0 +1,167 @@
+/**
+* @file llfloaterexperiencepicker.cpp
+* @brief Implementation of llfloaterexperiencepicker
+* @author dolphin@lindenlab.com
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterexperiencepicker.h"
+
+
+#include "lllineeditor.h"
+//#include "llfloaterreg.h"
+#include "lluictrlfactory.h"
+#include "llscrolllistctrl.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+#include "llexperiencecache.h"
+#include "llslurl.h"
+#include "llavatarnamecache.h"
+#include "llfloaterexperienceprofile.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "lldraghandle.h"
+#include "llpanelexperiencepicker.h"
+
+//
+LLFloaterExperiencePicker* show_xp_picker(const LLSD& key)
+{
+ LLFloaterExperiencePicker* floater =
+ LLFloaterExperiencePicker::getInstance(key);
+ if (!floater)
+ {
+ floater = new LLFloaterExperiencePicker(key);
+ }
+ floater->open();
+ return floater;
+}
+//
+
+LLFloaterExperiencePicker* LLFloaterExperiencePicker::show( select_callback_t callback, const LLUUID& key, BOOL allow_multiple, BOOL close_on_select, filter_list filters, LLView * frustumOrigin )
+{
+ LLFloaterExperiencePicker* floater = show_xp_picker(key);
+
+ if (floater->mSearchPanel)
+ {
+ floater->mSearchPanel->mSelectionCallback = callback;
+ floater->mSearchPanel->mCloseOnSelect = close_on_select;
+ floater->mSearchPanel->setAllowMultiple(allow_multiple);
+ floater->mSearchPanel->setDefaultFilters();
+ floater->mSearchPanel->addFilters(filters.begin(), filters.end());
+ floater->mSearchPanel->filterContent();
+ }
+
+ if(frustumOrigin)
+ {
+ floater->mFrustumOrigin = frustumOrigin->getHandle();
+ }
+
+ return floater;
+}
+
+void LLFloaterExperiencePicker::drawFrustum()
+{
+ if(mFrustumOrigin.get())
+ {
+ LLView * frustumOrigin = mFrustumOrigin.get();
+ LLRect origin_rect;
+ frustumOrigin->localRectToOtherView(frustumOrigin->getLocalRect(), &origin_rect, this);
+ // draw context cone connecting color picker with color swatch in parent floater
+ LLRect local_rect = getLocalRect();
+ if (hasFocus() && frustumOrigin->isInVisibleChain() && mContextConeOpacity > 0.001f)
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ LLGLEnable cull_face;
+ gGL.begin(LLRender::TRIANGLE_STRIP);
+ {
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mRight, origin_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mRight, origin_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mLeft, origin_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity);
+ gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop);
+ }
+ gGL.end();
+ }
+
+ if (gFocusMgr.childHasMouseCapture(getDragHandle()))
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), LLCriticalDamp::getInterpolant(mContextConeFadeTime));
+ }
+ else
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(mContextConeFadeTime));
+ }
+ }
+}
+
+void LLFloaterExperiencePicker::draw()
+{
+ drawFrustum();
+ LLFloater::draw();
+}
+
+LLFloaterExperiencePicker::LLFloaterExperiencePicker( const LLSD& key )
+ :LLFloater()
+ ,LLInstanceTracker(key.asUUID())
+ ,mSearchPanel(nullptr)
+ ,mContextConeOpacity(0.f)
+ ,mContextConeInAlpha(0.f)
+ ,mContextConeOutAlpha(0.f)
+ ,mContextConeFadeTime(0.f)
+{
+ mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha");
+ mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha");
+ mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime");
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_experience_search.xml", NULL, false);
+ //buildFromFile("floater_experience_search.xml");
+}
+
+LLFloaterExperiencePicker::~LLFloaterExperiencePicker()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+}
+
+BOOL LLFloaterExperiencePicker::postBuild()
+{
+ mSearchPanel = new LLPanelExperiencePicker();
+ addChild(mSearchPanel);
+ mSearchPanel->setOrigin(0, 0);
+ return LLFloater::postBuild();
+}
diff --git a/indra/newview/llfloaterexperiencepicker.h b/indra/newview/llfloaterexperiencepicker.h
new file mode 100644
index 000000000..4aff63169
--- /dev/null
+++ b/indra/newview/llfloaterexperiencepicker.h
@@ -0,0 +1,69 @@
+/**
+* @file llfloaterexperiencepicker.h
+* @brief Header file for llfloaterexperiencepicker
+* @author dolphin@lindenlab.com
+*
+* $LicenseInfo:firstyear=2014&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2014, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LL_LLFLOATEREXPERIENCEPICKER_H
+#define LL_LLFLOATEREXPERIENCEPICKER_H
+
+#include "llfloater.h"
+#include "llinstancetracker.h"
+
+class LLScrollListCtrl;
+class LLLineEditor;
+class LLPanelExperiencePicker;
+
+
+class LLFloaterExperiencePicker : public LLFloater
+, public LLInstanceTracker
+{
+public:
+
+ typedef std::function select_callback_t;
+ // filter function for experiences, return true if the experience should be hidden.
+ typedef std::function filter_function;
+ typedef std::vector filter_list;
+
+ static LLFloaterExperiencePicker* show( select_callback_t callback, const LLUUID& key, BOOL allow_multiple, BOOL close_on_select, filter_list filters, LLView * frustumOrigin);
+
+ LLFloaterExperiencePicker(const LLSD& key);
+ virtual ~LLFloaterExperiencePicker();
+
+ BOOL postBuild() override;
+
+ void draw() override;
+private:
+
+ LLPanelExperiencePicker* mSearchPanel;
+
+ void drawFrustum();
+ LLHandle mFrustumOrigin;
+ F32 mContextConeOpacity;
+ F32 mContextConeInAlpha;
+ F32 mContextConeOutAlpha;
+ F32 mContextConeFadeTime;
+};
+
+#endif // LL_LLFLOATEREXPERIENCEPICKER_H
+
diff --git a/indra/newview/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp
new file mode 100644
index 000000000..3195a3bda
--- /dev/null
+++ b/indra/newview/llfloaterexperienceprofile.cpp
@@ -0,0 +1,934 @@
+/**
+ * @file llfloaterexperienceprofile.cpp
+ * @brief llfloaterexperienceprofile and related class definitions
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, 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 "llfloaterexperienceprofile.h"
+
+#include "llagent.h"
+#include "llappviewer.h"
+#include "llavatarnamecache.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llcommandhandler.h"
+//#include "llexpandabletextbox.h"
+#include "llexperiencecache.h"
+//#include "llfloaterreg.h"
+#include "llgroupactions.h"
+#include "lluictrlfactory.h"
+#include "lllayoutstack.h"
+#include "lllineeditor.h"
+#include "llnotificationsutil.h"
+#include "llsdserialize.h"
+#include "llslurl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltexteditor.h"
+#include "lltexturectrl.h"
+#include "lltrans.h"
+#include "llviewerregion.h"
+#include "llevents.h"
+#include "llfloatergroups.h"
+#include "llnotifications.h"
+#include "llfloaterreporter.h"
+#include "llurlaction.h"
+
+#define XML_PANEL_EXPERIENCE_PROFILE "floater_experienceprofile.xml"
+#define TF_NAME "experience_title"
+#define TF_DESC "experience_description"
+#define TF_SLURL "LocationTextText"
+#define TF_MRKT "marketplace"
+#define TF_MATURITY "ContentRatingText"
+#define TF_OWNER "OwnerText"
+#define TF_GROUP "GroupText"
+#define TF_GRID_WIDE "grid_wide"
+#define TF_PRIVILEGED "privileged"
+#define EDIT "edit_"
+
+#define IMG_LOGO "logo"
+
+#define PNL_TOP "top panel"
+#define PNL_IMAGE "image_panel"
+#define PNL_DESC "description panel"
+#define PNL_LOC "location panel"
+#define PNL_MRKT "marketplace panel"
+#define PNL_GROUP "group_panel"
+#define PNL_PERMS "perm panel"
+
+#define BTN_ALLOW "allow_btn"
+#define BTN_BLOCK "block_btn"
+#define BTN_CANCEL "cancel_btn"
+#define BTN_CLEAR_LOCATION "clear_btn"
+#define BTN_EDIT "edit_btn"
+#define BTN_ENABLE "enable_btn"
+#define BTN_FORGET "forget_btn"
+#define BTN_PRIVATE "private_btn"
+#define BTN_REPORT "report_btn"
+#define BTN_SAVE "save_btn"
+#define BTN_SET_GROUP "Group_btn"
+#define BTN_SET_LOCATION "location_btn"
+
+
+class LLExperienceHandler : public LLCommandHandler
+{
+public:
+ LLExperienceHandler() : LLCommandHandler("experience", UNTRUSTED_THROTTLE) { }
+
+ bool handle(const LLSD& params, const LLSD& query_map,
+ LLMediaCtrl* web) override
+ {
+ if (params.size() != 2 || params[1].asString() != "profile")
+ return false;
+
+ LLExperienceCache::instance().get(params[0].asUUID(), boost::bind(&LLExperienceHandler::experienceCallback, this, _1));
+ return true;
+ }
+
+ void experienceCallback(const LLSD& experienceDetails)
+ {
+ if (!experienceDetails.has(LLExperienceCache::MISSING))
+ {
+ LLFloaterExperienceProfile::showInstance(experienceDetails[LLExperienceCache::EXPERIENCE_ID].asUUID());
+ }
+ }
+};
+
+LLExperienceHandler gExperienceHandler;
+
+void LLFloaterExperienceProfile::showInstance(const LLSD& data)
+{
+ bool is_map = data.has("experience_id");
+ LLFloaterExperienceProfile* floater = getInstance(is_map ? data["experience_id"].asUUID() : data.asUUID());
+ if (!floater) floater = new LLFloaterExperienceProfile(data);
+ else if (is_map && data.has("edit_experience") && data["edit_experience"].asBoolean())
+ floater->changeToEdit();
+ floater->open();
+}
+
+LLFloaterExperienceProfile::LLFloaterExperienceProfile(const LLSD& data)
+ : LLFloater(), LLInstanceTracker(data.asUUID())
+ , mSaveCompleteAction(NOTHING)
+ , mDirty(false)
+ , mForceClose(false)
+{
+ if (data.has("experience_id"))
+{
+ mExperienceId = data["experience_id"].asUUID();
+ mPostEdit = data.has("edit_experience") && data["edit_experience"].asBoolean();
+}
+ else
+{
+ mExperienceId = data.asUUID();
+ mPostEdit = false;
+ }
+ LLUICtrlFactory::getInstance()->buildFloater(this, XML_PANEL_EXPERIENCE_PROFILE, NULL, false);
+ //buildFromFile(XML_PANEL_EXPERIENCE_PROFILE);
+}
+
+
+LLFloaterExperienceProfile::~LLFloaterExperienceProfile()
+{
+
+}
+
+BOOL LLFloaterExperienceProfile::postBuild()
+{
+
+ if (mExperienceId.notNull())
+ {
+ LLExperienceCache::instance().fetch(mExperienceId, true);
+ LLExperienceCache::instance().get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback,
+ getDerivedHandle(), _1));
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ LLExperienceCache::instance().getExperienceAdmin(mExperienceId, boost::bind(
+ &LLFloaterExperienceProfile::experienceIsAdmin, getDerivedHandle(), _1));
+ }
+ }
+
+ childSetAction(BTN_EDIT, boost::bind(&LLFloaterExperienceProfile::onClickEdit, this));
+ childSetAction(BTN_ALLOW, boost::bind(&LLFloaterExperienceProfile::onClickPermission, this, "Allow"));
+ childSetAction(BTN_FORGET, boost::bind(&LLFloaterExperienceProfile::onClickForget, this));
+ childSetAction(BTN_BLOCK, boost::bind(&LLFloaterExperienceProfile::onClickPermission, this, "Block"));
+ childSetAction(BTN_CANCEL, boost::bind(&LLFloaterExperienceProfile::onClickCancel, this));
+ childSetAction(BTN_SAVE, boost::bind(&LLFloaterExperienceProfile::onClickSave, this));
+ childSetAction(BTN_SET_LOCATION, boost::bind(&LLFloaterExperienceProfile::onClickLocation, this));
+ childSetAction(BTN_CLEAR_LOCATION, boost::bind(&LLFloaterExperienceProfile::onClickClear, this));
+ childSetAction(BTN_SET_GROUP, boost::bind(&LLFloaterExperienceProfile::onPickGroup, this));
+ childSetAction(BTN_REPORT, boost::bind(&LLFloaterExperienceProfile::onReportExperience, this));
+
+ getChild(EDIT TF_DESC)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this));
+ getChild(EDIT TF_MATURITY)->setCommitCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this));
+ getChild(EDIT TF_MRKT)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this));
+ getChild(EDIT TF_NAME)->setKeystrokeCallback(boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this));
+
+ childSetCommitCallback(EDIT BTN_ENABLE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), nullptr);
+ childSetCommitCallback(EDIT BTN_PRIVATE, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), nullptr);
+
+ childSetCommitCallback(EDIT IMG_LOGO, boost::bind(&LLFloaterExperienceProfile::onFieldChanged, this), nullptr);
+
+ if (auto logo = findChild(IMG_LOGO))
+ {
+ void show_picture(const LLUUID& id, const std::string& name);
+ LLTextBox* name = getChild(TF_NAME);
+ std::function cb = [logo, name]() { show_picture(logo->getImageAssetID(), "Experience Picture: " + name->getText()); };
+ logo->setMouseUpCallback(boost::bind(cb));
+ }
+
+ getChild(EDIT TF_DESC)->setCommitOnFocusLost(TRUE);
+
+ LLEventPumps::instance().obtain("experience_permission").listen(mExperienceId.asString()+"-profile",
+ boost::bind(&LLFloaterExperienceProfile::experiencePermission, getDerivedHandle(this), _1));
+
+ if (mPostEdit && mExperienceId.notNull())
+ {
+ mPostEdit = false;
+ changeToEdit();
+ }
+
+ return TRUE;
+}
+
+void LLFloaterExperienceProfile::experienceCallback(LLHandle handle, const LLSD& experience )
+{
+ LLFloaterExperienceProfile* pllpep = handle.get();
+ if (pllpep)
+ {
+ pllpep->refreshExperience(experience);
+ }
+}
+
+
+bool LLFloaterExperienceProfile::experiencePermission( LLHandle handle, const LLSD& permission )
+{
+ LLFloaterExperienceProfile* pllpep = handle.get();
+ if (pllpep)
+ {
+ pllpep->updatePermission(permission);
+ }
+ return false;
+}
+
+
+void LLFloaterExperienceProfile::onClickEdit()
+{
+ changeToEdit();
+}
+
+
+void LLFloaterExperienceProfile::onClickCancel()
+{
+ changeToView();
+}
+
+void LLFloaterExperienceProfile::onClickSave()
+{
+ doSave(NOTHING);
+}
+
+void LLFloaterExperienceProfile::onClickPermission(const char* perm)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return;
+ LLExperienceCache::instance().setExperiencePermission(mExperienceId, perm, boost::bind(
+ &LLFloaterExperienceProfile::experiencePermissionResults, mExperienceId, _1));
+}
+
+
+void LLFloaterExperienceProfile::onClickForget()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return;
+
+ LLExperienceCache::instance().forgetExperiencePermission(mExperienceId, boost::bind(
+ &LLFloaterExperienceProfile::experiencePermissionResults, mExperienceId, _1));
+}
+
+bool LLFloaterExperienceProfile::setMaturityString( U8 maturity, LLTextBox* child, LLComboBox* combo )
+{
+ //LLStyle::Params style; // Singu Note: Nope.
+ std::string access;
+ if (maturity <= SIM_ACCESS_PG)
+ {
+ //style.image(LLUI::getUIImage(getString("maturity_icon_general"))); // Singu Note: Nope.
+ access = LLTrans::getString("SIM_ACCESS_PG");
+ combo->setCurrentByIndex(2);
+ }
+ else if (maturity <= SIM_ACCESS_MATURE)
+ {
+ //style.image(LLUI::getUIImage(getString("maturity_icon_moderate"))); // Singu Note: Nope.
+ access = LLTrans::getString("SIM_ACCESS_MATURE");
+ combo->setCurrentByIndex(1);
+ }
+ else if (maturity <= SIM_ACCESS_ADULT)
+ {
+ //style.image(LLUI::getUIImage(getString("maturity_icon_adult"))); // Singu Note: Nope.
+ access = LLTrans::getString("SIM_ACCESS_ADULT");
+ combo->setCurrentByIndex(0);
+ }
+ else
+ {
+ return false;
+ }
+
+ /* Singu Note: Nope.
+ child->setText(LLStringUtil::null);
+
+ child->appendImageSegment(style);
+ */
+ child->setText(access);
+
+ return true;
+}
+
+
+void LLFloaterExperienceProfile::refreshExperience( const LLSD& experience )
+{
+ mExperienceDetails = experience;
+ mPackage = experience;
+
+
+ LLLayoutPanel* imagePanel = getChild(PNL_IMAGE);
+ LLLayoutPanel* descriptionPanel = getChild(PNL_DESC);
+ LLLayoutPanel* locationPanel = getChild(PNL_LOC);
+ LLLayoutPanel* marketplacePanel = getChild(PNL_MRKT);
+ LLLayoutPanel* topPanel = getChild(PNL_TOP);
+
+
+ imagePanel->setVisible(FALSE);
+ descriptionPanel->setVisible(FALSE);
+ locationPanel->setVisible(FALSE);
+ marketplacePanel->setVisible(FALSE);
+ topPanel->setVisible(FALSE);
+
+
+ LLTextBox* child = getChild(TF_NAME);
+ //child->setText(experience[LLExperienceCache::NAME].asString());
+ child->setValue(experience[LLExperienceCache::EXPERIENCE_ID].asUUID());
+
+ LLLineEditor* linechild = getChild(EDIT TF_NAME);
+ linechild->setText(experience[LLExperienceCache::NAME].asString());
+
+ std::string value = experience[LLExperienceCache::DESCRIPTION].asString();
+ LLTextEditor* exchild = getChild(TF_DESC);
+ exchild->setText(value);
+ descriptionPanel->setVisible(value.length()>0);
+
+ LLTextEditor* edit_child = getChild(EDIT TF_DESC);
+ edit_child->setText(value);
+
+ mLocationSLURL = experience[LLExperienceCache::SLURL].asString();
+ edit_child = getChild(TF_SLURL);
+ bool has_slurl = !mLocationSLURL.empty() && mLocationSLURL != "last";
+ locationPanel->setVisible(has_slurl);
+ if (has_slurl) mLocationSLURL = LLSLURL(mLocationSLURL).getSLURLString();
+ edit_child->setText(mLocationSLURL);
+
+
+ edit_child = getChild(EDIT TF_SLURL);
+ if (has_slurl)
+ {
+ edit_child->setText(mLocationSLURL);
+ }
+ else
+ {
+ edit_child->setText(getString("empty_slurl"));
+ }
+
+ setMaturityString((U8)(experience[LLExperienceCache::MATURITY].asInteger()), getChild(TF_MATURITY), getChild(EDIT TF_MATURITY));
+
+ LLUUID agent_id = experience[LLExperienceCache::AGENT_ID].asUUID();
+ getChild(TF_OWNER)->setValue(agent_id);
+
+ LLUUID id = experience[LLExperienceCache::GROUP_ID].asUUID();
+ bool id_null = id.isNull();
+ if (!id_null)
+ {
+ getChild(TF_GROUP)->setValue(id);
+ }
+ getChild(PNL_GROUP)->setVisible(!id_null);
+
+ setEditGroup(id);
+
+ getChild(BTN_SET_GROUP)->setEnabled(agent_id == gAgentID);
+
+ LLCheckBoxCtrl* enable = getChild(EDIT BTN_ENABLE);
+ S32 properties = mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger();
+ enable->set(!(properties & LLExperienceCache::PROPERTY_DISABLED));
+
+ enable = getChild(EDIT BTN_PRIVATE);
+ enable->set(properties & LLExperienceCache::PROPERTY_PRIVATE);
+
+ topPanel->setVisible(TRUE);
+ child=getChild(TF_GRID_WIDE);
+ child->setVisible(TRUE);
+
+ if(properties & LLExperienceCache::PROPERTY_GRID)
+ {
+ child->setText(LLTrans::getString("Grid-Scope"));
+ }
+ else
+ {
+ child->setText(LLTrans::getString("Land-Scope"));
+ }
+
+ if (getChild(BTN_EDIT)->getVisible())
+ {
+ topPanel->setVisible(TRUE);
+ }
+
+ if (properties & LLExperienceCache::PROPERTY_PRIVILEGED)
+ {
+ child = getChild(TF_PRIVILEGED);
+ child->setVisible(TRUE);
+ }
+ else
+ {
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ LLExperienceCache::instance().getExperiencePermission(mExperienceId, boost::bind(
+ &LLFloaterExperienceProfile::experiencePermissionResults, mExperienceId, _1));
+ }
+ }
+
+ value=experience[LLExperienceCache::METADATA].asString();
+ if (value.empty())
+ return;
+
+ LLPointer parser = new LLSDXMLParser();
+
+ LLSD data;
+
+ std::istringstream is(value);
+ if (LLSDParser::PARSE_FAILURE != parser->parse(is, data, value.size()))
+ {
+ value.clear();
+ if (data.has(TF_MRKT))
+ {
+ value=data[TF_MRKT].asString();
+
+ edit_child = getChild(TF_MRKT);
+ edit_child->setText(value);
+ if(!value.empty())
+ {
+ marketplacePanel->setVisible(TRUE);
+ }
+ else
+ {
+ marketplacePanel->setVisible(FALSE);
+ }
+ }
+ else
+ {
+ marketplacePanel->setVisible(FALSE);
+ }
+
+ linechild = getChild(EDIT TF_MRKT);
+ linechild->setText(value);
+
+ if (data.has(IMG_LOGO))
+ {
+ LLTextureCtrl* logo = getChild(IMG_LOGO);
+
+ LLUUID id = data[IMG_LOGO].asUUID();
+ logo->setImageAssetID(id);
+ imagePanel->setVisible(TRUE);
+
+ logo = getChild(EDIT IMG_LOGO);
+ logo->setImageAssetID(data[IMG_LOGO].asUUID());
+
+ imagePanel->setVisible(id.notNull());
+ }
+ }
+ else
+ {
+ marketplacePanel->setVisible(FALSE);
+ imagePanel->setVisible(FALSE);
+ }
+
+ mDirty=false;
+ mForceClose = false;
+ getChild(BTN_SAVE)->setEnabled(mDirty);
+}
+
+void LLFloaterExperienceProfile::setPreferences( const LLSD& content )
+{
+ S32 properties = mExperienceDetails[LLExperienceCache::PROPERTIES].asInteger();
+ if (properties & LLExperienceCache::PROPERTY_PRIVILEGED)
+ {
+ return;
+ }
+
+ const LLSD& experiences = content["experiences"];
+ const LLSD& blocked = content["blocked"];
+
+
+ for(const auto& exp : experiences.array())
+ {
+ if (exp.asUUID()==mExperienceId)
+ {
+ experienceAllowed();
+ return;
+ }
+ }
+
+ for(const auto& exp : blocked.array())
+ {
+ if (exp.asUUID()==mExperienceId)
+ {
+ experienceBlocked();
+ return;
+ }
+ }
+
+ experienceForgotten();
+}
+
+void LLFloaterExperienceProfile::onFieldChanged()
+{
+ updatePackage();
+
+ if (!getChild(BTN_EDIT)->getVisible())
+ {
+ return;
+ }
+
+ mDirty = mPackage.size() != mExperienceDetails.size();
+ if (!mDirty)
+ {
+ LLSD::map_const_iterator st = mExperienceDetails.beginMap();
+ LLSD::map_const_iterator dt = mPackage.beginMap();
+ LLSD::map_const_iterator ste = mExperienceDetails.endMap();
+ LLSD::map_const_iterator dte = mPackage.endMap();
+ for (; st != ste && dt != dte; ++st, ++dt)
+ if (mDirty = st->first != dt->first || st->second.asString() != dt->second.asString())
+ break;
+ }
+
+ getChild(BTN_SAVE)->setEnabled(mDirty);
+}
+
+
+BOOL LLFloaterExperienceProfile::canClose()
+{
+ if (mForceClose || !mDirty)
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLFloaterExperienceProfile::handleSaveChangesDialog, this, _1, _2, CLOSE));
+ return FALSE;
+ }
+}
+
+bool LLFloaterExperienceProfile::handleSaveChangesDialog( const LLSD& notification, const LLSD& response, PostSaveAction action )
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ switch( option )
+ {
+ case 0: // "Yes"
+ // close after saving
+ doSave( action );
+ break;
+
+ case 1: // "No"
+ if (action != NOTHING)
+ {
+ mForceClose = TRUE;
+ if (action==CLOSE)
+ {
+ close();
+ }
+ else
+ {
+ changeToView();
+ }
+ }
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ LLAppViewer::instance()->abortQuit();
+ break;
+ }
+ return false;
+}
+
+void LLFloaterExperienceProfile::doSave( int success_action )
+{
+ mSaveCompleteAction=success_action;
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ return;
+
+ LLExperienceCache::instance().updateExperience(mPackage, boost::bind(
+ &LLFloaterExperienceProfile::experienceUpdateResult,
+ getDerivedHandle(), _1));
+}
+
+void LLFloaterExperienceProfile::onSaveComplete( const LLSD& content )
+{
+ LLUUID id = getExperienceId();
+
+ if (content.has("removed"))
+ {
+ const LLSD& removed = content["removed"];
+ for(const auto& it : removed.map())
+ {
+ const std::string& field = it.first;
+ if (field == LLExperienceCache::EXPERIENCE_ID)
+ {
+ //this message should be removed by the experience api
+ continue;
+ }
+ const LLSD& data = it.second;
+ std::string error_tag = data["error_tag"].asString()+ "ExperienceProfileMessage";
+ LLSD fields;
+ if (LLNotificationTemplates::instance().templateExists(error_tag))
+ {
+ fields["field"] = field;
+ fields["extra_info"] = data["extra_info"];
+ LLNotificationsUtil::add(error_tag, fields);
+ }
+ else
+ {
+ fields["MESSAGE"] = data["en"];
+ LLNotificationsUtil::add("GenericAlert", fields);
+ }
+ }
+ }
+
+ if (!content.has("experience_keys"))
+ {
+ LL_WARNS() << "LLFloaterExperienceProfile::onSaveComplete called with bad content" << LL_ENDL;
+ return;
+ }
+
+ const LLSD& experiences = content["experience_keys"];
+
+ if (experiences.size() == 0)
+ {
+ LL_WARNS() << "LLFloaterExperienceProfile::onSaveComplete called with empty content" << LL_ENDL;
+ return;
+ }
+
+ const auto& exp = experiences[0];
+ if (!exp.has(LLExperienceCache::EXPERIENCE_ID) || (exp[LLExperienceCache::EXPERIENCE_ID].asUUID() != id))
+ {
+ LL_WARNS() << "LLFloaterExperienceProfile::onSaveComplete called with unexpected experience id" << LL_ENDL;
+ return;
+ }
+
+ refreshExperience(exp);
+ LLExperienceCache::instance().insert(exp);
+ LLExperienceCache::instance().fetch(id, true);
+
+ if (mSaveCompleteAction == VIEW)
+ {
+ LLTabContainer* tabs = getChild("tab_container");
+ tabs->selectTabByName("panel_experience_info");
+ }
+ else if (mSaveCompleteAction == CLOSE)
+ {
+ close();
+ }
+}
+
+void LLFloaterExperienceProfile::changeToView()
+{
+ if (mForceClose || !mDirty)
+ {
+ refreshExperience(mExperienceDetails);
+ LLTabContainer* tabs = getChild("tab_container");
+
+ tabs->selectTabByName("panel_experience_info");
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ LLNotificationsUtil::add("SaveChanges", LLSD(), LLSD(), boost::bind(&LLFloaterExperienceProfile::handleSaveChangesDialog, this, _1, _2, VIEW));
+ }
+}
+
+void LLFloaterExperienceProfile::changeToEdit()
+{
+ LLTabContainer* tabs = getChild("tab_container");
+
+ tabs->selectTabByName("edit_panel_experience_info");
+}
+
+void LLFloaterExperienceProfile::onClickLocation()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ auto child = getChild(EDIT TF_SLURL);
+ mLocationSLURL = LLSLURL(region->getName(), gAgent.getPositionGlobal()).getSLURLString();
+ child->setText(mLocationSLURL);
+ onFieldChanged();
+ }
+}
+
+void LLFloaterExperienceProfile::onClickClear()
+{
+ auto child = getChild(EDIT TF_SLURL);
+ mLocationSLURL.clear();
+ child->setText(getString("empty_slurl"));
+ onFieldChanged();
+}
+
+void LLFloaterExperienceProfile::updatePermission(const LLSD& permission)
+{
+ if (permission.has("experience"))
+ {
+ if (permission["experience"].asUUID() != mExperienceId)
+ {
+ return;
+ }
+
+ std::string str = permission[mExperienceId.asString()]["permission"].asString();
+ if (str == "Allow")
+ {
+ experienceAllowed();
+ }
+ else if (str == "Block")
+ {
+ experienceBlocked();
+ }
+ else if (str == "Forget")
+ {
+ experienceForgotten();
+ }
+ }
+ else
+ {
+ setPreferences(permission);
+ }
+}
+
+void LLFloaterExperienceProfile::experienceAllowed()
+{
+ LLButton* button=getChild(BTN_ALLOW);
+ button->setEnabled(FALSE);
+
+ button=getChild(BTN_FORGET);
+ button->setEnabled(TRUE);
+
+ button=getChild(BTN_BLOCK);
+ button->setEnabled(TRUE);
+}
+
+void LLFloaterExperienceProfile::experienceForgotten()
+{
+ LLButton* button=getChild(BTN_ALLOW);
+ button->setEnabled(TRUE);
+
+ button=getChild(BTN_FORGET);
+ button->setEnabled(FALSE);
+
+ button=getChild(BTN_BLOCK);
+ button->setEnabled(TRUE);
+}
+
+void LLFloaterExperienceProfile::experienceBlocked()
+{
+ LLButton* button=getChild(BTN_ALLOW);
+ button->setEnabled(TRUE);
+
+ button=getChild(BTN_FORGET);
+ button->setEnabled(TRUE);
+
+ button=getChild(BTN_BLOCK);
+ button->setEnabled(FALSE);
+}
+
+void LLFloaterExperienceProfile::onClose( bool app_quitting )
+{
+ LLEventPumps::instance().obtain("experience_permission").stopListening(mExperienceId.asString()+"-profile");
+ LLFloater::onClose(app_quitting);
+}
+
+void LLFloaterExperienceProfile::updatePackage()
+{
+ mPackage[LLExperienceCache::NAME] = getChild(EDIT TF_NAME)->getText();
+ mPackage[LLExperienceCache::DESCRIPTION] = getChild(EDIT TF_DESC)->getText();
+ if (mLocationSLURL.empty())
+ {
+ mPackage[LLExperienceCache::SLURL] = LLStringUtil::null;
+ }
+ else
+ {
+ mPackage[LLExperienceCache::SLURL] = mLocationSLURL;
+ }
+
+ mPackage[LLExperienceCache::MATURITY] = getChild(EDIT TF_MATURITY)->getSelectedValue().asInteger();
+
+ LLSD metadata;
+
+ metadata[TF_MRKT] = getChild(EDIT TF_MRKT)->getText();
+ metadata[IMG_LOGO] = getChild(EDIT IMG_LOGO)->getImageAssetID();
+
+ LLPointer formatter = new LLSDXMLFormatter();
+
+ std::ostringstream os;
+ if (formatter->format(metadata, os))
+ {
+ mPackage[LLExperienceCache::METADATA] = os.str();
+ }
+
+ int properties = mPackage[LLExperienceCache::PROPERTIES].asInteger();
+ LLCheckBoxCtrl* enable = getChild(EDIT BTN_ENABLE);
+ if (enable->get())
+ {
+ properties &= ~LLExperienceCache::PROPERTY_DISABLED;
+ }
+ else
+ {
+ properties |= LLExperienceCache::PROPERTY_DISABLED;
+ }
+
+ enable = getChild(EDIT BTN_PRIVATE);
+ if (enable->get())
+ {
+ properties |= LLExperienceCache::PROPERTY_PRIVATE;
+ }
+ else
+ {
+ properties &= ~LLExperienceCache::PROPERTY_PRIVATE;
+ }
+
+ mPackage[LLExperienceCache::PROPERTIES] = properties;
+}
+
+void LLFloaterExperienceProfile::onPickGroup()
+{
+ LLFloater* parent_floater = gFloaterView->getParentFloater(this);
+
+ LLFloaterGroupPicker* widget = LLFloaterGroupPicker::showInstance(gAgent.getID());
+ if (widget)
+ {
+ widget->setSelectGroupCallback(boost::bind(&LLFloaterExperienceProfile::setEditGroup, this, _1));
+ if (parent_floater)
+ {
+ LLRect new_rect = gFloaterView->findNeighboringPosition(parent_floater, widget);
+ widget->setOrigin(new_rect.mLeft, new_rect.mBottom);
+ parent_floater->addDependentFloater(widget);
+ }
+ }
+}
+
+void LLFloaterExperienceProfile::setEditGroup( LLUUID group_id )
+{
+ LLTextBox* child = getChild(EDIT TF_GROUP);
+ child->setValue(group_id);
+ mPackage[LLExperienceCache::GROUP_ID] = group_id;
+ onFieldChanged();
+}
+
+void LLFloaterExperienceProfile::onReportExperience()
+{
+ LLFloaterReporter::showFromExperience(mExperienceId);
+}
+
+/*static*/
+bool LLFloaterExperienceProfile::hasPermission(const LLSD& content, const std::string &name, const LLUUID &test)
+{
+ if (!content.has(name))
+ return false;
+
+ const LLSD& list = content[name];
+ for (const auto& it : list.array())
+ {
+ if (it.asUUID() == test)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*static*/
+void LLFloaterExperienceProfile::experiencePermissionResults(const LLUUID& exprienceId, const LLSD& result)
+{
+ std::string permission("Forget");
+ if (hasPermission(result, "experiences", exprienceId))
+ permission = "Allow";
+ else if (hasPermission(result, "blocked", exprienceId))
+ permission = "Block";
+
+ LLSD experience;
+ LLSD message;
+ experience["permission"] = permission;
+ message["experience"] = exprienceId;
+ message[exprienceId.asString()] = experience;
+
+ LLEventPumps::instance().obtain("experience_permission").post(message);
+}
+
+/*static*/
+void LLFloaterExperienceProfile::experienceIsAdmin(LLHandle handle, const LLSD &result)
+{
+ LLFloaterExperienceProfile* parent = handle.get();
+ if (!parent)
+ return;
+
+ bool enabled = true;
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ {
+ enabled = false;
+ }
+ else
+ {
+ std::string url = region->getCapability("UpdateExperience");
+ if (url.empty())
+ enabled = false;
+ }
+ if (enabled && result["status"].asBoolean())
+ {
+ parent->getChild(PNL_TOP)->setVisible(TRUE);
+ parent->getChild(BTN_EDIT)->setVisible(TRUE);
+ }
+}
+
+/*static*/
+void LLFloaterExperienceProfile::experienceUpdateResult(LLHandle handle, const LLSD &result)
+{
+ LLFloaterExperienceProfile* parent = handle.get();
+ if (parent)
+ {
+ parent->onSaveComplete(result);
+ }
+}
diff --git a/indra/newview/llfloaterexperienceprofile.h b/indra/newview/llfloaterexperienceprofile.h
new file mode 100644
index 000000000..d9dad7784
--- /dev/null
+++ b/indra/newview/llfloaterexperienceprofile.h
@@ -0,0 +1,113 @@
+/**
+ * @file llfloaterexperienceprofile.h
+ * @brief llfloaterexperienceprofile and related class definitions
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, 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_LLFLOATEREXPERIENCEPROFILE_H
+#define LL_LLFLOATEREXPERIENCEPROFILE_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "llsd.h"
+
+class LLLayoutPanel;
+class LLTextBox;
+class LLComboBox;
+
+class LLFloaterExperienceProfile final : public LLFloater
+, public LLInstanceTracker
+{
+ LOG_CLASS(LLFloaterExperienceProfile);
+public:
+ enum PostSaveAction
+ {
+ NOTHING,
+ CLOSE,
+ VIEW,
+ };
+
+ static void experiencePermissionResults(const LLUUID& exprienceId, const LLSD& result);
+
+ static void showInstance(const LLSD& data);
+ LLFloaterExperienceProfile(const LLSD& data);
+ virtual ~LLFloaterExperienceProfile();
+
+ LLUUID getExperienceId() const { return mExperienceId; }
+ void setPreferences( const LLSD& content );
+
+
+ void refreshExperience(const LLSD& experience);
+ void onSaveComplete( const LLSD& content );
+ BOOL canClose() override;
+
+ void onClose(bool app_quitting) override;
+protected:
+ void onClickEdit();
+ void onClickPermission(const char* permission);
+ void onClickForget();
+ void onClickCancel();
+ void onClickSave();
+ void onClickLocation();
+ void onClickClear();
+ void onPickGroup();
+ void onFieldChanged();
+ void onReportExperience();
+
+ void setEditGroup(LLUUID group_id);
+
+ void changeToView();
+ void changeToEdit();
+
+ void experienceForgotten();
+ void experienceBlocked();
+ void experienceAllowed();
+
+ static void experienceCallback(LLHandle handle, const LLSD& experience);
+ static bool experiencePermission(LLHandle handle, const LLSD& permission);
+
+ BOOL postBuild() override;
+ bool setMaturityString(U8 maturity, LLTextBox* child, LLComboBox* combo);
+ bool handleSaveChangesDialog(const LLSD& notification, const LLSD& response, PostSaveAction action);
+ void doSave( int success_action );
+
+ void updatePackage();
+
+ void updatePermission( const LLSD& permission );
+ LLUUID mExperienceId;
+ LLSD mExperienceDetails;
+ LLSD mPackage;
+ std::string mLocationSLURL;
+ int mSaveCompleteAction;
+ bool mDirty;
+ bool mForceClose;
+ bool mPostEdit; // edit experience after opening and updating it
+private:
+ static bool hasPermission(const LLSD& content, const std::string &name, const LLUUID &test);
+ static void experienceIsAdmin(LLHandle handle, const LLSD &result);
+ static void experienceUpdateResult(LLHandle handle, const LLSD &result);
+};
+
+#endif // LL_LLFLOATEREXPERIENCEPROFILE_H
diff --git a/indra/newview/llfloaterexperiences.cpp b/indra/newview/llfloaterexperiences.cpp
new file mode 100644
index 000000000..395b09470
--- /dev/null
+++ b/indra/newview/llfloaterexperiences.cpp
@@ -0,0 +1,377 @@
+/**
+ * @file llfloaterexperiences.cpp
+ * @brief LLFloaterExperiences class implementation
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, 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 "llfloaterexperiences.h"
+
+#include "llagent.h"
+#include "llcororesponder.h"
+#include "llevents.h"
+#include "llexperiencecache.h"
+#include "llfloaterexperienceprofile.h"
+#include "llfloaterregioninfo.h"
+#include "llnotificationsutil.h"
+#include "llpanelexperiencelog.h"
+#include "llpanelexperiencepicker.h"
+#include "llpanelexperiences.h"
+#include "lltabcontainer.h"
+#include "lltrans.h"
+#include "lluictrlfactory.h"
+#include "llviewerregion.h"
+
+
+#define SHOW_RECENT_TAB (0)
+LLFloaterExperiences::LLFloaterExperiences(const LLSD& data)
+ :LLFloater(data)
+{
+ LLUICtrlFactory::getInstance()->buildFloater(this, "floater_experiences.xml", NULL, false);
+ //buildFromFile("floater_experiences.xml");
+}
+
+LLPanelExperiences* LLFloaterExperiences::addTab(const std::string& name, bool select)
+{
+ LLPanelExperiences* newPanel = LLPanelExperiences::create(name);
+ getChild("xp_tabs")->addTabPanel(
+ newPanel,
+ LLTrans::getString(name),
+ select);
+
+ return newPanel;
+}
+
+BOOL LLFloaterExperiences::postBuild()
+{
+ LLPanel* panel = new LLPanelExperiencePicker();
+ getChild("xp_tabs")->addTabPanel(panel, panel->getLabel());
+ addTab("Allowed_Experiences_Tab", true);
+ addTab("Blocked_Experiences_Tab", false);
+ addTab("Admin_Experiences_Tab", false);
+ addTab("Contrib_Experiences_Tab", false);
+ LLPanelExperiences* owned = addTab("Owned_Experiences_Tab", false);
+ owned->setButtonAction("acquire", boost::bind(&LLFloaterExperiences::sendPurchaseRequest, this));
+ owned->enableButton(false);
+#if SHOW_RECENT_TAB
+ addTab("Recent_Experiences_Tab", false);
+#endif //SHOW_RECENT_TAB
+ panel = new LLPanelExperienceLog();
+ getChild("xp_tabs")->addTabPanel(panel, panel->getLabel());
+ resizeToTabs();
+
+ return TRUE;
+}
+
+
+void LLFloaterExperiences::clearFromRecent(const LLSD& ids)
+{
+#if SHOW_RECENT_TAB
+ LLTabContainer* tabs = getChild("xp_tabs");
+
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Recent_Experiences_Tab");
+ if(!tab)
+ return;
+
+ tab->removeExperiences(ids);
+#endif // SHOW_RECENT_TAB
+}
+
+void LLFloaterExperiences::setupRecentTabs()
+{
+#if SHOW_RECENT_TAB
+ LLTabContainer* tabs = getChild("xp_tabs");
+
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Recent_Experiences_Tab");
+ if(!tab)
+ return;
+
+ LLSD recent;
+
+ const LLExperienceCache::cache_t& experiences = LLExperienceCache::getCached();
+
+ LLExperienceCache::cache_t::const_iterator it = experiences.begin();
+ while( it != experiences.end() )
+ {
+ if(!it->second.has(LLExperienceCache::MISSING))
+ {
+ recent.append(it->first);
+ }
+ ++it;
+ }
+
+ tab->setExperienceList(recent);
+#endif // SHOW_RECENT_TAB
+}
+
+
+void LLFloaterExperiences::resizeToTabs()
+{
+ const S32 TAB_WIDTH_PADDING = 16;
+
+ LLTabContainer* tabs = getChild("xp_tabs");
+ LLRect rect = getRect();
+ if(rect.getWidth() < tabs->getTotalTabWidth() + TAB_WIDTH_PADDING)
+ {
+ rect.mRight = rect.mLeft + tabs->getTotalTabWidth() + TAB_WIDTH_PADDING;
+ }
+ reshape(rect.getWidth(), rect.getHeight(), FALSE);
+}
+
+void LLFloaterExperiences::refreshContents()
+{
+ setupRecentTabs();
+
+ LLViewerRegion* region = gAgent.getRegion();
+
+ if (region)
+ {
+ NameMap_t tabMap;
+ LLHandle handle = getDerivedHandle();
+
+ tabMap["experiences"]="Allowed_Experiences_Tab";
+ tabMap["blocked"]="Blocked_Experiences_Tab";
+ tabMap["experience_ids"]="Owned_Experiences_Tab";
+
+ retrieveExperienceList(region->getCapability("GetExperiences"), handle, tabMap);
+
+ updateInfo("GetAdminExperiences","Admin_Experiences_Tab");
+ updateInfo("GetCreatorExperiences","Contrib_Experiences_Tab");
+
+ retrieveExperienceList(region->getCapability("AgentExperiences"), handle, tabMap,
+ "ExperienceAcquireFailed", boost::bind(&LLFloaterExperiences::checkPurchaseInfo, this, _1, _2));
+ }
+}
+
+void LLFloaterExperiences::onOpen()
+{
+ LLEventPumps::instance().obtain("experience_permission").stopListening("LLFloaterExperiences");
+ LLEventPumps::instance().obtain("experience_permission").listen("LLFloaterExperiences",
+ boost::bind(&LLFloaterExperiences::updatePermissions, this, _1));
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if(region)
+ {
+ if(region->capabilitiesReceived())
+ {
+ refreshContents();
+ return;
+ }
+ region->setCapabilitiesReceivedCallback(boost::bind(&LLFloaterExperiences::refreshContents, this));
+ return;
+ }
+}
+
+bool LLFloaterExperiences::updatePermissions( const LLSD& permission )
+{
+ LLTabContainer* tabs = getChild("xp_tabs");
+ LLUUID experience;
+ std::string permission_string;
+ if(permission.has("experience"))
+ {
+ experience = permission["experience"].asUUID();
+ permission_string = permission[experience.asString()]["permission"].asString();
+
+ }
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName("Allowed_Experiences_Tab");
+ if(tab)
+ {
+ if(permission.has("experiences"))
+ {
+ tab->setExperienceList(permission["experiences"]);
+ }
+ else if(experience.notNull())
+ {
+ if(permission_string != "Allow")
+ {
+ tab->removeExperience(experience);
+ }
+ else
+ {
+ tab->addExperience(experience);
+ }
+ }
+ }
+
+ tab = (LLPanelExperiences*)tabs->getPanelByName("Blocked_Experiences_Tab");
+ if(tab)
+ {
+ if(permission.has("blocked"))
+ {
+ tab->setExperienceList(permission["blocked"]);
+ }
+ else if(experience.notNull())
+ {
+ if(permission_string != "Block")
+ {
+ tab->removeExperience(experience);
+ }
+ else
+ {
+ tab->addExperience(experience);
+ }
+ }
+ }
+ return false;
+}
+
+void LLFloaterExperiences::onClose( bool app_quitting )
+{
+ LLEventPumps::instance().obtain("experience_permission").stopListening("LLFloaterExperiences");
+ LLFloater::onClose(app_quitting);
+}
+
+void LLFloaterExperiences::checkPurchaseInfo(LLPanelExperiences* panel, const LLSD& content) const
+{
+ panel->enableButton(content.has("purchase"));
+
+ LLFloaterExperiences::findInstance()->updateInfo("GetAdminExperiences","Admin_Experiences_Tab");
+ LLFloaterExperiences::findInstance()->updateInfo("GetCreatorExperiences","Contrib_Experiences_Tab");
+}
+
+void LLFloaterExperiences::checkAndOpen(LLPanelExperiences* panel, const LLSD& content) const
+{
+ checkPurchaseInfo(panel, content);
+
+ // determine new item
+ const LLSD& response_ids = content["experience_ids"];
+
+ if (mPrepurchaseIds.size() + 1 == response_ids.size())
+ {
+ // we have a new element
+ for (const auto& it : response_ids.array())
+ {
+ LLUUID experience_id = it.asUUID();
+ if (std::find(mPrepurchaseIds.begin(), mPrepurchaseIds.end(), experience_id) == mPrepurchaseIds.end())
+ {
+ // new element found, open it
+ LLSD args;
+ args["experience_id"] = experience_id;
+ args["edit_experience"] = true;
+ LLFloaterExperienceProfile::showInstance(args);
+ break;
+ }
+ }
+ }
+}
+
+void LLFloaterExperiences::updateInfo(std::string experienceCap, std::string tab)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ NameMap_t tabMap;
+ LLHandle handle = getDerivedHandle();
+
+ tabMap["experience_ids"] = tab;
+
+ retrieveExperienceList(region->getCapability(experienceCap), handle, tabMap);
+ }
+}
+
+void LLFloaterExperiences::sendPurchaseRequest()
+{
+ LLViewerRegion* region = gAgent.getRegion();
+
+ if (region)
+ {
+ NameMap_t tabMap;
+ const std::string tab_owned_name = "Owned_Experiences_Tab";
+ LLHandle handle = getDerivedHandle();
+
+ tabMap["experience_ids"] = tab_owned_name;
+
+ // extract ids for experiences that we already have
+ LLTabContainer* tabs = getChild("xp_tabs");
+ LLPanelExperiences* tab_owned = (LLPanelExperiences*)tabs->getPanelByName(tab_owned_name);
+ mPrepurchaseIds.clear();
+ if (tab_owned)
+ {
+ tab_owned->getExperienceIdsList(mPrepurchaseIds);
+ }
+
+ requestNewExperience(region->getCapability("AgentExperiences"), handle, tabMap, "ExperienceAcquireFailed",
+ boost::bind(&LLFloaterExperiences::checkAndOpen, this, _1, _2));
+ }
+}
+
+
+void LLFloaterExperiences::retrieveExperienceList(const std::string &url,
+ const LLHandle &hparent, const NameMap_t &tabMapping,
+ const std::string &errorNotify, Callback_t cback, bool post)
+{
+ if (url.empty())
+ {
+ LL_WARNS() << "retrieveExperienceListCoro called with empty capability!" << LL_ENDL;
+ return;
+ }
+
+ auto responder = new LLCoroResponder(
+ boost::bind(LLFloaterExperiences::retrieveExperienceListCoro, _1,
+ hparent, tabMapping, errorNotify, cback));
+
+ if (post)
+ LLHTTPClient::post(url, LLSD(), responder);
+ else
+ LLHTTPClient::get(url, responder);
+}
+
+void LLFloaterExperiences::retrieveExperienceListCoro(const LLCoroResponder& responder,
+ LLHandle hparent, NameMap_t tabMapping,
+ std::string errorNotify, Callback_t cback)
+{
+ LLSD result = responder.getContent();
+
+ if (!responder.isGoodStatus(responder.getStatus()))
+ {
+ LLSD subs;
+ subs["ERROR_MESSAGE"] = responder.getReason();
+ LLNotificationsUtil::add(errorNotify, subs);
+
+ return;
+ }
+
+ if (hparent.isDead())
+ return;
+
+ LLFloaterExperiences* parent = hparent.get();
+ LLTabContainer* tabs = parent->getChild("xp_tabs");
+
+ for (auto& it : tabMapping)
+ {
+ if (result.has(it.first))
+ {
+ LLPanelExperiences* tab = (LLPanelExperiences*)tabs->getPanelByName(it.second);
+ if (tab)
+ {
+ const LLSD& ids = result[it.first];
+ tab->setExperienceList(ids);
+ if (cback != nullptr)
+ {
+ cback(tab, result);
+ }
+ }
+ }
+ }
+
+}
diff --git a/indra/newview/llfloaterexperiences.h b/indra/newview/llfloaterexperiences.h
new file mode 100644
index 000000000..ebe1a00fe
--- /dev/null
+++ b/indra/newview/llfloaterexperiences.h
@@ -0,0 +1,78 @@
+/**
+ * @file llfloaterexperiences.h
+ * @brief LLFloaterExperiences class definition
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, 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_LLFLOATEREXPERIENCES_H
+#define LL_LLFLOATEREXPERIENCES_H
+
+#include "llfloater.h"
+
+class LLPanelExperiences;
+
+class LLFloaterExperiences final :
+ public LLFloater
+, public LLFloaterSingleton
+{
+ friend class LLUISingleton >;
+public:
+ LLFloaterExperiences(const LLSD& data);
+ void onClose(bool app_quitting) override;
+
+ void onOpen(/*const LLSD& key*/) override;
+
+protected:
+ typedef std::map NameMap_t;
+ typedef std::function Callback_t;
+
+ void clearFromRecent(const LLSD& ids);
+ void resizeToTabs();
+ /*virtual*/ BOOL postBuild() override;
+ void refreshContents();
+ void setupRecentTabs();
+ LLPanelExperiences* addTab(const std::string& name, bool select);
+
+ bool updatePermissions(const LLSD& permission);
+ void sendPurchaseRequest();
+ void checkPurchaseInfo(LLPanelExperiences* panel, const LLSD& content)const;
+ void checkAndOpen(LLPanelExperiences* panel, const LLSD& content) const;
+ void updateInfo(std::string experiences, std::string tab);
+
+ void retrieveExperienceList(const std::string &url, const LLHandle &hparent, const NameMap_t &tabMapping,
+ const std::string &errorNotify = std::string("ErrorMessage"), Callback_t cback = Callback_t(), bool post = false);
+
+ void requestNewExperience(const std::string &url, const LLHandle &hparent, const NameMap_t &tabMapping,
+ const std::string &errorNotify, Callback_t cback)
+ {
+ retrieveExperienceList(url, hparent, tabMapping, errorNotify, cback, true);
+ }
+
+private:
+ static void retrieveExperienceListCoro(const struct LLCoroResponder& responder, LLHandle hparent,
+ NameMap_t tabMapping, std::string errorNotify, Callback_t cback);
+ uuid_vec_t mPrepurchaseIds;
+};
+
+#endif //LL_LLFLOATEREXPERIENCES_H
+
diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp
index 5367bcc6d..0e3374ecf 100644
--- a/indra/newview/llfloatergroups.cpp
+++ b/indra/newview/llfloatergroups.cpp
@@ -375,7 +375,6 @@ LLSD create_group_element(const LLGroupData *group_datap, const LLUUID &active_g
void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id, U64 powers_mask)
{
- S32 count = gAgent.mGroups.size();
LLUUID id;
LLCtrlListInterface *group_list = ctrl->getListInterface();
if (!group_list) return;
@@ -386,11 +385,16 @@ void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id, U64 pow
group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
- for(S32 i = 0; i < count; ++i)
+ for(const auto& group : gAgent.mGroups)
{
- LLSD element = create_group_element(&gAgent.mGroups[i], highlight_id, powers_mask);
- if(element.size())
+ LLSD element = create_group_element(&group, highlight_id, powers_mask);
+ if (element.size())
+ {
group_list->addElement(element, ADD_SORTED);
+ // Force a name lookup here to get it added to the cache so other UI is prepared
+ std::string dummy;
+ gCacheName->getGroupName(group.mID, dummy);
+ }
}
// add "none" to list at top
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index df031e4d8..0ad893756 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -46,16 +46,16 @@
#include "llagent.h"
#include "llagentaccess.h"
-#include "llavataractions.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
-#include "llfloaterauction.h"
#include "llfloateravatarpicker.h"
+#include "llfloaterauction.h"
#include "llfloaterbanduration.h"
#include "llfloatergroups.h"
#include "llfloaterscriptlimits.h"
-#include "llgroupactions.h"
+#include "llavataractions.h"
+#include "lllayoutstack.h"
#include "lllineeditor.h"
#include "llnamelistctrl.h"
#include "llpanellandaudio.h"
@@ -64,6 +64,7 @@
#include "llscrolllistctrl.h"
#include "llscrolllistitem.h"
#include "llselectmgr.h"
+#include "llslurl.h"
#include "llspinctrl.h"
#include "lltabcontainer.h"
#include "lltextbox.h"
@@ -81,15 +82,20 @@
#include "roles_constants.h"
#include "llworld.h"
#include "lltrans.h"
+#include "llpanelexperiencelisteditor.h"
+#include "llpanelexperiencepicker.h"
+#include "llexperiencecache.h"
+#include "llgroupactions.h"
#include "hippogridmanager.h"
-
// [RLVa:KB]
#include "rlvhandler.h"
// [/RLVa:KB]
+const F64 COVENANT_REFRESH_TIME_SEC = 60.0f;
+
static std::string OWNER_ONLINE = "0";
static std::string OWNER_OFFLINE = "1";
static std::string OWNER_GROUP = "2";
@@ -98,17 +104,37 @@ static std::string OWNER_INSIM = "4";
// constants used in callbacks below - syntactic sugar.
static const BOOL BUY_GROUP_LAND = TRUE;
static const BOOL BUY_PERSONAL_LAND = FALSE;
-LLPointer LLPanelLandGeneral::sSelectionForBuyPass = NULL;
// Statics
LLParcelSelectionObserver* LLFloaterLand::sObserver = NULL;
S32 LLFloaterLand::sLastTab = 0;
// Local classes
-class LLParcelSelectionObserver : public LLParcelObserver
+class LLParcelSelectionObserver final : public LLParcelObserver
{
public:
- virtual void changed() { LLFloaterLand::refreshAll(); }
+ void changed() override { LLFloaterLand::refreshAll(); }
+};
+
+class LLPanelLandExperiences final
+ : public LLPanel
+{
+public:
+ LLPanelLandExperiences(LLSafeHandle& parcelp);
+ BOOL postBuild() override;
+ void refresh() override;
+
+ void experienceAdded(const LLUUID& id, U32 xp_type, U32 access_type);
+ void experienceRemoved(const LLUUID& id, U32 access_type);
+protected:
+ void setupList(LLPanelExperienceListEditor* child, const char* control_name, U32 xp_type, U32 access_type );
+ void refreshPanel(LLPanelExperienceListEditor* panel, U32 xp_type);
+
+ LLSafeHandle& mParcel;
+
+
+ LLPanelExperienceListEditor* mAllowed;
+ LLPanelExperienceListEditor* mBlocked;
};
//---------------------------------------------------------------------------
@@ -203,7 +229,7 @@ void LLFloaterLand::refreshAll()
void LLFloaterLand::onOpen()
{
- // moved from triggering show instance in llviwermenu.cpp
+ // moved from triggering show instance in llviewermenu.cpp
if (LLViewerParcelMgr::getInstance()->selectionEmpty())
{
@@ -221,21 +247,9 @@ void LLFloaterLand::onOpen()
refresh();
}
-// virtual
-void LLFloaterLand::onClose(bool app_quitting)
-{
- // Might have been showing owned objects
- LLSelectMgr::getInstance()->unhighlightAll();
-
- // Save which panel we had open
- sLastTab = mTabLand->getCurrentPanelIndex();
-
- destroy();
-}
-
LLFloaterLand::LLFloaterLand(const LLSD& seed)
- : LLFloater(std::string("floaterland"), std::string("FloaterLandRect5"), std::string("About Land"))
+: LLFloater(std::string("floaterland"), std::string("FloaterLandRect5"), std::string("About Land"))
, mTabLand(nullptr)
, mPanelGeneral(nullptr)
, mPanelObjects(nullptr)
@@ -243,6 +257,7 @@ LLFloaterLand::LLFloaterLand(const LLSD& seed)
, mPanelMedia(nullptr)
, mPanelAccess(nullptr)
, mPanelCovenant(nullptr)
+ , mPanelExperiences(nullptr)
{
mFactoryMap["land_general_panel"] = LLCallbackMap(createPanelLandGeneral, this);
mFactoryMap["land_covenant_panel"] = LLCallbackMap(createPanelLandCovenant, this);
@@ -251,6 +266,7 @@ LLFloaterLand::LLFloaterLand(const LLSD& seed)
mFactoryMap["land_audio_panel"] = LLCallbackMap(createPanelLandAudio, this);
mFactoryMap["land_media_panel"] = LLCallbackMap(createPanelLandMedia, this);
mFactoryMap["land_access_panel"] = LLCallbackMap(createPanelLandAccess, this);
+ //mFactoryMap["land_experiences_panel"] = LLCallbackMap(createPanelLandExperiences, this); // We need to buildPanel this, so we call this in postBuild
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_about_land.xml", &getFactoryMap(), false);
@@ -266,6 +282,8 @@ BOOL LLFloaterLand::postBuild()
if (tab)
{
+ if (!gAgent.getRegion()->getCapability("RegionExperiences").empty())
+ createPanelLandExperiences(this); // We need to buildPanel this, so call it here instead of during floater building
tab->selectTab(sLastTab);
}
@@ -295,10 +313,9 @@ void LLFloaterLand::refresh()
mPanelMedia->refresh();
mPanelAccess->refresh();
mPanelCovenant->refresh();
+ if (mPanelExperiences) mPanelExperiences->refresh();
}
-
-
void* LLFloaterLand::createPanelLandGeneral(void* data)
{
LLFloaterLand* self = (LLFloaterLand*)data;
@@ -331,14 +348,6 @@ void* LLFloaterLand::createPanelLandOptions(void* data)
return self->mPanelOptions;
}
-// static
-void* LLFloaterLand::createPanelLandAudio(void* data)
-{
- LLFloaterLand* self = (LLFloaterLand*)data;
- self->mPanelAudio = new LLPanelLandAudio(self->mParcel);
- return self->mPanelAudio;
-}
-
// static
void* LLFloaterLand::createPanelLandMedia(void* data)
{
@@ -355,6 +364,26 @@ void* LLFloaterLand::createPanelLandAccess(void* data)
return self->mPanelAccess;
}
+// static
+void* LLFloaterLand::createPanelLandAudio(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelAudio = new LLPanelLandAudio(self->mParcel);
+ return self->mPanelAudio;
+}
+
+// static
+void* LLFloaterLand::createPanelLandExperiences(void* data)
+{
+ LLFloaterLand* self = static_cast(data);
+ self->mPanelExperiences = new LLPanelLandExperiences(self->mParcel);
+ self->mTabLand->addTabPanel(self->mPanelExperiences, self->mPanelExperiences->getLabel()); // We must build this panel separately from the floater xml, so add it to the tab container manually.
+ return self->mPanelExperiences;
+}
+
+
+LLPointer LLPanelLandGeneral::sSelectionForBuyPass = NULL;
+
//---------------------------------------------------------------------------
// LLPanelLandGeneral
//---------------------------------------------------------------------------
@@ -398,12 +427,12 @@ LLPanelLandGeneral::LLPanelLandGeneral(LLParcelSelectionHandle& parcel)
BOOL LLPanelLandGeneral::postBuild()
{
mEditName = getChild("Name");
- mEditName->setCommitCallback(onCommitAny, this);
+ mEditName->setCommitCallback(boost::bind(&LLPanelLandGeneral::onCommitAny, this));
getChild("Name")->setPrevalidate(LLLineEditor::prevalidatePrintableNotPipe);
mEditDesc = getChild("Description");
mEditDesc->setCommitOnFocusLost(TRUE);
- mEditDesc->setCommitCallback(onCommitAny, this);
+ mEditDesc->setCommitCallback(boost::bind(&LLPanelLandGeneral::onCommitAny, this));
// No prevalidate function - historically the prevalidate function was broken,
// allowing residents to put in characters like U+2661 WHITE HEART SUIT, so
// preserve that ability.
@@ -421,25 +450,24 @@ BOOL LLPanelLandGeneral::postBuild()
mBtnSetGroup->setCommitCallback(boost::bind(&LLPanelLandGeneral::onClickSetGroup, this));
mCheckDeedToGroup = getChild( "check deed");
- childSetCommitCallback("check deed", onCommitAny, this);
+ mCheckDeedToGroup->setCommitCallback(boost::bind(&LLPanelLandGeneral::onCommitAny, this));
mBtnDeedToGroup = getChild("Deed...");
- mBtnDeedToGroup->setClickedCallback(onClickDeed, this);
+ mBtnDeedToGroup->setCommitCallback(boost::bind(&LLPanelLandGeneral::onClickDeed, this));
mCheckContributeWithDeed = getChild( "check contrib");
- childSetCommitCallback("check contrib", onCommitAny, this);
+ mCheckContributeWithDeed->setCommitCallback(boost::bind(&LLPanelLandGeneral::onCommitAny, this));
-
mSaleInfoNotForSale = getChild("Not for sale.");
mSaleInfoForSale1 = getChild